The code is starting to come together. I wanted a way to check when the roomba is docked and charging. Of course I could use the datastream for this. It has a bit set for when it's docked at its home base and a byte for its charging state.
But when it's docked, the roomba also gives a message every second. Something like:
bat: min 16 sec 35 mV 16198 mA 1102 tenths-deg-C 223 mAH 2496 state 5
When it's docked, I don't need the high speed datastream. So I want to switch over to getting the info from this message instead. Then the ESP8266 has its processing power freed up to do ... Idunno ¯\_(ツ)_/¯
The docked message is in ASCII instead of bytes. I want the values to be interpreted AND to be throughput to MQTT (when debugging).
You know what this means of course:
IT'S HEADACHE-... I MEAN CHARACTER-ARRAY-TIME!
Some of the characteristics of the message we're dealing with:
- The message is terminated with a line feed char '\n';
- It always starts the same "bat: min ....";
- It has a fixed number of elements;
- The elements are in a fixed order;
- Every element has a name and a value;
- Name and value are separated by one space;
- All elements are separated by two spaces;
- All values are represented by integers;
- The element-headers only appear in this message and in no other message (such as boot up);
I messed around with a few different ways to interpret the message.
- Get the complete message in a shifting buffer (or circular buffer with rotating index). Then await the correct header, set the starting index and interpret the complete message;
- Ignore any message header and focus on the elements. Do all checks parallel. So pass the incoming character to multiple functions and only catch the separate elements;
In the end I settled on the following:
- Awaiting the first character of the header 'b'. This is a simple check so won't gobble up processing time for every incoming character;
- When the first character of the header is found. Try checking for the rest of the header message: "at: min ". If this condition is met, we can be confident we found the start of a docked message;
- Put the rest of the incoming characters in a buffer until the end of the message (or a timeout and throw away the message);
- Split the buffered message in separate elements and interpret the values;
All is done in a separate function that gets passed an incoming character. It uses C string functions as strcmp(), strcpy() and strtok().
void Roomba632::CheckChargingMessage(char incommingChar){
//Example:
//"bat: min 16 sec 35 mV 16198 mA 1102 tenths-deg-C 223 mAH 2496 state 5"
static int chargingMessageState;
char* pos;
char buffer[7];
static char j;
static char chargingHeaderBuffer[10], chargingMessageBuffer[80];
switch (chargingMessageState){
case chargingMessageWaitingForHeader:
if(incommingChar =='b'){
chargingMessageState = chargingMessageReadingHeader;
p_requestStopDatastreamFlag = true;
j=0;
}
break;
case chargingMessageReadingHeader:
chargingHeaderBuffer[j] = incommingChar;
j++;
if (j==10){
if (strncmp(chargingHeaderBuffer, "at: min ",10)==0){
chargingMessageState = chargingMessageReadingMessage;
}
else{
chargingMessageState = chargingMessageWaitingForHeader;
}
j = 0;
}
break;
case chargingMessageReadingMessage:
chargingMessageBuffer[j] = incommingChar;
j++;
if (j==79){
j=0;
chargingMessageState = chargingMessageReadingHeader;
}
if (incommingChar == '\n'){
pos = strtok(chargingMessageBuffer, " ");
j = 1;
while (pos != NULL){
if (j== 5){ //Voltage
strcpy(buffer, pos);
batteryVoltage = atol(buffer);
}
if (j== 7){
strcpy(buffer, pos);
current = atol(buffer);
}
if (j== 9){
strcpy(buffer, pos);
batteryTemp = atol(buffer)/10; //Decimals are dropped
}
if (j== 11){
strcpy(buffer, pos);
batteryCapacity = atol(buffer);
}
if (j== 13){
strcpy(buffer, pos);
chargingState = atoi(buffer);
}
j++;
pos = strtok(NULL, " ");
}
state = roombaHome;
charging = true;
docked = true;
p_requestStopDatastreamFlag = true;
chargingMessageState = chargingMessageReadingHeader;
j = 0;
}
break;
}
}
The states are all defined with a bunch of enumerations in the header file. The code is far from perfect, but it works quite nicely and is another element for my roomba class.
Still some improvements: I'm setting states of other state machines outside of the state machines. This is a big no-no that I will try to solve when I tidy up everything.
Also, I'm quite charmed by the idea of a circular buffer with a rotating index and might try that too.
For now, it'll do. I'm getting ~23000 main loops every second. So maybe it has enough spare processing power to do my taxes or plot for world domination??
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.