Go Down

Topic: Serial input into Byte array (Read 958 times) previous topic - next topic

NaokiS

Nov 25, 2018, 06:26 pm Last Edit: Nov 25, 2018, 06:28 pm by NaokiS
Hi all,

Working on a project for an arduino328 based device (Uno, pro mini etc) which can control 2 channels of RGB lighting with fading between colours and transistions. It takes it's commands via I2C or TTL serial and responds appropriately.

For example, if I sent the TTL command "L1S,255,255,255.\n" , this would tell the module that I want:
Light channel 1, Set RGB values R, G, B, stop.

What I have a problem with is extracting that 8 bit value from the serial string to a usable integer. It's probably simple but I always fall at this stage. I2C works fine because I just treat the RGB values as raw 8-bit data during transfers.

The current command handling is probably sloppy and also doesn't use the ttlColour array at the minute since I'm focusing on getting the parser working then update the second stage. Also I don't want to use my old method of Alt-0"value" for the RGB values since, altough it worked, it wasn't convenient especially when using a terminal which I couldn't use that keyboard combo with

Any help would be appreicated

The current code I have is as follows (edited out none related functions):

Code: [Select]

#include <Wire.h>
//#include <SPI.h>
#include "defs.h"
#include <stdlib.h>

unsigned long currentTime = 0;

char iicReceived[16] = {};
byte iicToSend[8] = {};
byte iicLength = 0;
byte iicSendLength = 0;

// 8-Bit array to hold RGB/w? brightness for comms
byte ttlColour[4] = {};


struct RGB {
  byte red;
  byte green;
  byte blue;
  byte setRed;
  byte setGreen;
  byte setBlue;
  bool fade = false;
};

// Dual channel RGB
RGB rgb1 = {};
RGB rgb2 = {};

bool cyclicPattern = 0;

void setup() {
  Wire.begin(moduleAddr);
  Wire.onRequest(iicReq);
  Wire.onReceive(iicRec);
  Serial.begin(57600);

  pinMode(red1, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(blue1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(green2, OUTPUT);
  pinMode(blue2, OUTPUT);

  // Intro
  Serial.println();
  Serial.print(F(moduleID));
  Serial.print(F(" - "));
  Serial.println(programVer);
  Serial.println(F(make));
  Serial.println(F("---------"));
  Serial.print(F("Type: "));
  Serial.println(moduleType);
  Serial.print(F("Type Ver: "));
  Serial.println(moduleVer);
  Serial.print(F("Chipset: "));
  Serial.println(AVR_BOARD);
  Serial.print( F("Compiled: "));
  Serial.print( F(__DATE__));
  Serial.print( F(", "));
  Serial.println( F(__TIME__));
  Serial.println(F("-----------------------"));
}

void loop() {
  // Receive from host
  if (Serial.available()) {
    serialRec();
  }
  // Transmit to host
  if ( iicSendLength > 0) {
    for (int i = 0; i < iicSendLength; i++) {
      Serial.print(iicToSend[i], HEX);
      Serial.print(", ");
    }
    Serial.println();
    for (int i = 0; i < iicSendLength; i++) {
      char temp = iicToSend[i];
      Serial.print(temp);
      Serial.print(", ");
    }
    Serial.println();
    memset(iicToSend, 0, sizeof(iicToSend));
    iicSendLength = 0;
    //serialCom = 0;
  }
  
  if (cyclicPattern == 1) cycleRGB(1);
  currentTime = millis();
  fadeRGB(currentTime);
  ioUpdate(currentTime);
}

// Parse input from TTL
void serialRec() {
  bool endMsg = 0;
  while (Serial.available() > 0 && endMsg == 0) {
    char rc = Serial.read();
    if (rc == '\n') {
      endMsg = 1;
    } else {
      iicReceived[iicLength] = rc;
      iicLength++;
    }
  }
  if (endMsg == 1) {
    byte colour = 0;                      // 0 = red, 1 = green, 2 = blue, 3 = white?
    Serial.print(F("Serial: "));
    for (int i = 0; i < iicLength; i++) {
      // Parse for RGB values if present
      Serial.print(iicReceived[i]);
      if (iicReceived[i] == ',') {
        Serial.println();
        char val[4] = {};
        for (byte c = 0; c < 3; c++) {
          i++;
          Serial.print(iicReceived[i]);
          if (iicReceived[i] == ',' || iicReceived[i] == '.') {
            val[c] = '\0';
            ttlColour[0] = atoi(val);
            Serial.println(val);
            Serial.println(ttlColour[0]);
            break;
          }
          else val[c] += iicReceived[i];
        }

        //ttlColour[colour] =
      }
    }
    Serial.println();
    if (!commandCode()) {
      Serial.println(F("Command not found"));
    }
    //serialCom = 1;
    memset(iicReceived, 0, sizeof(iicReceived));
    iicLength = 0;
    endMsg = 0;
  }
}


bool commandCode() {
  bool exitCode = 0;
  switch (iicReceived[0]) {
    case 'B': // Assume broadcast ID.
      switch (iicReceived[1]) {
        case 'I':
          //Serial.println(F("Broadcast ID"));
          iicToSend[0] = moduleAddr;
          iicSendLength = 1;
          exitCode = 1;
          break;
        case 'N':
          //Serial.println(F("Broadcast Name"));
          iicToSend[0] = moduleAddr;
          iicSendLength = 1;
          exitCode = 1;
          break;
      }
      break;
    case 'C':
      Serial.print("Cycle pattern ");
      if (cyclicPattern == 0) {
        cyclicPattern = 1;
        fadeRate = 50;
        Serial.println("on.");
      }
      else {
        Serial.println("off.");
        cyclicPattern = 0;
        fadeRate = 30;
        setRGB(0, 0, 0, 1);
      }
      exitCode = 1;
      break;
    case 'L':
      //Serial.print(F("Light Channel "));
      byte channel = iicReceived[1] - '0';
      //Serial.print(channel);
      char mode = iicReceived[2];
      switch (mode) {
        case 'S':
          //Serial.println(F(" set"));
          setRGB(iicReceived[3], iicReceived[4], iicReceived[5], channel);
          exitCode = 1;
          break;
        case '0':
          //Serial.println(F(" blank"));
          setRGB(0, 0, 0, channel);
          exitCode = 1;
          break;
        case 'T':

          //Serial.print(F(" target"));
          if (channel == 1) {
            iicToSend[0] = rgb1.setRed;
            iicToSend[1] = rgb1.setGreen;
            iicToSend[2] = rgb1.setBlue;
            iicSendLength = 3;
            exitCode = 1;
          } else if (channel == 2) {
            iicToSend[0] = rgb2.setRed;
            iicToSend[1] = rgb2.setGreen;
            iicToSend[2] = rgb2.setBlue;
            iicSendLength = 3;
            exitCode = 1;
          }
          break;
        case 'A':

          //Serial.print(F(" actual"));
          if (channel == 1) {
            iicToSend[0] = rgb1.red;
            iicToSend[1] = rgb1.green;
            iicToSend[2] = rgb1.blue;
            iicSendLength = 3;
            exitCode = 1;
          } else if (channel == 2) {
            iicToSend[0] = rgb2.red;
            iicToSend[1] = rgb2.green;
            iicToSend[2] = rgb2.blue;
            iicSendLength = 3;
            exitCode = 1;
          }
          break;
        case 'D':
          Serial.println("Daylight White");
          setRGB(255, 255, 255, channel);
          exitCode = 1;
          break;
        case 'W':
          Serial.println("Warm White");
          setRGB(255, 103, 64, channel);
          exitCode = 1;
          break;
        case 'R':
          setRGB(255, 0, 0, channel);
          exitCode = 1;
          break;
        case 'B':
          setRGB(0, 255, 0, channel);
          exitCode = 1;
          break;
        case 'G':
          setRGB(0, 0, 255, channel);
          exitCode = 1;
          break;
        case 'C':

          //Serial.println(F(" program colour"));
          char colour = iicReceived[3];
          if (colour == 'R') {
            if (channel == 1) rgb1.setRed = iicReceived[4];
            else if (channel == 2) rgb2.setRed = iicReceived[4];
            rgb1.fade = true;
            rgb2.fade = true;
            exitCode = 1;
          } else if (colour == 'G') {
            if (channel == 1) rgb1.setGreen = iicReceived[4];
            else if (channel == 2) rgb2.setGreen = iicReceived[4];
            rgb1.fade = true;
            rgb2.fade = true;
            exitCode = 1;
          } else if (colour == 'B') {
            if (channel == 1) rgb1.setBlue = iicReceived[4];
            else if (channel == 2) rgb2.setBlue = iicReceived[4];
            rgb1.fade = true;
            rgb2.fade = true;
            exitCode = 1;
          }
          break;

      }
      //Serial.println();
      if (exitCode != 0 && cyclicPattern == 1) {
        Serial.println("Cycle pattern off.");
        cyclicPattern = 0;
        fadeRate = 30;
      }
      break;
  }
  return exitCode;
}

Robin2

#1
Nov 25, 2018, 08:13 pm Last Edit: Nov 25, 2018, 08:15 pm by Robin2
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

I would just send the data in the form <1,255,255,255> then the position of the items determines how they are interpreted. I don't see any value in the letters 'L' and 'S'

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Danois90

Seems like you are overcomplicating things, look at this:

Code: [Select]
#define MAX_LINE_LEN 25

bool readCommand(char* cmd, byte *rgb)
{
  char line[MAX_LINE_LEN]; //One line of data
  char idx = 0, cc; //Buffer index
  while ((idx < MAX_LINE_LEN - 1) && Serial.available())
  {
    cc = Serial.read();
    if (cc == '\n')
    {
      line[idx] = 0;
      break;
    }
    else line[idx++] = cc;
  }
  if (idx >= MAX_LINE_LEN) return false; //Newline was not found
  char* token = strtok(line, ",");
  idx = 0;
  while (token && (idx < 4))
  {
    if (idx == 0) memcpy(cmd, token, strlen(token) + 1); //+1 to include \0
    else rgb[idx-1] = atoi(token);
    token = strtok(NULL, ",");
    idx++;
  }
  return (idx == 4); //Success if 4 tokens was received
}

void loop()
{
  char cmd[20];
  byte rgb[3];
  if (readCommand(cmd, rgb))
  {
    Serial.print("Command: ");
    Serial.print(cmd);
    Serial.print(" R=");
    Serial.print(rgb[0]);
    Serial.print(" G=");
    Serial.print(rgb[1]);
    Serial.print(" B=");
    Serial.println(rgb[2]);
  }
}
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

NaokiS

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

I would just send the data in the form <1,255,255,255> then the position of the items determines how they are interpreted. I don't see any value in the letters 'L' and 'S'

...R
The only reason I do that is because I also need to read module ids, capabilities and sometimes not a light command so using L is simple and says the following is a light command. S is similar since im setting rgb values. Sending L10 clears the light data, L1W sets it to warm white etc. But i appreciate the layout suggestion, just don't get how to take 3 chars and interpret that into a byte.

Danios i will look at that code later tonight, am on phone at the minute, thabks for the example

Robin2

The only reason I do that is because I also need to read module ids, capabilities and sometimes not a light command so using L is simple and says the following is a light command. S is similar since im setting rgb values.
Then if you send the 'L' or 'S' as separate entities - like this <L,1,S,255,255,255> - it will be easier to parse.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Danois90

In my example he could (after readCommand in loop) just get "L" from cmd[0], "1" from cmd[1] and "S" from cmd[2]. If the command always consists of 3 characters there is no real need to separate it further.
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

NaokiS

Seems like you are overcomplicating things, look at this:

Code: [Select]

  char* token = strtok(line, ",");
  idx = 0;
  while (token && (idx < 4))
  {
    if (idx == 0) memcpy(cmd, token, strlen(token) + 1); //+1 to include \0
    else rgb[idx-1] = atoi(token);
    token = strtok(NULL, ",");
    idx++;
  }
  return (idx == 4); //Success if 4 tokens was received
}

Sorry to sound a pain, but could you explain how this part works? I'm all for using it but I just dont understand what exactly it does  :smiley-confuse:

jremington

#7
Nov 26, 2018, 06:50 pm Last Edit: Nov 26, 2018, 06:51 pm by jremington
Quote
I just dont understand what exactly it does
Solve that problem by mentally stepping through the code, line by line, and determine what each line does and why.

Look up what strtok() does.

If you get stuck on a particular point , post again and ask about that point.

NaokiS

Solve that problem by mentally stepping through the code, line by line, and determine what each line does and why.

Look up what strtok() does.

If you get stuck on a particular point , post again and ask about that point.
Thats the problem, I get the general idea of it but I don't understand how strtok is seperating the string up in such a manageable way. I've been reading on the C++ reference site and even with the description I still dont understand how it's sorted out.

Other than that I can see how the code does seperate out commands and data from positions, just the whole strtok and indexing parts

Danois90

#9
Nov 26, 2018, 08:08 pm Last Edit: Nov 26, 2018, 08:10 pm by Danois90
Which part don't you understand? Source code for strtok and strtok_r / __strtok_r.
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

NaokiS

Which part don't you understand?
That snippet. I get the flow of it but not why it works. For example, I don't know why setting token = strtok gives you multiple sections, or how you can know which part of the string you put in is being read.

Same with the memcpy. I know what memcpy does but not how it gets the seperate values from the input string. or why you later NULL it out

jremington

#11
Nov 26, 2018, 08:41 pm Last Edit: Nov 26, 2018, 08:45 pm by jremington
Quote
how you can know which part of the string you put in is being read.
It is very simple. Successive calls to strtok() point you to successive pieces of the character string. The first one, second one, etc.

This action is very clearly described in several C/C++ reference sites on the web, like this one.

Please study them, we don't have time or interest to write a new tutorial for people who can't be bothered to read the existing tutorials.

NaokiS

It is very simple. Successive calls to strtok() point you to successive pieces of the character string. The first one, second one, etc.
Ok, but I only see the call being used with NULL in that while loop, so that confuses me... Unless it will advance until you give it a second string to parse i guess?

Robin2

I believe the NULL tells it to continue with the cstring it was previously using. The first call the strtok() identifies the ccstring.

Note that strtok() alters the cstring as it works through it so you can't process the same cstring twice unless you made a copy of it before starting with strtok()

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Danois90

When you pass a cstring to strtok, it will return a pointer to the first token of the cstring. When you pass a NULL to strtok, it will return the next token from a previously passed cstring. If you look at the source code of strtok, you can see that there is a static variable in that function that will store the pointer to the cstring. Static variables will "live" even after the method has returned.

As for figuring out where to put the tokens, the variable "idx" (index) is used. When idx is 0, the token is stored to "cmd" using memcpy. When idx is above 0, the token is converted to an integer and stored to "idx-1" in "rgb". As soon as 4 tokens have been properly decoded, the while loop end and a success is returned. If 4 tokens are not decoded ("token" is NULL before idx is 4), failure is returned.
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

Go Up