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
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.
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
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);
}
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.
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
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);
}