Using 2 types of bluetooth commands

Hi everyone,

I have an Arduino Giga with a HC-05 module (the embedded BLE is above my paygrade). I've managed the things I want it to do, but only seperately.

The first function is to switch relays connected to the Arduino. Sending 1-8 will switch corresponding relays on and off. Check.
The second function is to change a stored EEPROM value. This is to change settings through the app on my phone. To do this I send 4 digits. The first 2 are for the EEPROM address and the second 2 digits are the new value to be saved. Check.

Now I'm trying to wite a function that will recognize if there is 1 or 4 incomming digits. I thought this would be simple but for some reason the change causes the app to crash with "List index too large". I haven't the faintest why changing the code to receive causes the List index to flip out but somehow it does.

Here is what I have (and does not work)

void bluetooth() {

  static int parsedAddress = -1;
  static int parsedValue = -1;
  static byte commandIndex = 0;
  static boolean commandComplete = false;

  if (Serial1.available() > 0 && !commandComplete) {
    
    char incomingByte = Serial1.read();
    outputDebugln(incomingByte);    
    int digit = incomingByte - '0';
    
    if (digit >= 0 && digit <= 9 && commandIndex >= 0 && commandIndex <= 1) {
      int relayIndex = digit - '0';
      digitalWrite(relay[relayIndex], !digitalRead(relay[relayIndex]));
      Serial.println(relayIndex);    
      commandComplete = true; // Mark command as complete
    }
    
    if (digit >= 0 && digit <= 9 && commandIndex >= 2) { // Read digit if it's valid (0-9)
      // Update address or value based on commandIndex
      if (commandIndex < 2) {
        parsedAddress = (parsedAddress < 0) ? digit : parsedAddress * 10 + digit;
      } else if (commandIndex >= 2 && commandIndex < 4) {
        parsedValue = (parsedValue < 0) ? digit : parsedValue * 10 + digit;
      }      
      commandIndex++;
      
      if (commandIndex >= 4) { // Check if the command length has reached maximum
        commandComplete = true; // Mark command as complete
      }
    }
  }
  
  if (commandComplete) { // Process the command if complete

    outputDebug("EEPROM address: ");
    outputDebugln(parsedAddress);
    outputDebug("set value: ");
    outputDebugln(parsedValue);

    EEPROM.writeByte(parsedAddress, parsedValue);

    // Reset variables for next command
    commandComplete = false;
    parsedAddress = -1;
    parsedValue = -1;
    commandIndex = 0;
  }
}

.
:
Edit:
it actually seems to perform better than I though. I did get some functionality when sending 1 digit. I had not accounted for the difference in relay numbering starting at 0, so after changing int relayIndex = digit - 1; I get the correct relay, instead of a cascade tripping them all one by one :sweat_smile:
the digit - '0' seemed to produce the List problem.

it won't recognize a 4 digit command properly though. Can I somehow fill the commandIndex before anything else and use that to determine how to proceed?

I don't understand your code 100%, but wouldn't subtracting '0' from digit which is already 0-9 make relayIndex negative? Accessing a negative index is definitely a source of bugs.

my understanding is the purpose of -'0' is to convert the byte to an integer

Yes, but you've already done that here

digit is a char not a byte hence, the number 5, for example, is represented by the character '5' which has an ASCII value of 53. Thus, subtracting '0' which has an ASCII value of 48 from it gives a result of 5 which is what is required

True, I missed your comment was aimed at int relayIndex = digit - '0';

As I stated in the edit to the original post I corrected this.

Take a look at the Serial Input Basics Tutorial.

It will give the the most reliable method for reading your command.

Yes, but in the code it's done twice, so relayIndex = incomingByte - 2*'0'. This will certainly result in relayIndex going negative.

As I said, that has been corrected (before anyone responded)

Unfortunately not

if (Serial1.available() > 0 && !commandComplete)
{
    char incomingByte = Serial1.read();
    outputDebugln(incomingByte);
    int digit = incomingByte - '0';

    if (digit >= 0 && digit <= 9 && commandIndex >= 0 && commandIndex <= 1)
    {
        int relayIndex = digit - '0';
        digitalWrite(relay[relayIndex], !digitalRead(relay[relayIndex]));
        Serial.println(relayIndex);
        commandComplete = true;  // Mark command as complete
    }

Sigh, but it is.

I did not edit the orignal code I posted, I did add text below the code stating I found the bit that was causing a problem and changed it in my running project.

If I edit the original code I posted none of the topic would make sense afterwards would it?

Very true, and I am glad that you did not do it but what would have been better would be to have posted the corrected sketch in a new reply explaining what had been changed and why. As it was, the edit was easy to miss, which I certainly did

I understand. Storm in a glass of water as we say in Dutch.

I went back the the serial input basics thread (I did read it before but was a little overwhelmed by strcpy and strtokIndx. I've made a start but reallyt have to get some sleep now.

here is what I have ATM. It fits the 4 digit input but I'm still working on the 1 digit input

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing   
int eepromAddress = 0; 
int newSetting = 0;
boolean newData = false;

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    if (Serial1.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,","); // get the first part - the string
    eepromAddress = atoi(strtokIndx);;  // convert this part to an integer
 
    strtokIndx = strtok(NULL, ",");     // this continues where the previous call left off
    newSetting = atoi(strtokIndx);     


}

//============

void showParsedData() {
    Serial.print("EEPROM address ");
    Serial.println(eepromAddress);
    Serial.print("New value ");
    Serial.println(newSetting);
}

It fits the 4 digit input but I'm still working on the 1 digit input

Can you please explain why you need to know the difference between a four digit integer like 1234 and a single digit integer like 2?

Atoi can certainly parse out both.

Is the issue with eeprom management?

I have an Arduino Giga

I think the GigaR1 with the mbed OS can use KV store which is a very simple method for persistent storage.

https://forum.arduino.cc/t/easy-sketch-for-storing-data-values-int-float-etc-in-qspi-flash-giga-r1/1259334/2

If I had been able to find anything about KV storage a year ago I would have looked into it, but the EEPROM is much easier.

The problem is not storage but functionality. When I'm parsing a 4 digit code I'm using it do to operation X. Everything is fine if I only ever wanted to do operation X. Every input would be 4 digits.
Now I'm trying to add functionality to recognized if there is only 1 digit incomming.
When I'm parsing a single digit code I want to do operation Y. Now operation Y is fine, but every time I would send a 4 digit code it uses operation Y and doesn't get to operation X anymore.

After going back to the basic code and using start and end tags I should be able to figure out beforehand how long the command is. Now if the command is 1 digit I want to do operation Y and if the command is 5 digits (4 numbers separated into pairs by a comma) I want to do operation X.

I see that 'ndx' is used to read the individual incomming characters. I'd like to use it to determine length before it get's reset. I'm trying to put a flag in 'recvInProgress' but I'm still testing. I'll let you know if it works.

I won't be able to test untill I get home from work but here is what I have atm:

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];  // temporary array for use when parsing
int eepromAddress = 0;
int newSetting = 0;
bool newData = false;
bool dataToParse = false;
int relayNumber = 0;

//============

void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars); // this temporary copy is necessary to protect the original data because strtok() used in parseData() replaces the commas with \0       
    if (dataToParse) {
      parseData();
      showParsedData();
    } else {
      switchRelay();
    }
    newData = false;
  }
}

//============

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  if (Serial1.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= 4) {
          dataToParse = true;
        }
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0';  // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//============

void parseData() {  // split the data into its parts

  char* strtokIndx;  // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");  // get the first part
  eepromAddress = atoi(strtokIndx); // convert this part to an integer
  
  strtokIndx = strtok(NULL, ",");  // this continues where the previous call left off
  newSetting = atoi(strtokIndx);

  dataToParse = false;
}

//============

void showParsedData() {
  Serial.print("EEPROM address ");
  Serial.println(eepromAddress);
  Serial.print("New value ");
  Serial.println(newSetting);
}

//============

void switchRelay() {

  relayNumber = atoi(receivedChars);
  digitalWrite(relay[relayNumber], !digitalRead(relay[relayNumber]));

}

I was unsure if the start and endmarker would get in the way but I suppose by using atoi they are ignored?

A disconnected state pin had me going in circles for a while. I have it set so if there was no connection on the pin it would not write anything to Serial1.

Afterwards I was receiving on the app but not sending. I noticed 'rc = Serial.read' so I changed it to Serial1.read and now it's working. Almost all of it is working.

After adding

EEPROM.writeByte(parsedAddress, parsedValue);

to the parseData function everything is working, and I am happy :slight_smile:

Complete working code (snippet) here:

const byte numChars = 10;
char receivedChars[numChars];
char tempChars[numChars];  // temporary array for use when parsing
int eepromAddress = 0;
int newSetting = 0;
bool newData = false;
bool dataToParse = false;
int relayNumber = 0;

//============

void loop() {
  bluetoothReceive();
  if (newData == true) {
    strcpy(tempChars, receivedChars); // this temporary copy is necessary to protect the original data because strtok() used in parseData() replaces the commas with \0       
    if (dataToParse) {
      parseData();
      showParsedData();
    } else {
      switchRelay();
    }
    newData = false;
  }
}

//============

void bluetoothReceive() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  if (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= 4) {
          dataToParse = true;
        }
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0';  // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void parseData() {  // split the data into its parts

  char* strtokIndx;  // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");  // get the first part
  eepromAddress = atoi(strtokIndx); // convert this part to an integer
  
  strtokIndx = strtok(NULL, ",");  // this continues where the previous call left off
  newSetting = atoi(strtokIndx);

  EEPROM.writeByte(parsedAddress, parsedValue);
  dataToParse = false;
}

void showParsedData() {
  Serial.print("EEPROM address ");
  Serial.println(eepromAddress);
  Serial.print("New value ");
  Serial.println(newSetting);
}

void switchRelay() {

  relayNumber = atoi(receivedChars);
  digitalWrite(relay[relayNumber], !digitalRead(relay[relayNumber]));
  
  Serial.print("Switching relay ");
  Serial.println(relayNumber);

}
1 Like

Can you please explain how your are using an eeprom with the giga r1.

Is is an external eeprom you are using?

Is there an eerpom library for the device which emulates and eeprom in flash?

Yes, it's this external I2C EEPROM unit.

With it there is no need for emulation, though it's a different library than the normal EEPROM one since it uses I2C. I'm using <I2C_eeprom.h>