2

I know this is a basic question, but as a hardware electronics guy and not a programmer I cannot seem to fathom how to do this - so any examples of code would be greatly appreciated:

I want the arduino to spit out some initialization chatter on startup - (ok, done easy).

Then I want it to listen for serial commands, they can all be same length if that helps but I want them to be strings. i.e. identifier followed by value

Command --- Result

PWR_TRUE --- Power on device

PWR_FLSE --- Power off device

REP_FREQ --- Print frequency over serial

SP000752 --- Set power value to 752

SP016675 --- Set power value to 16675

Obviously the commands are two types, the first two being booleans and then integers, but I cant seem to find any examples not working off a single ascii character value, which is useless to me. I need more information and then from the message be able to break it down into something the arduino does.

An example of how to import whole commands and either do something, and / or chop the command up to get the value out, would be most useful.

Many thanks in advance :)

EDIT/UPDATE: I now seem to have found a solution thanks to Edgar as given in the comments. However I now have another question relating to updating the variables so as to effect the loop, as well as performing actions upon receipt of commands (which itself now works) LINK HERE

  • There are several parts to this process: Collecting the input; locating the command and the argument (value), validating each of them (is there such a command? Is the value valid?); and finally, acting on the result. What part(s) are you having trouble with? – JRobert Jun 28 '17 at 15:17
  • If you just want an example, you may take a look at this Simple Arduino command line interpreter. – Edgar Bonet Jun 28 '17 at 15:21
  • Hi JRobert, sorry I missed the comments section... Essentially I have my output code for each relevant function done, and i currently have seperate arduinos doing about 8 small mundane tasks for a single item. Now I want to combine them and use software to send commands over serial to either update variables the arduino is using or command it to execute various pieces of code. I will have a look at that Edgar, thank you – Rendeverance Jun 28 '17 at 16:28
  • Edgar that program seems to do the trick nicely, will have a play and see if I can add my required commands :) - thank you – Rendeverance Jun 28 '17 at 16:35
  • Hi Edgar, adapting your linked script worked well, I would mark it as the answer but I cant as its a comment - but thank you greatly! :) – Rendeverance Jun 28 '17 at 16:50
3

Expanding on my comment... I wrote a simple command line interpreter that you can use as a base, or an inspiration, for your own interpreter. In order to not make a link-only answer, I will put here an annotated and simplified version.

The incoming characters are handled by loop(). They are stored into a static buffer until a carriage return (CR = '\r') is received. When the CR is received:

  • the buffer is NUL-terminated in order to make it a proper C string
  • it is handed to the exec() function for interpretation
  • the variable storing the command length is reset to zero for the next command

Here is the code:

while (Serial.available()) {
    static char buffer[BUF_LENGTH];
    static int length = 0;  // length of line received so far
    int data = Serial.read();
    if (data == '\r') {
        buffer[length] = '\0';     // properly terminate the string
        if (length) exec(buffer);  // give to interpreter
        length = 0;                // reset for next command
    } else if (length < BUF_LENGTH - 1) {
        buffer[length++] = data;   // buffer the incoming byte
    }
}

The exec() interpreter function uses strsep() to split the command line into a command verb and subsequent parameters. It then compares the command verb with all supported commands in a long if... else if sequence, like this:

static void exec(char *cmdline)
{
    char *command = strsep(&cmdline, " ");

    if (strcmp(command, "mode") == 0) {
        int pin = atoi(strsep(&cmdline, " "));
        int mode = atoi(cmdline);
        pinMode(pin, mode);
    } else if (strcmp(command, "read") == 0) {
        int pin = atoi(cmdline);
        Serial.println(digitalRead(pin));
    } else if (strcmp(command, "write") == 0) {
        int pin = atoi(strsep(&cmdline, " "));
        int value = atoi(cmdline);
        digitalWrite(pin, value);
    } else {
        Serial.print(F("Error: Unknown command: "));
        Serial.println(command);
    }
}

This one supports the three commands:

  • mode <pin> <mode>, meaning pinMode()
  • read <pin>, meaning digitalRead()
  • write <pin> <value>, meaning digitalWrite()

The full version is here: Simple Arduino command line interpreter. Compared to the version in this answer, the main differences are:

  • it echoes the characters it reads, and does so in a way which is friendly to terminal emulators:
    • it echoes CR as CRLF
    • it handles BS (backspace) and echoes it as BS + space + BS
  • it uses strcmp_P() instead of strcmp(), with the PSTR() macro, in order to keep the constant strings in Flash only
  • it supports more commands, including "help".
2

You need to read the serial input into a string(char[]) or String. You then need to parse that string to read a series of acceptable characters and then convert that into an action.

This is semi-pseudo code (not compiled, not tested, not complete)

bool quit = false;
const int MaxCommand = 10;
char command[MaxCommand];
while (!quit)
{
   int index[0];
   bool wholeCommand = false;
   bool invalidCommand = false;
   while (!wholeCommand && !invalidCommand)
   {
      while (!Serial.available())
        ;
      command[index++] = Serial.read();
      invalidCommand = CheckInvalidCommand();
      wholeCommand = CheckWholeCommand();
   }
   if (invalidCommand)
   {
      Serial.print("That's an invalid command");
   }
   else if (wholeCommand)
   {
      if (strcmp(wholeCommand, "PWR_TRUE") == 0) // 0 means it matches
      {
         power(true);
      }
      else if strcmp(wholeCommand, "PWR_TRUE") == 0) // 0 means it matches
      {
         power (false);
      }
   }
}

However That's the hard way to do it. A much easier way is to utilities the user's CPU a bit more and get them to type data in a more computer friendly method. After all users are easier to program, particularly with a base ball bat.

Make you language fixed length:

  • POWER TRUE |FALSE
  • REPOR FREQ
  • SETPW NUMBER

For instance use 5 letters for the command and then until the end of the line for arguments.

Probably the easiest way of manipulating strings for a newbie is to look at the String object class, but the C library string functions maybe the only way to truly get what you want (strcmp, strlen, strtok, strcpy, etc)


EDIT If you are struggling to code something on the Arduino then what I do is try and knock it up on Visual Studio. In this case you will need a simulated Serial object.

/// Code to simulate a Serial object on a Windows PC - Not Arduino code!!!
#pragma once
#include <afx.h>

class CSerial
{
public:
    CSerial () {}
    virtual ~CSerial () {}

    void begin (const int& baudRate) {}
    void flush (void) {}
    int available (void) {return 1;}
    unsigned char read (void) {return 42;}

    void print (const char* data) {_tprintf(L"%s", data);}
    void println (const char* data = NULL) {_tprintf(L"%s\n", data);}
    void print (const int& data) {_tprintf(L"%d", data);}
    void println (const int& data) {_tprintf(L"%d\n", data);}
};

static CSerial Serial = CSerial();

You will need to change read() and available() so that they return meaningful data. You could do this be adding a char[] to the class that is prepopulated with data, such as "POWER TRUE\nREPOR FREQ\nPOWER FALSE" and then use an int as an index into that.

  • Not having much luck implementing it within my sketch yet, but thank you for the heads up, getting lots of errors to debug that are probably my own doing. The issue I cant get my head around is if i do not send the value with the command and have the arduino do the lifting, how does it know which variable its changing? I want the arduino to monitor something as failsafe and update values over serial. I am trying to make it user friendly, but at the same time what seems to be a simple goal is becoming seemingly impossible to implement. :( – Rendeverance Jun 28 '17 at 15:41
  • Read until you see the end of line character '\n' then parse the string you have read. If the string doesn't contain a space then the command is invalid, ignore the line (show error?) and start reading again. Is that what you mean? It really isn't difficult (I appreciate it doesn't feel like that), it might be easier to do with pen and paper, write down what you want to do, break that down into small chunks and then implement each of those chunks as a function. It will be easier to debug. – Code Gorilla Jun 29 '17 at 11:46
  • If you are on Windows, download visual studio from Microsoft and use that to code and test. You will need to code a serial object that mocks/fakes the functions you require, but its not difficult. See edited answer for a starting point. – Code Gorilla Jun 29 '17 at 11:48
2

Perhaps you should consider using https://github.com/j123b567/scpi-parser

It's an API library that allows you to build an 'instrument' that uses a recognized standard 'SCPI' as the communications protocol. It makes your device compliant with an industry standard, and can be implemented using tested code. Your example protocol is pretty SCPI-like already.

  • Thank you for the suggestion, something I should look into for sure. The code looks a little harder for me to disipher as a noob but I will try and work it out. Do you happen to know if SCPI is mandatory or just good practice in industry? Thanks – Rendeverance Jun 29 '17 at 21:26
  • It's far from mandatory; god knows I've had to deal with a a bunch of devices that use their own 'prtocol', and invariably, they are terrible. Probably simple for them to code, but far from simple to handle as an end user. To be able to simply say 'SCPI Compliant' will explain a lot to many people. – theNbomr Jul 12 '17 at 19:41
  • Is your device interface intended to interact with humans or with code? If you don't have to cater to human foibles such as forgiveness in commandline editing, it gets easier. – theNbomr Jul 12 '17 at 19:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.