Search code examples
c#arduinoserial-porttimeoutsemaphore

C# to and from Arduino DUE Serial Semaphore Time out


it's my first question here in Stackoverflow so excuse me if I forget to mention something!

I am creating an interface for an industrial washing machine using a WPF C# app and an Arduino DUE. They comunicate through a Serial Port.

Serial Port current settings:

  • BaudRate = 115200
  • Over USB cable (arduino programming port)
  • DTR and Rts enabled
  • WriteTimeout = 1000;

The Arduino is printing to the Serial Port without any delays, the states of all Analog input pins and a couple Digital input pins.

In the current state of the interface I try to simply turn on and off certain pins, however the c# app freezes and then crashes with the following error message = "System.IO.IOException: 'The semaphore timeout period has expired.".

Heres some experiences I've made and the following results:

  • Reducing the BaudRate to 9600 managed to make it so some messages get through and the pins turn off and on, if I change them too fast the same error message occurs.

  • With the baud rate back to 115200 and on the Arduino code i stop the sending of input pin states to the serial port, the error is gone and i can change pin states fast without any problems, but I won't be able to read anything from the arduino.

The Following code represents the arduino code with the sending of analog and digital input states enable. To disable I comment the 2 last lines

void read_input_analog()
{
  for(int i = 54; i<= NUMBER_ANALOG_INPUT_PORTS; i++)
  {
    String pinNumber = "A";
    pinNumber += i;
    String message = "#A";
    message += i-54;
    message +=":";
    // IMPORTANT NOTE: 54 represents analog pin A0 and 64 Analog pin A11
    message += analogRead(i);
    message +='!';
    Serial.println(message);
  }
}

void read_input_digital()
{
  for(int i = 22; i<= NUMBER_DIGITAL_INPUTS; i++)
  {
    String pinNumber = "D";
    pinNumber += i;
    String message = "#D";
    message += i;
    message +=":";
    // IMPORTANT NOTE: 54 represents analog pin A0 and 64 Analog pin A11
    message += digitalRead(i);
    message +='!';
    Serial.println(message);
  }
}

void loop(){

  
  while(Serial.available()>=8)
  {
    char c = Serial.read();
    Serial.println(c);
    if(c == '#')
    {
      recieve_order();
    }
  }
  
  read_input_analog();
  read_input_digital();
}

EDIT 1 Every property not mentioned in the Serial Port settings have been left at default.

DataBits property defaults to 8, the Parity property defaults to the None enumeration value, the StopBits property defaults to 1

EDIT 2

I have noticed UART only works as Half Duplex which I mistakenly thought was Full Duplex. I have since added a delay to the sending of information from the arduino side with the use of the millis() function. This helped the problem quite a bit however the problem still persists but not as often.

EDIT 3

I have made it so the read code on the Arduino clears the buffer faster by appending a string everytime a byte enters the buffer instead of having it wait to fill 8 bytes before doing something.

It helped as sending individual messages no longer crashed the program, however now with a Slider to control an analog signal, the program crashes as scrolling through the slider sends a chunk of messages!

I have also tested with a Windows Forms project (That I Found online) and it's slider worked like a charm! No crash!

System.IO.IOException
  HResult=0x80070079
  Message=The semaphore timeout period has expired.

  Source=System
  StackTrace:
   at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
   at System.IO.Ports.SerialStream.EndWrite(IAsyncResult asyncResult)
   at System.IO.Ports.SerialStream.Write(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
   at System.IO.Ports.SerialPort.Write(String text)
   at Washing_Machine_Interface.Communication.Arduino.ArduinoProtocol.AnalogValue(Int32 pinNumber, Int32 power) in D:\André O Viking\Projetos\Washing_Machine_Interface\washing-machine-interface\WM_Interface\Washing_Machine_Interface\Washing_Machine_Interface\Communication\Arduino\ArduinoProtocol.cs:line 67
   at Washing_Machine_Interface.MainWindow.Pin2Slider_ValueChanged(Object sender, RoutedPropertyChangedEventArgs`1 e) in D:\André O Viking\Projetos\Washing_Machine_Interface\washing-machine-interface\WM_Interface\Washing_Machine_Interface\Washing_Machine_Interface\MainWindow.xaml.cs:line 115
   at System.Windows.RoutedPropertyChangedEventArgs`1.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
   at System.Windows.Controls.Primitives.RangeBase.OnValueChanged(Double oldValue, Double newValue)
   at System.Windows.Controls.Slider.OnValueChanged(Double oldValue, Double newValue)
   at System.Windows.Controls.Primitives.RangeBase.OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   at System.Windows.DependencyObject.SetCurrentValueInternal(DependencyProperty dp, Object value)
   at System.Windows.Controls.Slider.UpdateValue(Double value)
   at System.Windows.Controls.Slider.OnThumbDragDelta(DragDeltaEventArgs e)
   at System.Windows.Controls.Slider.OnThumbDragDelta(Object sender, DragDeltaEventArgs e)
   at System.Windows.Controls.Primitives.DragDeltaEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
   at System.Windows.Controls.Primitives.Thumb.OnMouseMove(MouseEventArgs e)
   at System.Windows.UIElement.OnMouseMoveThunk(Object sender, MouseEventArgs e)
   at System.Windows.Input.MouseEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at Washing_Machine_Interface.App.Main()

Solution

  • After a few days I have figured out what happened!

    The Arduino Due was crashing do to an overflow of data coming from C#!

    When it crashed C# would throw the Semaphore Timeout Exception and would never recover from it even when a try and catch was in place, and I gave it time to clear the traffic of all the data sent!

    Initially the message sent from C# was the following:

    serialPort.Write("#30" + pinNumber + power + "\n");

    • '#' -> order
    • 3 -> Analog Output
    • '0'+pinNumber -> pin number (0 is added when the pin number has only 1 digit ex. 02 por pin 2)
    • power -> value from 0-255
    • '\n' -> end command

    When using a slider it would send this message with a different power value many times and in a short time. Crashing the Arduino and requiring a reset.

    After days of testing I tried the following message as an experiment:

    serialPort.Write(power + "\n");

    I temporarily changed the arduino code to only change pin 2, that way only a value to change to was required.

    After this change , no matter how fast I dragged the slider from on side to the other, the Arduino would comply and the problem was solved.