Understanding serial communication with another software

I posted in my original post. here is it again:

//digital pins connected to Leds//

const int NUM_LEDS = 2;
const int LED_pins[] = { 13, 4 };
const char LED_ids[] = { 'A', 'B' };



void setup() {

  for (int i = 0; i < NUM_LEDS; i++) {
    pinMode(LED_pins[i], OUTPUT);
  }
  Serial.setTimeout(10); //window time to receive data from Serial
  Serial.begin(115200); // start serial communication at 115200 baud (bits per second)
}

void loop() {
  LEDsControl();
}
void LEDsControl() {
  if (Serial.available() > 0) {
    char id = Serial.read();
    int state = Serial.parseInt();
  

    for (int i = 0; i < NUM_LEDS; i++) {
      if (id == LED_ids[i]) {
        digitalWrite(LED_pins[i], state);
        Serial.print(id);
        Serial.print(" ");
        Serial.println(state);
      }
    }
  }
}

Edit: Thanks for your state machine code. It’s looks far more better then my original one. I wander how to impliment it if using multiple char id’s ? B C E F etc etc. you just multiple the code for each id or?

it is working for me. I just want to better understand the code I’m using wich is very much (I guess) easy.

I understand how the first line is working

char id = Serial.read();

Serial.read(); will read the first byte from the serial and I’m storing it as char therefore the number 65 I send is translated into A, right?

the next line uses Serial.parseInt() that according the documentation “Looks for the next valid integer in the incoming serial.”

the thing I don’t understand is - if its looks for the next valid integer how does he knows I’m sending integer? because I send an ASCII number 48 which is not integer rather ..? character? I got confused…

Why not compare it to an ‘A’ and be sure?

how to? could you post an example please?

Surely, you are joking?

sadly I’m not

char id = Serial.read();


if (id == 'A')
{
   //do stuff
}

Thanks. This what my original code is doing.

I still don’t understand the question I rise in post #24

(I understand the question not the solution..)

As I said the example was very verbose on purpose to show how you could approach this.

You could indeed just create as many cases as you have letters but your code can be smarter.

If your code receive a letter, a space and a digit, then that's what the state machine should handle. What it should return is no longer just the digit but what got recognized (the letter + the digit).

here is an example with 'A' to 'F'. In the loop I kept a very verbose switch case to take action based on the recognized label.

enum State {
  WAIT_FOR_LETTER,
  READ_SPACE,
  GET_DIGIT
};

struct Command {
  char letter;
  byte digit;
};

State state = WAIT_FOR_LETTER;
char currentLetter;

bool readCommand(Command &outCmd) {
  if (Serial.available()) {
    char c = Serial.read();

    switch (state) {
      case WAIT_FOR_LETTER:
        if (c >= 'A' && c <= 'F') {
          currentLetter = c;
          state = READ_SPACE;
        }
        break;

      case READ_SPACE:
        if (c == ' ') {
          state = GET_DIGIT;
        } else if (c >= 'A' && c <= 'F') {
          currentLetter = c;
          state = READ_SPACE;
        } else {
          state = WAIT_FOR_LETTER;
        }
        break;

      case GET_DIGIT:
        state = WAIT_FOR_LETTER;
        if (c >= '0' && c <= '9') {
          outCmd.letter = currentLetter;
          outCmd.digit = c - '0';
          return true;
        }
        break;
    }
  }
  return false;
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  Command cmd;
  if (readCommand(cmd)) {
    switch (cmd.letter) {
      case 'A':
        Serial.print("Case A with digit ");
        Serial.println(cmd.digit);
        break;
      case 'B':
        Serial.print("Case B with digit ");
        Serial.println(cmd.digit);
        break;
      case 'C':
        Serial.print("Case C with digit ");
        Serial.println(cmd.digit);
        break;
      case 'D':
        Serial.print("Case D with digit ");
        Serial.println(cmd.digit);
        break;
      case 'E':
        Serial.print("Case E with digit ");
        Serial.println(cmd.digit);
        break;
      case 'F':
        Serial.print("Case F with digit ");
        Serial.println(cmd.digit);
        break;
    }
  }
}

a better version could use function pointers so that it's easy from the loop to take the right action when a match is received. Something like this

enum State {
  WAIT_FOR_LETTER,
  READ_SPACE,
  GET_DIGIT
};

struct Command {
  byte index;
  byte digit;
};

State state = WAIT_FOR_LETTER;
byte currentIndex;

// the callback functions when a specific command is received.
void cmdA(byte d) { Serial.print("Case A with digit "); Serial.println(d); }
void cmdB(byte d) { Serial.print("Case B with digit "); Serial.println(d); }
void cmdC(byte d) { Serial.print("Case C with digit "); Serial.println(d); }
void cmdD(byte d) { Serial.print("Case D with digit "); Serial.println(d); }
void cmdE(byte d) { Serial.print("Case E with digit "); Serial.println(d); }
void cmdF(byte d) { Serial.print("Case F with digit "); Serial.println(d); }


typedef void (*CmdFunc)(byte);
CmdFunc handlers[] = { cmdA, cmdB, cmdC, cmdD, cmdE, cmdF };

bool readCommand(Command &outCmd) {
  if (Serial.available()) {
    char c = Serial.read();

    switch (state) {
      case WAIT_FOR_LETTER:
        if (c >= 'A' && c <= 'F') {
          currentIndex = c - 'A';
          state = READ_SPACE;
        }
        break;

      case READ_SPACE:
        if (c == ' ') {
          state = GET_DIGIT;
        } else if (c >= 'A' && c <= 'F') {
          currentIndex = c - 'A';
          state = READ_SPACE;
        } else {
          state = WAIT_FOR_LETTER;
        }
        break;

      case GET_DIGIT:
        state = WAIT_FOR_LETTER;
        if (c >= '0' && c <= '9') {
          outCmd.index = currentIndex;
          outCmd.digit = c - '0';
          return true;
        }
        break;
    }
  }
  return false;
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  Command cmd;
  if (readCommand(cmd)) {
    handlers[cmd.index](cmd.digit);
  }
}

If the device sending the data prints an integer, say 12 then 2 characters will be sent. a '1' and a '2'

If, however it writes an integer, say 12, then it will send a single byte containing 12

Serial.parseInt() looks for characters that represent an integer, not the integer itself which may well be too large to fit into a byte

that's a clarifying point needed : the state machine proposed above does read only a 1 digit number. the language it recognizes is cmd space digit not multiple digits to form an integer.

if you can send A 1234 or B -2345 then the state machine needs to be modified (the wait for digit would become wait for value)

Thanks. This sentence explained the thing I did not get! it might be a (another) stupid question but I will ask - Is the serial can handle integers such as 1 (byte 00000001) or it will needed to be translated first to ASCII? :face_with_peeking_eye: (When sending from MAX)

edit: I think this: “If the device sending the data prints an integer, say 12 then 2 characters will be sent. a '1' and a '2'“ answer my question

And don't call him Shirley :laughing: :sweat_smile: :rofl: :joy:

You answered your own question

Deep down, in the hardware, all of it is stored in RAM (memory, which can be read and written). RAM is organized as separate bytes, each consisting of exactly 8 bits. Bit is zero 0 or one 1. And that is all there. No other types. Program is in machine code and the processor manipulate the RAM without understanding what value means what, it just execute commands.

There are layers of abstraction over it.

There are system routines, as digitalWrite and pinMode written in C++, there are objects like Serial, which have methods like read and parseInt (which call other routins/functions inside) and there is you code, which calls the routines and uses the objects etc. etc.

The C++ enable easier manipulation with the RAM bytes as it allows to name them (and call that variables) and manipulate them acordingly types assigned to the variables - using one byte for character type, two bytes for int type, 4 bytes for long etc. etc.
So if you increase integer, you do not need to think about increasing the lower byte and carry eventual overflow into higher byte - C++ assumes from variable declaration, that it is, what you want to do.

So back to your code.

The Serial.begin(115200); did many things, one of them was initialising serial HW part of the MCU (processor).

When some serial data came over wires to the right pin, this HW converted them into bytes and the system stored them in RAM in form of First In First Out buffer.

When you called Serial.read(); it took first byte from the buffer and returned it. You created variable char id and let the returned value be stored there. (So to memory allocated for the variable id was stored byte 65 (decimal) = 41 (hex) = 01000001 (bin) - processor does not care, there are 8 bits in one byte - decimal/hexa/bin is as you see it, not as processor see it).

Later you compared id with LED_ids[i] and as you marked both of them as chars (the later by little more complicated way via array) the C++ compiled it to compare value of those bytes and depending on result to set LED state, or not.

LED_ids is declared as array of characters with size 2 (so it takes 2 bytes in RAM) and initialised with values ‘A’ and ‘B’ so in RAM are stored bytes 65 and 66. C++ manipulates each byte separately.

When you declared variable int state the C++ reserved 2 bytes for it and know to manipulate it as 16bits long numerical (which use different manipulation, then LED_pins, which takes also 2 bytes).

You called Serial.parseInt(), which inside started reading bytes from the serial buffer - first was 32 and so was discarted as space before number, than came 49 which was used as character ‘1’ and considered valid part of integer, then came 13 which was considered carriage return and so not valid part of integer and so end of reading for this function. So far collected characters ( just ‘1’) was converted into integer value (00 01) and returned as result.

Both the bytes (00 01) was stored int variable state. (00 as high byte, 01 as low byte.)

Later it was used as second parameter for digitalWrite, so it was converted from int (2 bytes) to uint8_t (1 byte) by using just the lower byte. And later in the digitalWrite it was compared with value of LOW (which is zero 0) and action was taken acordingly.

So basically only you see it as numbers, characters, strings etc. C++ knows, what type you assigned to variables and manipulates byte acordingly your [strike]wishes[/strike] commands, MCU see only bytes and works only with bytes. (And wires around see only some voltages, not knowing, that you see it as serial communication.)

And in some other view, you program see the first byte as command “set first LED to following state”.

@gilhad, please pass on my thanks to your AI

I did not use AI for this.

But I do programm compilers and interprets for like 40 years and now I am working with atmega2560 (exrtacted from Arduino Mega Pro, see GitHub - githubgilhad/MegaHomeFORTH: Mega Home Computer with FORTH - based on ATmega2560 - step to graphic card for HD6309 - VGA, RCA, PS/2 ) so I do know something about this subject :slight_smile:

I asked an AI what it thought about this text and the answer was

This text has many signs of having been written by a human rather than an AI tool. The structure is long and meandering, with run-on sentences, grammar mistakes, typos like “acordingly”, “discarted”, “you code”, and inconsistent capitalization. An AI tool would usually produce more polished grammar and consistent style, unless explicitly prompted to mimic errors.

The explanations mix technical detail with informal phrasing in a way that suggests a non-native English speaker explaining concepts from memory or practice. Phrases such as “and that is all there” or “and there is you code” sound like human mistakes rather than typical AI output.

So the likelihood is much higher that this was written by a person with some technical knowledge, possibly with English as a second language, than by an AI tool.

So @gilhad is English a second language for you and was AI right? :slight_smile:

As usual AI got it slightly wrong. English is my 4. language :slight_smile: