Arduino to PC messaging – More devices, protocols, speeds

| 29 Comments

Introduction

A few months ago I made a BIG update to the existing CmdMessenger library. My aim was to make it into a full-featured communication library as well as adding a PC based C# counterpart. I got a lot of positive reactions and mails. In fact, a few of these mails came from a start-up that is considering to use it in a product. So thank you all!

I also got a lot of questions about existing and non-existing functionality. Looking back, the theme seems to be: more devices, more communication protocols (USB, WiFi, Bluetooth, ZigBee), etc) and more speeds.

The library

First of all, the updated library can be downloaded here:

Or at the Github repository:
https://github.com/thijse/Arduino-Libraries/tree/master/CmdMessenger

An overview of all articles on CmdMessenger can be found here:

http://thijs.elenbaas.net/tag/CmdMessenger/

 

Transport Layers

The start-up that asked me about CmdMessenger is planning to control a quadcopter and is looking for the best way to wirelessly connect the base station to the quadcopter. While the Arduino CmdMessenger library was already prepared for different transport layers (just inject it as a stream), in the C# version the transport layer was not nicely separated from the communication protocol. I fixed this, and now you can easily create a new mode of transport and inject it in the library as long as it implements the following interface:

Communication speed
I found out that CmdMessenger is used for a large range of transmission speeds. Let’s consider the quadcopter. I’m no expert on aerial communication, but I imagine that depending on things like distance and line-of-sight, the speed may vary and even may become intermittent. At the other side of the spectrum is the Teensy 3. Like the Arduino, the Teensy 3 implements a virtual serial port over USB. unlike the Arduino, the Teensy 3 lets the serial port run on full USB speed.

Other developers discovered that the .Net implementation of the serial port is not able to cope with these speeds. To be more specific: the NewDataReceived event that notifies us on new data in the serial port buffer, does this by polling the buffer with an interval that matches traditional serial port speeds (4800, 9600, 19200, 38400, 57600 and 115200 bit/s). Because the Teensy is that much faster (12 Mbit/sec), the buffer may overflow in between polling.

In the last version I had implemented my own NewDataReceived event that polls the serial buffer, because the Mono implementation did not support the .NET event. I now added adaptive throttling (similar to what is described in this blog, but generalized. This means that at low communication speeds the buffer polling happens at a relatively large interval, reducing system load. If data comes in faster, the polling speeds ramps up (at the expense of a higher system load).

I have tested this with the Teensy 3, and it seems to run quite smoothly with CmdMessenger.

Command Queues

CmdMessenger has also become more responsive due another big change I introduced: both the send- and receive-commands are now being queued. At the request of a CmdMessenger user I wrote two small applications that have a simple user interface (DataLogging and ArduinoController ). Both worked with the previous version of command messenger, but I was not really satisfied with either of them: Using ArduinoController I noticed that when I pulled the slider that changed the blinking frequency of a led, the slider (called track-bar in .NET) moved very choppy, halting and then make a big jump. The reason is that for every TrackBarValueChanged event a new command was issued to the Arduino and the application blocked until this was done. This is a bad thing, you should never perform time-consuming work on the user interface thread.

Instead commands the track-bar issues are now queued, and the UI thread can continue immediately after queuing them. The commands on the queue are sent in the background as fast as the Arduino can receive them. This queue uses the same adaptive throttling as the buffer polling described above.

The queue command syntax is:

void QueueCommand(SendCommand commandToSend)

The old send command still exists and the syntax has not changed. However, under the bonnet it also uses the send queue, but places the command at the front of the queue instead of at the end. This means that SendCommand is also asynchronous and exits immediately. There is one exception to this: if you want to receive an acknowledgment from the Arduino, SendCommand will block and wait until the command has been received or the timeout interval has expired.

SendCommand also has a new, optional argument that will clear the send queue and/or the receive queue after the command has been send. This is used int the new example TemperatureController to halt data acquisition immediately.

I also implemented a queue for receiving commands, for similar reasons. In the example of the charting application DataLogging, it may happen that the data from the Arduino or Teensy comes in faster than it can be plotted. This means that (serial) buffer will be filled faster than it is being emptied, also resulting in a buffer overflow. We solve this by getting the raw serial data out the buffer and put them as commands on the receive queue.

You may have noticed that queuing is happening on the PC side alone. The reason is that I want to keep the Arduino library as lightweight as possible, while on the PC we have memory and performance to spare. This means that issues can still arise when a connection is intermittent: The Arduino will block on sending the command, halting until data has been send. So far I have not seen this happen, but I will keep an eye out for this, and maybe at some point implement a Arduino buffer similar to the arduino-buffered-serial library.

Generally speaking, the queues are there to buffer incidental speed differences between the sending- and receiving party. However, if the speed difference is structural the queues will continue to expand and eventually grow too large, even for a PC. The only way to remedy this is by throwing away commands. This leads me to the next improvement:

Queue strategies

The main question is: how to decide which commands to throw away? The short answer is: I don’t know.

The slightly longer answer is: I don’t know, since it depends on the application and the commands in question. Only the writer of the application knows how to deal with this.

The good thing is you can decide for yourself by using specific queue strategies.

Let me give an example: Recall the track-bar that we implemented. when we move the slider, the TrackBarValueChanged event is called many times, resulting in a queue full of SetBlinkFrequency commands. However, we are only interested in sending the most up-to-date (the last) blink frequency.

For this reason we can wrap the commands in a CollapseCommandStrategy. This strategy walks through the queue, and if it finds an other SetBlinkFrequency command, it will replace it at that position in the queue. If there is no SetBlinkFrequency command in the queue, it will add the command to the back of the queue.

Another example, but now for the DataLogging example. If the data comes in fast than we can plot it, the queue will expand and the chart will lag more and more. To remedy this, we remove commands that have been longer on the queue than 1000 milliseconds. In this way, no matter how fast the data comes in, the lag will always be less than 1000 ms. Because we cannot wrap individual commands, we implement a general command strategy

I’ve implemented one other strategy that a command at the front of a queue:

TopCommandStrategy

If your situation requires its own strategy, you can easily make your own, the classes are extremely simple. The only thing you have to do is implement a function that enqueues the command, or a function that dequeues in a way that you want, or both.

The collapse strategy only overrides the default enqueue functionality:

CmdMessenger Arduino updates

On the Arduino side I only made two small updates. My thanks go to Nate who suggested these changes

1. Added the ability to send longs:

long readLongArg()

2. Added the ability to see if the last argument fetched is well formed:

isArgOk()

TemperatureController example

In the last few weeks I have been working on an Arduino based thermostat. It uses a K type thermocouple and a MAX31855 / Max6675 breakout board to measure temperature, a Solid State Relay to turn a heater on and off, and a PID controller implementation to steer the temperature optimally (no overshoots) to the goal temperature. My personal aim is to update update or Gaggia Classic  espresso machine with temperature stabilization, but this sample set up to be so generic that you can use it for everything: brewing beer, controlling a clay oven, etc.

I recommend starting simple. I bought a simple 10 euro electric water boiler, put the relay in the power cable, en let the thermocouple hang in the water.

So now to the program. I plan to write a detailed how to later on, so for now just a short description.

With the trackbar the goal temperature is changed, which is queued to be sent to the Arduino. The top chart shows a scrolling chart of the measured temperature and the goal temperature. The bottom chart shows the control value of the heater, with a value between 0 and 1.

Since the the heater is controlled using a solid state relay, it can only be turned on or off. In order control the heater power we modulate the power. Below is a representation of such a Pulse Width Modulated signal with changing duty cycles

pwm Arduino to PC messaging   More devices, protocols, speeds image

PWM modulation – different duty cycles on the Arduino

We have set our duty cycle time to 3s, in order not switch too often, and we let the PID controller control duty cycle percentage. The resulting PWM cycle is also shown in the bottom chart.

screenshot temperature controller Arduino to PC messaging   More devices, protocols, speeds image

Screenshot TemperatureController example

Next steps

The library is now flexible enough to work with different hardware platforms, and I would really like to try this out for more platforms. As said before, the library is already running nicely on a Teensy 3. The next step is getting it to run on a wireless device such as the Flutter , Spark Core or RFDuino. Who knows, I might get sponsored icon smile Arduino to PC messaging   More devices, protocols, speeds image

29 Comments

  1. Hello Thijs,

    thanks for this very nice library – i am struggling with the synchronuous command call though.

    I cannot get your example SendAndReceiveArguments work correctly. I uploaded the example sketch and recompiled everything with C# (VS2010 Express). It always tells me “no response”. I verified that I use the correct COM Port for communication because mine is COM13 instead of COM6 as in the example code.

    Could you please verify that “blocking” calls work with 3.3 and C#?

  2. Hello Thijs,

    I updated my C# application to use CmdMessenger 3.3 – I noticed that it now consumes a lot of CPU on my computer. It uses all one core 100%.

    I can reproduce this with your example C# code, too. For example starting the DataLogger application with 3.1 needs nearly no CPU % – with 3.3 same as stated above: 25% (or 100% of one core).

    I have VS2010 Express and an arduino based board.

    Even if i don’t call any methods from application neither attach callbacks at all, the behaviour is the same.

    I would appreciate your help – thank youz very much!

    Greets from Germany,
    Sebastian

  3. short update to my last post… the CPU load is distributed across my cores… not only one.

    • Thanks for your comments! You are quite correct. The throttling code had some bugs that actually gave worst of both worlds instead of best. The CPU load was very high, while the responsiveness was very low. With the new bugfixes (version 3.4), receiving data is approximately 100x faster, while resulting in a much lower CPU load.

      Regarding multiple cores: CmdMessenger uses 3 background threads, which .NET prefers to move to different cores. I have added a function that sets affinity to a single core on a single processor: SetSingleCore(). I think it is more of a suggestion than an instruction, though, so your mileage may vary. :-).

      • Ok, that sounds great … already looked into your sources and found a possible bug on initialization of SerialTransport

        Will check your fixes next year.

        Did you check the blocking call question?

        • Hi Sebastian,
          I’m curious to hear what initialization issue you’ve found. The blocking call issue I have trouble to reproduce. Do the other samples work

          Best wishes for 2014!
          Thijs

          • Hello Thijs,

            now i found out the problem: I had to set Serial.dtrEnable to “true”. Otherwise it did not correctly work with Sparkfun Pro Micro. Mega2560 worked correctly without it.

            I do not fully understand the meaning of this property or let’s say why one has to use it on the micro explicitly and not on the mega.

            I added this to the SerialSettings-class as new param in my code. If you are interested in this enhancement – i would be glad to mail the code to you.

          • Hi Sebastian,

            I’ve also never been able to find out precise what DTR does, but I’ve added DtrEnable to the SerialSettings as shown in SendAndReceiveArgument in version 3.5.

            Let me know if it works for you! I’m also very curious what you plan to use the library for.

          • As already stated below by Kia, the dtrEnable from your code also works fine.

            Another issue that I am struggling with is caused by the serial connect that triggers the auto-reset of my Arduino Mega.

            This is what i try to achieve:
            * I implement a GetInfo-callback in my arduino code.
            * I query the available serial ports
            * I send a synchronous GetInfo-message to the available ports
            * I check if the response-message is valid

            The problem:
            * If I connect to the device and then send the GetInfo-message, it seems that this happens to fast and the auto-reset still is ongoing.

            My current workaround:
            * I send the message twice, each time with 1000ms time-out

            Currently I am trying to query everything synchronous on application start up. I can imagine that if i changed everything to a more event-driven method (e.g. wainting for an initial kOnlineStatus-message and then send the GetInfo-message) this would work better. But it is also a little bit more complicated.

            Do you have a kind of pattern which should be applied to ensure, that the reset has run through? Or any other suggestion for an elegant solution?

          • Hi Sebastian,

            That funny: I wrote similar code! I want to poll and see if an Arduino (running a compatible sketch) is connected to my PC and based on that initiate communication. I also stumbled on the issue of waiting for the restart. As far as I know there is no smart way to detect whether a board is still resetting.

            My current solution is to have sufficient patience :-). I keep on polling all available ports with a certain interval until one responds.

            While I do not know of a more elegant way of ensuring the reset has been run through, I was thinking of opening all ports in parallel to reduce the overall time for finding and connecting.

            I have yet not done this myself, but I do not see an issue of creating multiple instances of the serialConnection and CmdMessenger, where each polls a different candidate serial port. It sounds like you are already doing that. If so, I would love to hear!

            Thijs

          • little addition to my goal explanation:

            This is what i try to achieve:
            * I implement a GetInfo-callback in my arduino code.
            * I query the available serial ports
            * I send a synchronous GetInfo-message to the available ports
            * I check if the response-message is valid
            * Now I know that I am connected with one of my devices on the current COM-port, otherwise I ignore the COM-port

          • Hello Thijs,

            yes – you are right, I am working with at least two modules right now.
            It seems to work fine so far.

            Now I have another issue.

            Just to give you some background information: I am working on a kind of framework to use arduinos as a hardware interface for flight simulators.
            So I attach different devices to the arduino which are buttons, leds, 7-segment modules, servos and steppers. I have developed a “firmware” which supports all these types.

            You can configure the module individually by uploading a config which is stored in EEPROM and restored after reboot.

            So for each module I want to upload a config which has necessary information about what module is attached to which pin(s) and some other intialization data. The config is a proprietary format with some IDs and so on – nearly no overhead.
            Now the payload of my command is greater then 61 bytes and the command id is two-digits. It seems to me that this does not work because the command buffer is set to 64.
            I imagined that you copy chunks of data to the buffer as long as there is serial data available – but it doesn’t seem so.

            Could you please confirm my doubts and maybe suggest a workaorund?

            Regards,
            Sebastian

  4. Hi Thijs, excellent job!!
    Just a question, i’m trying to verify the connection between PC and Arduino.

    I have:

    Arduino:

    enum
    {
    kConexionOk, // Comando nro 0,
    kVerificarConexion, // Comando nro 1
    };

    void attachCommandCallbacks()
    {
    cmdMessenger.attach(OnUnknownCommand);
    cmdMessenger.attach(kVerificarConexion, VerificarConexion);
    }

    void VerificarConexion()
    {
    //cmdMessenger.sendCmd(kConexionOk, “Arduino inicializado correctamente!”,1);
    cmdMessenger.sendCmdStart (kConexionOk);
    cmdMessenger.sendCmdArg (“Arduino inicializado correctamente!. Conection Ok.”);
    cmdMessenger.sendCmdArg (1);
    cmdMessenger.sendCmdEnd ();

    digitalWrite(ledStatus,HIGH);
    }

    C#:

    private void OnConexionOk(ReceivedCommand arguments)
    {
    string estado = “Arduino status: ” + arguments.ReadStringArg();
    int valorRetorno = arguments.ReadInt16Arg();
    if (valorRetorno == 1){
    arduinoConectado = true;
    } else{
    arduinoConectado = false;
    }
    }

    var command = new SendCommand((int)Command.VerificarConexion);
    _cmdMessenger.SendCommand(command);

    But i want to send:
    var command = new SendCommand((int)Command.VerificarConexion, (int)Command.ConexionOk,100);
    _cmdMessenger.SendCommand(command);

    to get an Ok or time out !!!!

    Could you help me please? Any example?

    thanks in advance!
    Alvaro (Montevideo – Uruguay)

    • If I understand correctly, you would like to wait for the acknowledgement, and then call On ConnextionOK?

      You can do this by something like:

      var command = new SendCommand((int)Command.VerificarConexion, (int)Command.ConexionOk,100);
      var answerCommand = _cmdMessenger.SendCommand(command);
      OnConexionOk(answerCommand);

      You’re welcome :-) Out of curiosity, what are you using the library for?
      Thijs

      • Hi, thanks for your answer!
        I’m trying to manage a machine to cut keys ( used to open doors.. ), send me an e-mail and i’ll explain you in detail!

        Arduino function ” void VerificarConexion() ” would change too, isn´t it?

      • I just want to test the connection PC-Arduino and, if it’s not ok, send a message from PC (C#) to the user.

        Everything else is working correctly!

        thanks in advance, Alvaro

  5. Hello Thijs,
    and first of all thanks for providing such great library. I was having the same issue with your third example. I would get “No reponse” message. As soon as I changed the flag to true the third example worked. Same issue also fixed the 4th example.
    Just as a suggestion, it would be real nice if you had a screen shot of what the expected results for each example is so we can better trouble shoot.
    thanks again.

  6. Hi Thijs,

    I like the cmdmessenger very well! Is it possible to port it to a ethernet version to use with socket etc.??

    Please let me know.

    Thanks,

    Leon

    • Hi Leon. Yes, yes it is! I set it up so that the transport layer (now Serial over USB) can easily be replaced with another one. If you have it working, I’d be happy to include your code in the package. Otherwise, you could always sponsor me with an Ethernet shield, and I may take this up. :-)

  7. Hi Thijs,

    I have almost completed my own library for connumication between a Teensy 3.1 and Ubuntu. I believe it’s got robust features for the linux side but I’m not contemplating dumbing it down for the teensy. But now I’ve come across your library and I’m intrigued. I have a few questions if you don’t mind:

    1) I see examples and I see the “document” html. These are not that helpful to understand the general scheme and pattern for using the library. Is there another document or wiki that summarizes use of all commands and constructs based on situation?

    You’ve provided a lot of good examples but it’s not clear why it is constructed the way it is, e.g. rational for the start() and stop(), etc.

    2) I run on ubuntu (gcc). do you have a library or code that would be compatible for the Linux side?

    I’m very impressed with what I’ve seen. Thanks

    • I’m very interested in your library as well. Can I find it somewhere online?
      1) In terms of documentation:
      - For every example there is an Arduino sketch and a C# counterpart project. I have tried building up the complexity, starting with basic sending and ending with 2-way communication from GUI, using “strategies” for optimal performance. These should give you a start on which functions to use based on situation.
      - The documentation HTML is auto-generated for every release based on code comments. However, the code is quite heavily documented and not all of these show up in the generated documentation. It would probably be good the have a look at the code itself, if you want to understand more
      - There is an overview of the Arduino side commands on the Arduino playground: http://playground.arduino.cc/Code/CmdMessenger
      - The general idea’s and patterns are further explained in blog posts: http://thijs.elenbaas.net/2013/09/arduino-to-pc-messaging/ and http://thijs.elenbaas.net/2013/11/arduino-to-pc-messaging-more-devices-protocols-speeds/ . With the next release I will write an additional post in this series, focusing on performance benchmarking for the Teensy (I have to do some smart things to get this up to par)

      I believe this is a significant amount of documentation, but if you have specific questions, I will try to answer them.
      2) I tested the code under mono, so it should run on Ubuntu, but I have not gotten round to testing this. GCC will, off course, not work.:-)

  8. Hi Thijs!!

    first of all thank you very much for this great library.
    i’m using it to control my project from a nice GUI.
    there is an arduino which driving a relay and some voltage divider to sample the battery voltage and i’ve few logics inside..
    now the logics needs to be calibrated when first installed or replacing to newer dc motor which have other properties.

    i’m using it on win8 with VS2012(with visual micro plug-in) and arduino ide.

    any way it worked perfectly for a month, but two weeks ago it stopped working.
    the GUI won’t start (every time i needed to close it from task manger) and when I go debuge mode: the last trace the debugger has is: Close function(see attached trace photo)

    i didn’t know what to do so i build the library again but no success.
    when i open the serial monitor(the one on arduino ide I see all the data running like it should so I guess the problem is on the PC side)

    the same problem when using the examples that comes with the library.
    i compile the skecth arduino controller, build arduino controller in VS and i get
    the same resault – the gui won’t start. (need to close it from Task Manger).

    on the other examples which are console application – the same thing.
    it’s not working…

    i’m using Arduino Micro, baudrate 115200, the same for 9600, DtrEnable = true.

    The arduino rx blink every time i run my project or any example,
    but again, no further response from the example/project.

    maybe the c# side waiting for some basic first time response? man…..

    i tried to migrate to VS2013 – same problem.
    i’m lost. i can move to develop on another computer which will work – but what if it happens again?

    i’m attaching few screen shots from VS
    1) http://i62.tinypic.com/snoufk.jpg
    2) http://i58.tinypic.com/wa3uop.jpg

    i’ve to solve it! please help

    Amit

    • Hi Amit,

      Sounds like a cool project! Your issue typically this occurs when the serial port is in use (for example by the Arduino serial monitor) or the serial port has changed. The latter has happened to me in the past. It probably has to do with the fact that it is a virtual port.

      I think the serial communication code can be cleaned up a bit and I plan to work on it, but after the next CmdMessenger release/

      • Hi

        i was waiting for your reply badly :)
        of course the port wasn’t used while i tried.

        i went again debuge mode, and as i wrote, it always stops at Close().
        so i went to see what about it…

        this is the original TranportLayer.Close()

        public bool Close()
        {
        try
        {
        if (SerialPort != null && PortExists())
        {
        _serialPort.Close();
        return true;
        }
        }
        catch
        {
        return false;
        }
        return true;
        }

        any way,
        when the port is closed – it has an error but the catch “won’t catch it”.
        so i’ve added this to the top of Close():

        if (!_serialPort.IsOpen)
        return true;

        public bool Close()
        {
        if (!_serialPort.IsOpen)
        return true;
        try
        {
        if (SerialPort != null && PortExists())
        {
        _serialPort.Close();
        return true;
        }
        }
        catch
        {
        return false;
        }
        return true;
        }
        and it worked!!!!!!!

        thank you for this library and also for the eepromex!!

  9. Hi Thijs,

    It’s quite a while that I posted on your website. I am still working on my FlightSim interface software and I am still using CmdMessenger :) It is quite stable and works nicely.

    I have one question regarding the maximum message size:

    I noticed that I am only able to process 255byte long arguments
    I need longer string arguments because I want to save and load a configuration in the EEPROM region (using your EEPROMex of course ;) )

    I don’t really see the limitation here… first I found:
    #define MESSENGERBUFFERSIZE 64 // The maximum length of the buffer (default: 64)
    #define MAXSTREAMBUFFERSIZE 32 // The maximum length of the buffer (default: 32)

    But I cannot really make it let me send strings bigger than 255 chars even if I alter either or both of them.
    Btw, i noticed that in your latest GitHub code you have increased MAXSTREAMBUFFERSIZE 512

    Could you please give me a hint how to handle this?

    My current approach is to split up my string in parts not bigger than 255 chars when sending data from PC to Arduino (save config). But I would have to implement a little bit more code to read the config out of the arduino because I suppose that I am also limited to 255 chars when sending data back from arduino to PC (load config).

    I would be glad if you could help me with a strategy.

    Thanks so much!!!

Leave a Reply

Required fields are marked *.