Dealing with Serial for a little OS

Hi !
I am trying to deal with Serial to implement a little Operating System on my Arduino.
Here is my actual code :

int incomingByte = 0;
byte index = 0;
int received[50];
int receivedSize = 0;
int i = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("--------------------------------------------------------");
  Serial.println("|                  WELCOME TO MYSERVER                 |");
  Serial.println("--------------------------------------------------------");
  Serial.println("Serial connection started, waiting for instructions...");
  Serial.println("");
  Serial.print("myserver# ");
}

void loop()
{
  if (Serial.available() > 0) {
    incomingByte = Serial.read();
    received[index] = incomingByte;
    index++;
    Serial.write(incomingByte);
    if((incomingByte == 13) && (index == 1)) { //if <<Enter>> was pressed without any command provided
      newPrompt();
    }
    if((incomingByte == 13) && (index > 1)) { //if <<Enter>> was pressed after a command has been submited
      getCommand();
      newPrompt();
    }
   }
}

void newPrompt() {
  received[50];
  incomingByte = 0;
  receivedSize = 0;
  index = 0;
  Serial.println("");
  Serial.print("myserver# ");
}

void getCommand() {
  Serial.println("");
  for(i=0; i<=index; i++)
  {
    Serial.write(received[i]);
  }
}

By now my problem is that i would like my function getCommand() to be able to return a char containing the command name, just as Serial.write is able to print it. This way i hope i would be able to do something like this :

myCommand = getCommand();
switch (myCommand) {
     case 'ls':
          doStuff();
     case 'show datetime':
          doOtherStuff();
     default: 
          Serial.println("unknown command");
}

Regards,

     case 'show datetime':

Which ONE key did you press to get that character?

That code won't even compile. Your getCommand() function doesn't get anything. It doesn't return a value. You can't assign the return value from a void function to a variable.

I really can't believe that you are trying to emulate an operating system without understanding the difference between a character and a string and without being able to make a function return something.

well, thanks for the tips and for this useless answer. Next time, please, stop answering so quicly for so useless infos and concentrate on more accurate advices on other topics.

The code i've provided was just a sample to explain what i am trying to do.

I know that void function will never return anything and by the way that my getCommand() function doesn't get anything (by now). Don't be afraid, i've already wrote functions that return something. In that case, i don't know what type to return from my function to achieve my goal.

But nevermind, i will continue to investigate by myself and find a solution...

I really can't believe that you are trying to emulate an operating system without understanding the difference between a character and a string and without being able to make a function return something.

Apart from your being wrong on these specific points, hopefully such a challenge does not afraid me and makes me have fun :P Sorry if my post was not explained enough, i will post back when i have a more precise question.

Best regards,

You need to learn the difference between character literals and string literals. If it's enclosed in single quotes, it's a character literal (just one single character).

If you want to compare two c-strings (null terminated char arrays) to see if they are equal, you can do that with strcmp(). You have already stored the incoming command line in a global char array, so you can do something like this to test whether the string exactly matches a specific value:

if(strcmp(received, "ABCDEF") == 0)
{
    // handle "ABCDEF" ...

If this is intended to be a flexible command interpreter then you will probably want to tokenise the received command to separate the command from its arguments and then process the arguments in some command-specific way. Note that Bitlash already provides quite a powerful and extensible interpreter.

wow ! that is a usefull reply ! thank you, will investigate this.

Regards,

I've done something similar for a project that i'm working on. What i found is that strcmp might not be suitable if you intend to have a significant amount of commands that arduino needs to respond to. The reason is that each string constant used in strcmp eats/wastes precious SRAM.

The better practice would be to use strcmp_P. Something like this:

 if(strcmp_P(recieved, PSTR("SOME_COMMAND"))==0){
       //do something
}

With strcmp_P your able to store the string constants in flash(accomplished via PSTR) , freeing up SRAM you can use for more meaningful things.

thanks for the hint ! :) I've been told that the use of String is not really recommanded in C++... I am thinking about converting user input command into an int, and then switch integers, something like this :

char command = 'show datetime'; //ascii : 101
  int ascii_command = command; 
  switch (ascii_command) {
   case 101:
      displayDateTime();
      break;
   default:
      Serial.println("unknown command"); 
  }

void displayDateTime()
{
 Serial.println("Do stuff");
}
char command = 'show datetime'; //ascii : 101

Which ONE key did you press to get the ONE character in the single quotes?

PaulS : you really disapoint me…always useless and non-constructive replies, always criticising without any proposal !

Finally i got it working this way :

#include <Wire.h>

int incomingByte = 0;
byte index = 0;
int received[50];
int receivedSize = 0;
int i = 0;

void setup() {
  Serial.begin(9600);
  analogReference(EXTERNAL);
  Wire.begin();
  Serial.println("--------------------------------------------------------");
  Serial.println("|                  WELCOME TO MYSERVER                 |");
  Serial.println("--------------------------------------------------------");
  Serial.println("Serial connection started, waiting for instructions...");
  Serial.println("");
  Serial.print("myserver# ");
}

void loop()
{
  if (Serial.available() > 0) {
    incomingByte = Serial.read();
    received[index] = incomingByte;
    index++;
    Serial.write(incomingByte);
    if((incomingByte == 13) && (index == 1)) { //if <<Enter>> was pressed without any command submited
      newPrompt();
    }
    if((incomingByte == 13) && (index > 1)) { //if <<Enter>> was pressed after a command has been submited
      int command = getCommand();
      switch (command) {
        case 1339: // 'show datetime'
            displayDateTime();
            newPrompt();
            break;
         default:
            Serial.println("unknown command");
            newPrompt();
            break;   
      }
    }
  }
}

void newPrompt() {
  received[50];
  incomingByte = 0;
  receivedSize = 0;
  index = 0;
  Serial.println("");
  Serial.print("myserver# ");
}

int getCommand() {
  int icommand;
  Serial.println("");
  for(i=0; i<=index; i++)
  {
    icommand += received[i];
  }
  return icommand;
}

void displayDateTime()
{
  char* datetimeBuffer = new char[25];
  char *wdBuffer;
  Wire.beginTransmission(0x68);
  Wire.write((byte)0);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7);
  while(Wire.available())
  { 
    byte ss = Wire.read(); 
    byte mi = Wire.read();
    byte hh = Wire.read();
    byte wd = Wire.read();
    byte dd = Wire.read();
    byte mo = Wire.read();
    byte yr = Wire.read();
    switch (wd) {
    case 1: 
      wdBuffer = "Mon"; 
      break;
    case 2: 
      wdBuffer = "Tue"; 
      break; 
    case 3: 
      wdBuffer = "Wed"; 
      break; 
    case 4: 
      wdBuffer = "Thu";
      break; 
    case 5: 
      wdBuffer = "Fri";
      break; 
    case 6: 
      wdBuffer = "Sat";
      break; 
    case 7: 
      wdBuffer = "Sun";
      break;
    default: 
      wdBuffer = "Bad";
    }
    sprintf(datetimeBuffer,"%02u/%02u/%02u(%3s) %02u:%02u:%02u",bcdToDec(dd),bcdToDec(mo),bcdToDec(yr) + 2000,wdBuffer,bcdToDec(hh),bcdToDec(mi),bcdToDec(ss));
  }
  Serial.println(datetimeBuffer);
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

Now, i have to implement the command “set datetime” :wink:

Regards

serial.png

Now, i have to implement the command “set datetime”

Please explain what “set datetime” will cause getCommand() to return.

How will that differ from what “test dateime” would cause it to return?

One is, presumably a valid command that should cause some action, while the other is nonsense.

As is adding the ASCII values of the letters in a string to form a “command”.

PaulS : you really disapoint me…always useless and non-constructive replies, always criticising without any proposal !

You’ve claimed to be an expert in C, and any expert can tell that single quotes denote a character, while double quotes denote a string. So, I figured an expert would simply need a gentle hint that the code was wrong. Next time, I’ll be sure to use a clue-by-four!

You've claimed to be an expert in C

Wow i’ve absolutely never never never claimed that !! simply because that is absolutely NOT right ! I’ve started C/C++ when i bought my Arduino 3 month ago…so i am really far from being an expert and to claim it !
I don’t need you to use a clue-by-four. Have a look at PeterH and racquemis replies : simple and usefull tips that guided me. I don’t need more.

“set datetime” will cause getCommand() to return 1222

“test datetime” will return “unkown command”. I could choose to implement “test datetime” instead of “show datetime” but that is.

Apart from your being wrong on these specific points, hopefully such a challenge does not afraid me and makes me have fun

So, are you, or are you not, aware of the differences between strings and characters? Never mind, I already know.

"set datetime" will cause getCommand() to return 1222

My fumble fingered calculations show that it will return 1209.

"test datetime" will return "unkown command"

How? "unknown command" is NOT an int. The function MUST return an int. The function does nothing but add up the ASCII values of the chars. "test datetime" was not what I suggested. What I suggested used exactly the same letters, in a different order, and will make the function return 1209, too.

Adding up the ASCII values of the letters is not a viable way to distinguish one string from another.

thanks for the hint, you are right : same letters in different order return the same integer ! :~ :~ As you say it is not viable...i have to look for another solution... This is a great hint, thanks, and no hard feelings man ! i apologize

i apologize

I accept. I don't mind helping anyone, as long as my comments are not misconstrued.

The getCommand() function is not needed. PeterH showed you haw to compare the string in received to known strings. Replace the switch statement and cases with a series of if/else if statements.

  received[50];

This isn't doing anything.

i’ve done my homeworks tonight !
this seems to work not so bad :slight_smile:

#include <Wire.h>

char inSerial[20];
byte index = 0;
char *command;
bool unknownCmd = true;

void setup() {
  Serial.begin(9600);
  analogReference(EXTERNAL);
  Wire.begin();
  Serial.println("--------------------------------------------------------");
  Serial.println("|                  WELCOME TO MYSERVER                 |");
  Serial.println("--------------------------------------------------------");
  Serial.println("Serial connection started, waiting for instructions...");
  Serial.println("");
  Serial.print("myserver# ");
}

void loop()
{
  unknownCmd = true;
  if (Serial.available() > 0) 
  {             
    inSerial[index]=Serial.read(); //read data
    Serial.print(inSerial[index]); 
    if((inSerial[index] == '\r') && (index == 0)) { //if <<Enter>> is pressed without any command submited
      newPrompt();
    } 
    else {
      if ((inSerial[index] == '\r') && (index > 0)) { //if <<Enter>> is pressed after a command has been submited
        Serial.println("");
        command = inSerial;
        execute(command);
        newPrompt();  
      } 
      else {
        index++; 
      } 
    }
  }
}

void newPrompt()
{
  index = 0;
  command = "";
  Serial.println("");
  Serial.print("myserver# ");  
}

void execute(char exeCommand[])
{
  if(strcmp(exeCommand, "show datetime\r") == 0) {
    unknownCmd = false;
    displayDateTime();
  }
  if(unknownCmd == true) {
    Serial.println("Unknown command");
  }
  exeCommand = "";
}

void displayDateTime()
{
  char* datetimeBuffer = new char[25];
  char *wdBuffer;
  Wire.beginTransmission(0x68);
  Wire.write((byte)0);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7);
  while(Wire.available())
  { 
    byte ss = Wire.read(); 
    byte mi = Wire.read();
    byte hh = Wire.read();
    byte wd = Wire.read();
    byte dd = Wire.read();
    byte mo = Wire.read();
    byte yr = Wire.read();
    switch (wd) {
    case 1: 
      wdBuffer = "Mon"; 
      break;
    case 2: 
      wdBuffer = "Tue"; 
      break; 
    case 3: 
      wdBuffer = "Wed"; 
      break; 
    case 4: 
      wdBuffer = "Thu";
      break; 
    case 5: 
      wdBuffer = "Fri";
      break; 
    case 6: 
      wdBuffer = "Sat";
      break; 
    case 7: 
      wdBuffer = "Sun";
      break;
    default: 
      wdBuffer = "Bad";
    }
    sprintf(datetimeBuffer,"%02u/%02u/%02u(%3s) %02u:%02u:%02u",bcdToDec(dd),bcdToDec(mo),bcdToDec(yr) + 2000,wdBuffer,bcdToDec(hh),bcdToDec(mi),bcdToDec(ss));
  }
  Serial.println(datetimeBuffer);
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

But now i have a question that will certainly make you laugh :
i’ve realized that my prompt “myserver#” is erasable using the backspace touch !! if you have an idea to avoid this, i take !

regards,

What are you sending serial data to?

i don’t understand what you mean…i send serial data to serial port using serial.print…
what do you mean ?

You are sending a prompt somewhere. Where?

ha ok !
i send my prompt using a basic "Serial.print("myserver# “);”
Also included in the function “void newPrompt()”

i send my prompt using a basic "Serial.print("myserver# ");"

You are sending to Mars, right? By way of Jupiter?

What the hell is on the other end of the serial port?