<

Working with Arduino from a С# application

Published on
3,273 Points
273 Views
Last Modified:
Editors:
Helga York
Specialist in serial communication field
In this article, I will tell you how to read data and control a USB connected Arduino from .Net and UWP applications.

In this article, I will tell you how to read data and control a USB connected Arduino from .Net and UWP applications. You can do this without using third-party libraries. All you’ll need is a virtual COM port or any other virtual serial port solution like com0com (Note: the free solution does not support 64x Windows).


First, let's write a sketch for Arduino. A string with a text containing the value of a variable should be sent to the virtual port. The text will constantly change in a cycle (thus simulating data received from a sensor).


Also, we will read data from the port. If receiving "1", we will turn on the built-in LED, which is located on the 13th pin and is marked on the board with the Latin letter L. When getting “0”, we’ll turn the LED off.


int i = 0;  // a variable for a counter simulating the sensor data
int led = 13;

void setup() {
  Serial.begin(9600);    // set the data rate
  pinMode(led, OUTPUT);  // and the mode of the 13th digital pin used as an output
}
void loop() {
  i = i + 1;  // so we can see that the data changed
  String stringOne = "Info from Arduino ";
  stringOne += i;  // concatenation
  Serial.println(stringOne);  // send the string to the port


  char incomingChar;
 
  if (Serial.available() > 0)
  {
    // record the value received from the port to the variable
    incomingChar = Serial.read();  
   // depending on the value of the variable, turn the LED on or off
    switch (incomingChar)
    {
      case '1':
        digitalWrite(led, HIGH);
        break;
      case '0':
        digitalWrite(led, LOW);
        break;
    }
  }
  delay(300);
}


A WPF application


Now, let’s create a WPF application. The markup is pretty simple. There will be two buttons and a label to display the text received from the port:


<StackPanel Orientation="Vertical">
 <Label x:Name="lblPortData" FontSize="48" HorizontalAlignment="Center" Margin="0,20,0,0">No data</Label>
 <Button x:Name="btnOne" Click="btnOne_Click" Width="100" Height="30" Margin="0,10,0,0">Send 1</Button>
 <Button x:Name="btnZero" Click="btnZero_Click" Width="100" Height="30" Margin="0,10,0,0">Send 0</Button>
 </StackPanel>


Add 2 namespaces:


using System.Timers;
using System.IO.Ports;


And 2 variables with a delegate to the class scope:


System.Timers.Timer aTimer;
SerialPort currentPort;
private delegate void updateDelegate(string txt);


Implement the event Window_Loaded. It will allow us to check all available ports, listen to them, and see whether any port displays a message with the text “Info from Arduino”. If we find the port sending such a message, it means it’s the Arduino port. In this case, we can set its parameters, open the port, and start the timer.


          bool ArduinoPortFound = false;

            try
            {
                string[] ports = SerialPort.GetPortNames();
                foreach (string port in ports)
                {
                    currentPort = new SerialPort(port, 9600);
                    if (ArduinoDetected())
                    {
                        ArduinoPortFound = true;
                        break;
                    }
                    else
                    {
                        ArduinoPortFound = false;
                    }
                }
            }
            catch { }

            if (ArduinoPortFound == false) return;
            System.Threading.Thread.Sleep(500); // wait a bit

            currentPort.BaudRate = 9600;
            currentPort.DtrEnable = true;
            currentPort.ReadTimeout= 1000;
            try
            {
                currentPort.Open();
            }
            catch { }

            aTimer = new System.Timers.Timer(1000);
            aTimer.Elapsed += OnTimedEvent;
            aTimer.AutoReset = true;
            aTimer.Enabled = true;


To read data from the port and compare it with the data I was looking for, I use the ArduinoDetected function:


       private bool ArduinoDetected()
        {
            try
            {
                currentPort.Open();
                System.Threading.Thread.Sleep(1000);
   // a short pause

                string returnMessage = currentPort.ReadLine();
                currentPort.Close();

   // void loop() in the sketch must contain the code Serial.println("Info from Arduino");
                if (returnMessage.Contains("Info from Arduino"))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception e)
            {
                return false;
            }
        }


Now, all that’s left is to implement the timer event handling. The OnTimedEvent method can be generated using Intellisense. Its contents will be as follows:


       private void OnTimedEvent(object sender, ElapsedEventArgs e)
        {
           if (!currentPort.IsOpen) return;
           try // as after the window closing the timer can still be run or the waiting limit can be exceeded
           {
               // clear the buffer
                currentPort.DiscardInBuffer();  
               // read the last value
                string strFromPort = currentPort.ReadLine();              
                lblPortData.Dispatcher.BeginInvoke(new updateDelegate(updateTextBox), strFromPort);
           }
           catch { }
        }

        private void updateTextBox(string txt)
        {
            lblPortData.Content = txt;
        }


We read the value from the port and output it as a label text. But since the timer works in a thread other than the UI thread, we need to use Dispatcher.BeginInvoke. This is where the delegate declared at the beginning of the code will come in handy.


After finishing the work with the port it is desirable to close it. But since you work with it all the time while the application is open, it is logical that you close it when you close the application. Now, let’s add the handling of the Closing event to our window:


       private void Window_Closing(object sender, EventArgs e)
        {
            aTimer.Enabled = false;
            currentPort.Close();
        }


Done. 


Now, we send a message with the text “1” or “0” (depending on the button press) to the port and can start testing the application. It's simple:


       private void btnOne_Click(object sender, RoutedEventArgs e)
        {
            if (!currentPort.IsOpen) return;
            currentPort.Write("1");
        }

        private void btnZero_Click(object sender, RoutedEventArgs e)
        {
            if (!currentPort.IsOpen) return;
            currentPort.Write("0");
        }


By the way, to create a WinForms application is even faster and easier. It is enough to drag the SerialPort element onto the form from the toolbar (as an option, you can drag the Timer element from the toolbar as well). After that, you can open the port (in the particular part of the code), read data from it, and write to it in approximately the same way as in a WPF application.


UWP application



To allow the work with a COM port, there should be the following declaration in the application manifest:


 <Capabilities>
    <Capability Name="internetClient" />
  <DeviceCapability Name="serialcommunication">
  <Device Id="any">
    <Function Type="name:serialPort"/>
  </Device>
</DeviceCapability>
  </Capabilities>


In C# code, we will need 4 namespaces:


using Windows.Devices.SerialCommunication;
using Windows.Devices.Enumeration;
using Windows.Storage.Streams;
using System.Threading.Tasks;


And one variable in the class scope:


    string deviceId;


When loading, we’ll record to it the ID value of the port to which the Arduino board is connected:


private async void Page_Loaded(object sender, RoutedEventArgs e)
 {
  string filt = SerialDevice.GetDeviceSelector("COM3");
  DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(filt);

  if (devices.Any())
  {
     deviceId = devices.First().Id;
  }
 }


The following Task will read 64 bytes from the port and display the text in the field named txtPortData:


private async Task Listen()
        {
     using (SerialDevice serialPort = await SerialDevice.FromIdAsync(deviceId))
            {
                if (serialPort != null)
                {
                    serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
                    serialPort.BaudRate = 9600;
                    serialPort.Parity = SerialParity.None;
                    serialPort.StopBits = SerialStopBitCount.One;
                    serialPort.DataBits = 8;
                    serialPort.Handshake = SerialHandshake.None;

                  try
                  {
      using (DataReader dataReaderObject = new DataReader(serialPort.InputStream))
                     {
             Task<UInt32> loadAsyncTask;
              uint ReadBufferLength = 64;
              dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
              loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask();
              UInt32 bytesRead = await loadAsyncTask;  
                       if (bytesRead > 0)
                       {
                           txtPortData.Text = dataReaderObject.ReadString(bytesRead);
                           txtStatus.Text = "Read operation completed";
                       }
                     }
                  }
                  catch (Exception ex)
                  {
                      txtStatus.Text = ex.Message;
                  }
                }
            }
        }


In UWP applications written in C#, there is no SerialPort.DiscardInBuffer method. Therefore, one of the possible options is to read the data by reopening the port every time, as demonstrated in this example. If you try it, you can see that the count always starts from one. Just about the same thing happens in the Arduino IDE when opening Serial Monitor. This method, of course, isn’t much convenient but if you rarely need to read the data, it will be a solution. In addition, the example written this way is shorter and clearer.


It’s recommended not to declare the port each time again, but to declare it once, for example, at loading. But in this case, it will be necessary to regularly read the data from the port so that it does not fill up with the old one and the data remains relevant. I suppose that the concept of not being able to clear the buffer is that being constantly asynchronously read, data does not really load the system. As soon as the required number of bytes is recorded into the buffer, the next code is executed.



To send data to the port, you can use a similar code:


 private async Task sendToPort(string sometext)
        {

  using (SerialDevice serialPort = await SerialDevice.FromIdAsync(deviceId))
            {
                Task.Delay(1000).Wait();

                if ((serialPort != null) && (sometext.Length != 0))
                {
                    serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
                    serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
                    serialPort.BaudRate = 9600;
                    serialPort.Parity = SerialParity.None;
                    serialPort.StopBits = SerialStopBitCount.One;
                    serialPort.DataBits = 8;
                    serialPort.Handshake = SerialHandshake.None;

                    Task.Delay(1000).Wait();

                    try
                    {

    using (DataWriter dataWriteObject = new DataWriter(serialPort.OutputStream))
                        {

                            Task<UInt32> storeAsyncTask;

                            dataWriteObject.WriteString(sometext);

                            storeAsyncTask = dataWriteObject.StoreAsync().AsTask();

                            UInt32 bytesWritten = await storeAsyncTask;

                            if (bytesWritten > 0)
                            {
                                txtStatus.Text = bytesWritten + " bytes written";
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        txtStatus.Text = ex.Message;
                    }
                }
            }
        }


As you can see, after the initialization of the port and the installation of parameters, there were added 1 second pauses. Without those pauses, it was impossible to make Arduino react. Again, note that it will be better to open the port once, and not to reopen/close it constantly. In this case, you will need no pauses.


Conclusion


Working from UWP with Arduino directly through a virtual serial port is possible without using third-party libraries.


I would also note that Microsoft is cooperating closely with Arduino, therefore there are a lot of different libraries and communication technologies that simplify the development process. Probably the most popular solution is the Windows Remote Arduino library that works with the Firmata protocol.

0
Author:Helga York
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free