PC to AVR message handling

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A coworker and I are working on a project together where he sends me messages over UART to my 328P and I do easy hardware things like turn on an LED. We only have like four options so it seemed easy enough to just send an ascii character for each of the options and if else to get the right response.

 

this works well enough for our current project but in an effort to learn new things I was trying to search for a way to handle more than four options efficiently. I think my problem is that I've been searching for a "Micro to PC communication protocol." Understandably this gets me things like UART and CAN.

 

If someone could explain another way to handle messages back and forth that would be great, I'd really like a more appropriate search term or resource to learn various ways to encode and decode messages between the micro and computer.
 

Thanks for any help

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is "UART" a protocol? ;)

 

So you have solved the problem of connecting the AVR to the PC, and it works reliably.  Many/most PCs nowadays do not have any real serial ports, and if they do it will be RS232.  So you probably have RS232 transceiver on your app, and a USB-RS232 converter?  Or perhaps a direct USB-UART converter like FTDI?

 

The Wikipedia article lists some dozens of protocols.  https://en.wikipedia.org/wiki/Li... As mentioned in https://www.eetimes.com/document... and reinforced by the Wikipedia list, they can roughly be grouped by application area.

 

Over a decade ago, Hulsebos listed over 300 identified "Fieldbus & Indistrial Network Protocols". http://www.enodenetworks.com/Lin...

 

So name your poison. ;)

 

A determined search of this Forum and site will uncover prior directly-related discussions.  And indeed, 'Freaks have built or shown "simple" protocols that are between your single-character command and e.g. full-blown ModbusRTU.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MODBUS would be a good starting point for turning thimgs on and off. After that, I think most of us design our own protocols.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

do a google search for S.N.A.P protocol, and easy to use serial comm protocol!

Scaleable Node Address Protocol

 

Have fun!

 

Jim

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a virtually endless list of "communication protocols" (I bet Wikipedia has collated some!) but the fact is that because resources in an AVR are usually "limited" you generally want to keep the protocol simple so you don't need 500K of some hugely complex parser to try and decode the information sent (so that may well rule out XML data transfer then cheeky).

 

So actually your single characters are a wise choice. If you have more commands then remember you have at the very least '0' .. '9' then 'A' .. 'Z' and, if those 36 are not enough, add another 26 with 'a' .. 'z' as distinct from 'A' .. 'Z'.

 

Of course things get a bit more complex when you choose to use 'V' to "set volume" but now you want to either say "set volume to 0.. 255" or perhaps "set volume to 0 ..65535". Now you have to consider single commands versus multiple byte and how you are going to get "0" .. "65535" (or whatever) across the link. If it's just 0..255 then you might choose to send 'V' followed by one byte that just holds a binary value 0x00 to 0xFF. (use 0x00,0x00 to 0xFF,0xFF for up to 65535) but that raise the question of "who is sending these command". If it is a user typing in terminal software they may have a job typing/sending V[0xA3] as two byte. They can type the 'V' but the 0xA3 is trickier. So now you may want to be able to let them actually type "V123" but, if you do that, now the number of characters after 'V' is variable. They might want to set V1 or V123. If just V1 then how do you know the command ended after just 1 digit, it could be the start of V123? So you probably use "V1[enter]" or "V123[enter]" with [enter] being used to signal the end of command transmission.

 

As you can see this can get more and more complex. So you really need to decide who will be sending the data (human or machine) and how complex you want the "dialect" to be.

 

When processing things like "V123[enter]" (which might also have been "V27[enter]") then a useful technique in a "protocol decoder" is a Finite State machine. You start in an "IDLE" state then once you have seen 'V' you switch to a RECEIVING_VOLUME state, as characters (hopefully digits) arrive after that you keep amalgamating them until you see [Enter] (aka '\n') and now you can go to SET_VOLUME_STATE.

 

But what if the user doesn't know the protocol - just that volume setting starts with a 'V'? They send 'V' but then immediately followed by [Enter] with no value? Or how about they typed "V HIGH please" at your code - what then?

 

When the sender is a computer you can ensure that it only will generate streams of symbols that are allowed/expected. But put a hairy chimp into the mix and you could have anything happen. So in your state machine for 'V' processing you maybe enter RECEIVING_VOLUME state but if you now receive anything but '0'..'9' you abandon the command and go back to IDLE.

 

Only thing is that now there may be some "noise" arrives (he really is typing " HIGH please") so you abandoned the 'V' command but now you are faced with an 'H' so is that a command ("Help" perhaps?) or is it just noise? When you decided to abandon 'V' should you really have entered a KEEP_READING_DISCARDING_UNTIL_ENTER state and just "throw away" all following characters until [Enter] (termination of a command) is seen? Probably.

 

So, yeah, this kind of stuff can get real complex, real quick. As an example you may know that "modems" (and these days things like Bluetooth) tend to be driven by "AT commands". That is a seemingly simple interface that can real complicated real quick too. It has an advantage (by design!) that all commands start with "AT" so if you get something you don't understand then just keep reading until the next 'A' then 'T' arrive and the very next letter will be a command. But if you have ever written code to send or receive in a conversation with an "AT device" there are all kinds of hidden "gotchas".

 

So, good luck with this - it can be quite a challenge to design and implement a new protocol.

 

A bit like programming languages every designer seems to think they can design and implement a "better" protocol than all that have gone before them so, like I say, the thousands of the things. You might choose to just find an implementation of a "simple" one that fits the resources of an AVR instead.

Last Edited: Wed. Oct 18, 2017 - 01:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I guessed, there are a lot of pages about comms protocols on Wikipedia but this page perhaps gives a taste of some....

 

https://en.wikipedia.org/wiki/Li...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of the gotchas with any protocol is knowing where a message starts and stops. Which is why pure binary protocols are not seen as often as other types.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

COBS (aka Consistent Overhead Byte Stuffing) is a useful trick to get your head around.

 

https://en.wikipedia.org/wiki/Co...

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just wanted to thank everyone for the suggestions, I got a little busy yesterday but my coworker and I started looking into a couple of the protocol suggestions. I think we are going to sit down and pick one and write up some functions for all projects going forward. I really appreciate the help in getting set on the right path.

 

clawson wrote:
So actually your single characters are a wise choice. If you have more commands then remember you have at the very least '0' .. '9' then 'A' .. 'Z' and, if those 36 are not enough, add another 26 with 'a' .. 'z' as distinct from 'A' .. 'Z'.

I also thought the single character was a sufficient solution for the project at hand, my thought was more in the decoding area though I guess. It is easy enough to if .. else if four options but if I was to use the whole alphabet for our communication protocol is there a more elegant and readable way to decode the command from the letter received?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For a protocol specifically designed to access the peripheral features of microcontrollers, see Firmata:

Firmata is a protocol for communicating with microcontrollers from software on a computer (or smartphone/tablet, etc). The protocol can be implemented in firmware on any microcontroller architecture as well as software on any computer software package (see list of client libraries below).

 

Firmata is based on the midi message format in that commands bytes are 8 bits and data bytes are 7 bits. For example the midi Channel Pressure (Command: 0xD0) message is 2 bytes long, in Firmata the Command 0xD0 is used to enable reporting for a digital port (collection of 8 pins). Both the midi and Firmata versions are 2 bytes long, but the meaning is obviously different. In Firmata, the number of bytes in a message must conform with the corresponding midi message. Midi System Exclusive (Sysex) messages however, can be any length and are therefore used most prominently throughout the Firmata protocol.

 

 

https://github.com/firmata/protocol

 

Arduino implementation: https://github.com/firmata/arduino

 

Arduino documentation: https://www.arduino.cc/en/Reference/Firmata

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pathare wrote:
It is easy enough to if .. else if four options but if I was to use the whole alphabet for our communication protocol is there a more elegant and readable way to decode the command from the letter received?

Look-up the switch() statement in your 'C' textbook.

 

A lookup table of function pointers would be another option ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm familiar with switch statements, I guess there isn't a way of getting around looking through the whole list to match your statement. I will look into lookup tables though, I've heard people talk about them but have never looked into it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pathare wrote:
I guess there isn't a way of getting around looking through the whole list to match your statement

I'm not sure what you mean by that?

 

Obviously, you have to have "something" to handle each option.

 

With a series of if ... else if ... else you literally do have to test each case in turn until you hit the "right" one.

 

A switch means you go straight to the "right" one - the compiler probably implements it as a lookup table anyhow.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

pathare wrote:
I guess there isn't a way of getting around looking through the whole list to match your statement

I'm not sure what you mean by that?

 

Obviously, you have to have "something" to handle each option.

 

With a series of if ... else if ... else you literally do have to test each case in turn until you hit the "right" one.

 

A switch means you go straight to the "right" one - the compiler probably implements it as a lookup table anyhow.

 

 

Ah! that was something I wasn't aware of, I must have missed that! I thought a switch statement basically compiled down to if else statements also. That's a great piece of advice.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pathare wrote:
I thought a switch statement basically compiled down to if else statements also.

It may be. The compiler will choose an appropriate implementation.

 

That's the whole point of using a high-level language - it lets you focus on what you're trying to achieve, rather than implementation details...

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just as example. The main.c that comes as a test program for the well known "FatFs" disk system for micros looks like this:

 

https://github.com/pabigot/FatFs...

 

Look towards the end and the handling of '?' to see the other 1 and 2 letter commands that it supports.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will look into lookup tables though, I've heard people talk about them but have never looked into it

Look, it looks like looking into lookup tables is something to look forward to. 

When in the dark remember-the future looks brighter than ever.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 Boy, you only have to say "protocol" around here to here to get a real "rain" storm of techno-babble dumped out.

 

Especially since you said that you are only sending one character from the PC to the AVR then doing a simple response to that one character.   What you are describing is a "remote menu-ing system", which doesn't need a 'protocol'.  A protocol is a dense and complex systematic means of sending and receiving blocks of data with error detection and correction.

 

Try using the case: switch command.  With lots of options, people set-up tables of pointers_to_functions.  Say you had 26 options with a single letter ['A' to 'Z'].  Set up a table of function pointers and use the ASCII char (with 0x41 subtracted) as an offset into the table.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
Boy, you only have to say "protocol" around here to here to get a real "rain" storm

Hey, thats the Freakin' protocol around here! laugh

 

IF all you are looking to do is a sinple demonstration of turning an LED on or off, then I have to agree that the excellent suggestions above are a bit of overkill.

 

Simonetta wrote:
Try using the case: switch command

That is not a bad idea and what I did recently for this:

 

http://www.avrfreaks.net/forum/f...

 

Very short distance, nothing spectacular.  It just worked.

 

HEre is the receive routine:

 

void loop() {
  // put your main code here, to run repeatedly:

    if(Serial.available())
        {inputString = Serial.readStringUntil('\r');
          recvData = (inputString.toInt());
          
          if(recvData != currentColor)
            {
              currentColor = recvData;
              dataReady = 1;
            }
        }//end of if
        
        if(dataReady == 1)
          {
            switch(currentColor)
            {
                  case white:
                  {
                    analogWrite(redledPin, wht[0]);
                    analogWrite(greenledPin, wht[1]);
                    analogWrite(blueledPin, wht[2]);
                    dataReady = 0;                   
                  } break;
                  
                  
                  case yellow:
                  {
                    analogWrite(redledPin, yel[0]);
                    analogWrite(greenledPin, yel[1]);
                    analogWrite(blueledPin, yel[2]);
                    dataReady = 0;
                  } break;
                  
                  case orange:
                  {
                    analogWrite(redledPin, ora[0]);
                    analogWrite(greenledPin, ora[1]);
                    analogWrite(blueledPin, ora[2]);
                    dataReady = 0;
                  } break;
                  
                  case pink:
                  {
                    analogWrite(redledPin, pnk[0]);
                    analogWrite(greenledPin, pnk[1]);
                    analogWrite(blueledPin, pnk[2]);
                    dataReady = 0;
                  } break;
                  
                  case red:
                  {
                    analogWrite(redledPin, rd[0]);
                    analogWrite(greenledPin, rd[1]);
                    analogWrite(blueledPin, rd[2]);
                    dataReady = 0;
                  } break;
                  
                  case red_yellow:
                  {
                    analogWrite(redledPin, rdyl[0]);
                    analogWrite(greenledPin, rdyl[1]);
                    analogWrite(blueledPin, rdyl[2]);
                    dataReady = 0;
                  } break;
                  
                  case yellow_green:
                  {
                    analogWrite(redledPin, ylgr[0]);
                    analogWrite(greenledPin, ylgr[1]);
                    analogWrite(blueledPin, ylgr[2]);
                    dataReady = 0;
                  } break;
                  
                  case blue_green:
                  {
                    analogWrite(redledPin, blgr[0]);
                    analogWrite(greenledPin, blgr[1]);
                    analogWrite(blueledPin, blgr[2]);
                    dataReady = 0;
                  } break;
                  
                  case blue:
                  {
                    analogWrite(redledPin, blu[0]);
                    analogWrite(greenledPin, blu[1]);
                    analogWrite(blueledPin, blu[2]);
                    dataReady = 0;
                  } break;
                  
                  case purple:
                  {
                    analogWrite(redledPin, prpl[0]);
                    analogWrite(greenledPin, prpl[1]);
                    analogWrite(blueledPin, prpl[2]);
                     dataReady = 0;
                  } break;
                  
                  default:
                  {
                    
                  }
                  
                }//end of switch
                
          }//end of if

So, in your case(no pun intended) you could do this:

 

 switch(currentColor)
            {
                  case a:
                  {
                    ledPin = high;   //turn LED on
                    dataReady = 0;   //reset dataready flag                
                  } break;
                  
                  
                  case b:
                  {
                    ledPin - low;   //turn led off
                    dataReady = 0;   //reset dataready flag
                  } break;
                  
                    default:
                  {
                    
                  }
                  
                }//end of switch

Untested.

 

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
you said that you are only sending one character from the PC to the AVR 

(S)he said that's what (s)he's currently doing - but specifically asked about how to extend this to a more general "protocol".

 

Of course, how "complicated" that "protocol" needs to be depends on how complex the control requirements to be supported are ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have to say, I've never had such a response to anything I've posted to a community before. I really appreciate all the help. I actually needed the responses from everyone. My question was how to extend the functionality of what I have and how to create a more robust protocol for later. so kudos to you all : )

 

I think technically the part about a case structure not necessarily compiling down to if elses and thus being the go to way of performing this "Remote menuing system" is actually what I was looking for with this post.

 

I'm a hardware engineer and have always wanted to bridge more into the software side of things so I'm still a newbie at this. My group does however work in an industrial environment and I have projects coming up where I will need to send  commands back and forth to the PC and send data back to the PC so I wanted to start looking into actual protocols for these projects.

 

Thanks again for all the great knowledge you have shared with me thus far!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, switch is the "go to way" of performing any choice where you want to go directly to the appropriate option.

 

A chain of if-else-if-else is good for where you specifically to order the tests; eg, to give some priority, or reduce processing time for come options, etc.

 

This is "general programming" - not specific to menus.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pathare wrote:
I think technically the part about a case structure not necessarily compiling down to if elses and thus being the go to way of performing this "Remote menuing system" is actually what I was looking for with this post.
If you like the approach you can help it along it's way a bit:

typedef void (*fptr_t)(void);

void commandA(void) {
    //handle command A
}

void commandB(void) {
    //handle command B
}

void commandc(void) {
    //handle command C
}

const struct {
    char input;
    fptr_t function;
} commands[] = {
    {'A', commandA},
    {'B', commandB},
    {'C', commandC}
};

int main(void) {
    char c = getinput();
    for (int n = 0; n < 3; n++) {
        if (c == commands[n].input) {
            commands[n].function();
        }
    }
}

That doesn't make a lot of sense for such a simple example (or one where they are consecutive A,B.C) but the concept of a table of input + function_to_call is often a good one for multiple command processing as the "core" of the thing is not some large, spread out, switc/case structure but here:

    {'A', commandA},
    {'B', commandB},
    {'C', commandC}

which directly ties an input to the function that will handle it.