Click here to Skip to main content
Click here to Skip to main content

Arduino, C#, and serial interface

By , 12 Oct 2012
 

Introduction

The Arduino boards provide an easy-to-use environment to control electrical components such as LEDs and motors. I have done this project with an Arduino Mega 2560 microcontroller board, but you can use nearly any official Arduino board.

What do you need for the tutorial:

  • An Arduino board, connected to your computer by USB
  • The official Arduino IDE (download from here)
  • The Arduino board driver must be installed as described here

Background

During my apprenticeship I have te chance to get into C and C++. So I decided to buy an Arduino board and try to send commands to it using the serial interface of the board.

What is a serial interface? 

A serial interface is used for information exchange between computers and peripheral devices. When using a serial communication the information is sent bit by bit (serial) over a cable.

Modern serial interfaces are Ethernet, Firewire, USB, CAN-Bus or RS-485 even though they are not always called "a serial interface".

About the board

 The Arduino Mega 2560 microcontroller board

The Arduino Mega 2560 board has 54 I/O pins. If you are interested in more details, you can find them at the Arduino Product Page.

Using the code

The Arduino source code

Please note: I wrote the source code with the official Arduino IDE (download from here). When you use the official Arduino IDE, every Arduino program must have at least two methods: setup() and loop().

Code in the setup() method is processed right after connecting the board to power, or updating the software on the board. After the code in the setup() method is processed, the board starts processing the code in the loop method. Code in the loop method processed continously forever (yes, something like a while(true)-loop) until you plug out the power.

How to recognize when information is avaible at the serial port of the board 

Serial interface basics 1

As a first step, I need to recognize when there is any information in the serial input buffer. This buffer has a size of 64 kilobyte on my board, but this size may vary on the Arduino board you are using.

As another requirement I want to be able to see when the board has finished reading data from the serial input buffer. I will turn on the LED on port 13 for one second when the board has finished reading information from the serial input buffer.

First, we are going to define our constants:

/*   CONSTANTS                                                   */
#define LED_TURN_ON_TIMEOUT  1000 //defines how long the LED stays powered on
#define LED_PIN 13                //Pin number on which the LED is connected
#define SERIAL_BAUDRATE 9600      //Baud-Rate of the serial Port

Afterwards, we have to write our setup()-method:

/*****************************************************************
The setup method is executed once after the bootloader is done
with his job.
******************************************************************/
void setup(){
  //setup the LED pin for output
  pinMode(LED_PIN, OUTPUT);
  //setup serial pin
  Serial.begin(SERIAL_BAUDRATE);
}
 

So far so good. But what happens there? Why am I calling these strange methods in the function?

These methods are already delivered with the official Arduino IDE, and there is a certain cause why they exist and why I call them:

pinMode(LED_PIN,OUTPUT);

This method defines that the pin number 13 (constant LED_PIN has the value 13) on the Arduino board is used for output ( though in our case it is an LED, but it could be for example a relais too).

Serial.begin(SERIAL_BAUDRATE)

This method initalizes the serial USB interface on the board to listen for incoming information.

So far so good. But now, we need a method which is listening for the information from the USB serial device.

Because I don't want to get this whole thing more complicated as it already is, I write the whole code into the loop()-method:

/*****************************************************************
The loop method is executed forever right after the setup method
is finished.
******************************************************************/
void loop(){
  int incomingByte = 0;
  
  if (Serial.available() > 0) {//Only when serial port is avaible
     // read the incoming byte:
     incomingByte = Serial.read(); //read incoming serial data
     
     //Light up LED for specified amount of milliseconds
     digitalWrite(LED_PIN, HIGH); 
     delay(LED_TURN_ON_TIMEOUT);
     digitalWrite(LED_PIN, LOW);
  }
}

incomingByte = Serial.read(); //read incoming serial data

This command reads one byte from the serial input buffer.

After the board has received information, we can continue with turning the LED on and off:

//Light up LED for specified amount of milliseconds
//Afterwards, delay program execution to keep the LED turned on

digitalWrite(LED_PIN, HIGH); 
delay(LED_TURN_ON_TIMEOUT);   //
digitalWrite(LED_PIN, LOW);

Afterwards, the board starts the execution of the loop()-function again. 

Adding the functionality to read different commands

Serial interface basics 2

So we are now able to read information from the serial input buffer.  But what if we want to read a command as String object from the serial input buffer?

For this purpose, I use the String object from the library "WString". This library is already installed with the official Arduino IDE. This means it can be included in an easy way:

 /*   INCLUDES                                                     */
  #include <WString.h> //Official Arduino IDE string library
/******************************************************************/ 

I don't want to mess up the code in the loop function. That is why I need a function which can read a string from the serial input buffer and returns an integer error code.

/*   METHOD DECLARATIONS                                          */
int readSerialInputString(String *command); //Method to read commands from serial input buffer

The code of the method will look like this: 

/*****************************************************************
Description:
This method reads the serial input buffer and writes the content 
of the serial input buffer to the variable which is adressed by the
pointer *command.

Return-Values:
 - MSG_METHOD_SUCCESS -> 
Serial buffer data stored to the variable which is adressed by the pointer *command.
 - ERR_SERIAL_IN_COMMAND_NOT_TERMINATED
Command was not terminated by a '#' (example: "command") or contains no command text (example: "#" or "").

Version: 1.0

Autor:   Marco Bertschi

Licence: http://www.gnu.org/licenses/lgpl-3.0

******************************************************************/
int readSerialInputCommand(String *command){
  
  int operationStatus = MSG_METHOD_SUCCESS;//Default return is MSG_METHOD_SUCCESS reading data from com buffer.
  
  //check if serial data is available for reading
  if (Serial.available()) {
     char serialInByte;//temporary variable to hold the last serial input buffer character
     
     do{//Read serial input buffer data byte by byte 
       serialInByte = Serial.read();
       *command = *command + serialInByte;//Add last read serial input buffer byte to *command pointer
     }while(serialInByte != '#' && Serial.available());//until '#' comes up and serial data is avaible
     
     if(((String)(*command)).indexOf('#') < 1) {
       operationStatus = ERR_SERIAL_IN_COMMAND_NOT_TERMINATED;
     }
  }
  else{//If not serial input buffer data is avaible, operationStatus becomes WRG_NO_SERIAL_DATA_AVAIBLE
    operationStatus = WRG_NO_SERIAL_DATA_AVAIBLE;
  }
  
  return operationStatus;
}

I decided to end every command with a '#', so I can make sure if all information was received completly. 

if (Serial.available()) { 

This line checks if there is any information available at the serial input buffer. This check is important because if there is no data available the software would read empty bytes from the serial input buffer. Depending on how you handle the input commands this would cause serious errors while running the software.

char serialInByte;//temporary variable to hold the last serial input buffer character
     
     do{//Read serial input buffer data byte by byte 
       serialInByte = Serial.read();
       *command = *command + serialInByte;//Add last read serial input buffer byte to *command pointer
     }while(serialInByte != '#' && Serial.available());//until '#' comes up and serial data is avaible 

This is the main part of the method. It reads the information byte by byte from the serial input buffer as long as there is information avaible or the last read byte was a '#'. Every read byte is added to the String object to which the pointer "*command" points. 

if(((String)(*command)).indexOf('#') < 1) {     
  operationStatus = ERR_SERIAL_IN_COMMAND_NOT_TERMINATED;
}   

This check is important if there is more than one information in the serial input buffer avaible. The '#' splits the information in the serial input buffer. Anyways, it insures that we have no empty information in the serial input buffer ("#").

 

At the end of the method the operation status code is returned. I defined the different operation status codes like this:

/*   OPERATION STATUS CONSTANTS                                   */
//MESSAGES
#define MSG_METHOD_SUCCESS 0                      //Code which is used when an operation terminated  successfully
#define MSG_SERIAL_CONNECTED 1                    //Code which is used to indicate that a new serial connection was established
//WARNINGS
#define WRG_NO_SERIAL_DATA_AVAIBLE 250            //Code indicates that no new data is avaible at the serial input buffer
//ERRORS
#define ERR_SERIAL_IN_COMMAND_NOT_TERMINATED -1   //Code used when command is empty
/******************************************************************/

Now it became pretty easy to use the method in the loop function:

String command = "";  //Used to store the latest received command
int serialResult = 0; //return value for reading operation method on serial in put buffer
  
serialResult = readSerialInputCommand(&command);
if(serialResult == WRG_NO_SERIAL_DATA_AVAIBLE){//If there is no data avaible at the serial port, let the LED blink
    digitalWrite(LED_PIN, HIGH);
     delay(250);
     digitalWrite(LED_PIN, LOW);
     delay(250);
  }
  
 String command = "";  //Used to store the latest received command 

 This string object is used to hold the information string to which the serial input buffer information will be stored.

if(serialResult == WRG_NO_SERIAL_DATA_AVAIBLE){//If there is no data avaible at the serial port, let the LED blink
    digitalWrite(LED_PIN, HIGH);
     delay(250);
     digitalWrite(LED_PIN, LOW);
     delay(250);
  } 

 If the methods return value tells me that there is no new information avaible at the serial input buffer I let the LED blink.

Off course we are now able to send the command back through the serial output:

if(serialResult == WRG_NO_SERIAL_DATA_AVAIBLE){//If there is no data avaible at the serial port, let the LED blink
     digitalWrite(LED_PIN, HIGH);
     delay(250);
     digitalWrite(LED_PIN, LOW);
     delay(250);
  }
  else{
    if(serialResult == ERR_SERIAL_IN_COMMAND_NOT_TERMINATED){//If the command format was invalid, the led is turned off for two seconds
      digitalWrite(LED_PIN, LOW);
      delay(2000);
    }
    Serial.print(serialResult);
    Serial.print(":  ");
    Serial.println(command);
  }

Testing the code  on the Arduino board

The official Arduino IDE comes with a tool called "Serial Monitor". You can start it after you have uploaded the code to the Arduino board with the key combination [Ctrl]+[Shift]+[M].

 

The Serial Monitor tool which is provided by the official Arduino IDE

If you send any commands to the Arduino board using the "Serial Monitor" and it replies to you with a string in the format [Number: Command you have sent] you have done it:
You can now "talk" to your Arduino board. And it even replies to you!

The C#/WPF source code  

The C# code is a simple thing, you can send commands to the board by using the SerialPort class which is avaible in the namespace System.IO.Ports of the .NET Framework. More information about the port object is avaible on the corresponding MSDN Page

Points of Interest

Arduino provides a huge amount of possibilities on working with hardware and connection to a computer. Last but not least, at the official Arduino homepage is a large community support avaible. 

It is very interesting which possibilities are available when you use a serial interface: You can send a pretty huge amount of data and it is not even really complicated.

History

  • 10-October-2012: Created the article.
  • 10-October-2012: Updated
  • 12-October-2012: Added the functionality to read different commands and reply via the serial interface 

What happens in the next Versions of this article:

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Marco Bertschi
Software Developer Apprenticeship
Switzerland Switzerland
Marco made his first steps in programming with C# and the .NET framework. Afterwards he switched over to C++ development with Visual Studio and the Qt framework. Java is also a part of his experience but he ain't gonna use it if you do not force him with all your strength to do so.
 
As a part of the "generation Facebook" he grew up with social networks, video platforms as YouTube and Vimeo are, Smartphones and HD screens but also remembers the noisy annoying analog modems and Windows 95 including DOS games.
 
He never gets enough sleep and beside working in the R&D department of a international medical diagnostic company he is the guy behind the SMGT Web-Portal (A web application to help local shooting clubs organizing their events and staff based on ASP.NET and a MySQL server) and contributing articles to the CodeProject. If he isn't either coding or sleeping you can meet him at the Muay Thai training or somewhere else in central Switzerland.
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberDrABELL10-Oct-12 10:19 
GeneralRe: My vote of 5memberMarco Bertschi10-Oct-12 22:25 
GeneralRe: My vote of 5memberDrABELL11-Oct-12 1:27 
GeneralRe: My vote of 5memberMarco Bertschi12-Oct-12 4:33 
GeneralRe: My vote of 5memberDrABELL12-Oct-12 10:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130617.1 | Last Updated 12 Oct 2012
Article Copyright 2012 by Marco Bertschi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid