Adding a XOR checksum to a Hall Effect voltage reading

Hello all, I think I may have bit off more than I can chew based on my level of knowledge. I have no prior programming background so I probably don’t know a lot of things that are taken as common knowledge. I do however have a desire to learn new things which is what I’m doing here.

What I am trying to do is collect a return voltage read from a variable resistance hall effect sensor attached to the arduino. I need to take that reading and insert it into a message string and transmit that serially to a radio with a checksum as the last byte. The checksum is a XOR of all the preceding bytes for example:

Message string = V;RB0475;
Checksum Calculation = 56 XOR 3B XOR 52 XOR 42 XOR 30 XOR 34 XOR 37 XOR 35 XOR 3B
New Message String = V;RB0475;

I have been sifting through forum posts for 2 days now and I think I have confused myself more than anything. I’m not even sure what have done so far is sufficient to get the job done.

Here is my code as it sits now where the output is correct without a checksum.

/*
  Hall_Effect_Read
  Reads an analog input on pin A5, converts it to voltage, multiplies by 100 to remove "." which is an unsupported character by the radio.
  Calculates a checksum using XOR and appends values to the end of the string.

 */
 //clear existing setup
void setup() {
  ///init serial comms
  Serial.begin(9600);
 }

// Loop for testing, final revision will be run once per power cycle
void loop() {
  //turn pin 9 on to full power
  analogWrite(9, 255);
  //wait 5ms for sensor to settle
  delay(5);
  // read the input on analog pin A5:
  int sensorValue = analogRead(A5);
  // Convert the analog reading to a voltage and multiply by 100 to remove decimal point:
  float voltage = sensorValue * (5.0 / 1023.0) * 100;
  //Setup message format (probably a more efficient way to do this
  //convert voltage to something other than float value since I can't add the float value to a string (errors out)
  word voltvalue = voltage;
  //set lead in string for radio protocol
  String Header = "V;RB0";
  //add voltage
  String Read = Header + voltvalue;
  //add final seperator before checksum bit
  String Transmission = Read + ";";
   //turn pin 9 off
  analogWrite(9, 0);
  // print out the message before checksum (FOR TESTING PURPOSES REMOVE A WHEN COMPLETE)
  Serial.println(Transmission);
  //calculate Checksum
  
  //add checksum to transmission
  
  //transmit message serially
  
  //delay X seconds between reads REMOVE WHEN COMPLETE
  delay(3000);
}

I think I need to convert what I have to an array to actually perform the checksum calculation, but I’m not sure.

I do know that I will need to convert the ascii string to individual bytes and convert those to hex to do the checksum which is what leads me to the array conversion.

Can someone point me in the right direction or give me a sample of code that will do this and I can parse through it an learn what the functions are doing knowing that I’m not chasing a rabbit down a hole?

Thanks.

P.S. Really enjoy reading these forums and seeing the amount of help that is offered.

It seems slightly unusual to apply a binary checksum to an ascii message but I will assume it works as you describe it.

I suggest you use 'C' strings rather than the String class. Mainly because the String class exposes you to a memory leak which may render your sketch unreliable, but also because 'C' strings make it easy to access the individual characters (bytes) that make up the string.

'C' strings are essentially char arrays with a null character at the end of the content. Various standard library functions that operate on 'C' strings use the null terminator to determine where the string ends.

You need to compose your message into a char array and then process each element in the array to derive the checksum. It sounds as if a simple for loop and '^' (bitwise XOR) operator should do this. After appending the checksum to the message, make sure it is null terminated before any printing.

Sorry, the checksum is sent as an ascii character as well in the string. Here is a synopsis of how I got here.

My initial transmission was going to be V;RB4.75;I123456 for example (based on Sensus water meter protocol).

The radio vendor replied that they can not support the ascii character “.” in the RB field “quote below” and they don’t use the I field so it’s not needed
“1. “V;RB0475;;”. We can’t use the ASCII “.”. The checksum is one byte of exclusive-ORs of all previous bytes e.g. V;RB0475;”

Memory leaks aren’t a big concern for me because of the way this will work. The radio will provide me a burst of 5.6v for a period of time (let’s say 1 second). During that time the chip needs to wake up, take the hall effect read, ans transmit back via a quasi Sensus protocol message the value of the read. Then power is turned off and it waits for the next cycle. There is no low-level voltage that keep the chip in sleep mode during the down cycle.

I am using the Arduino right now just as a proof of concept and ultimately want to put a chip inline between the radio and hall effect sensor.

I’ll research ‘C’ strings , thanks for the tip.

have a look at this (not tested)

char msg[24];

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

void loop() 
{
  //turn pin 9 on to full power and wait to settle
  analogWrite(9, 255);
  delay(5);

  // make measurement and convert 
  int voltage = analogRead(A5) * (5.0 / 10.23) ;

  // format the message  - %d  formats an int
  sprintf(msg, "V; RB0%d;", voltage);  

  // switch off the thing
  analogWrite(9, 0);

  Serial.print(msg);

  int xor = calcChecksum(msg);
  Serial.println(xor);
  
  delay(1000);
}

int calcChecksum(char *str)
{
   int chksum = 0;
   for (int i=0; i< len(str); i++) chksum = chksum ^str[i];
   return chksum;
}

robtillaart: have a look at this (not tested)

That looks great, with only one caveat: the protocol seems to expect the checksum to be sent in the form of an one-byte value rather than as a decimal ascii string. That seems pretty strange since the checksum is not guaranteed to be a printable character, but that's how it was described. So I think the checksum calculation needs to return a byte (not an int) and the output code needs to do a serial write (not a print).

That looks great, with only one caveat

That is left in the code as an exercise for the OP ;)

Thanks for all the responses. Below is what I cam up with after changing my way of thinking initially and it’s pretty close to what robtillaart proposed. As many stated representing the checksum as Ascii seemed strange, but that is what I was told initially. when I sent the sample output back to the radio manufacturer they acknowledged that the checksum should be in hex. Below is where I have the code right now (changed the voltage math per recommendation, feel stupid for not moving the decimal in the first place).

Still have some cleanup to do and have to add serial communication lines, but I’m hoping that will be pretty simple. Again thank to everyone who made suggestions.

Shane

/*
  Hall_Effect_Read
  Reads an analog input on pin A5, converts it to voltage, multiplies by 100 to remove "." which is an unsupported character by the radio.
  Calculates a checksum using XOR and appends values to the end of the string.

 */
 //clear existing setup
void setup() {
  ///init serial comms
  Serial.begin(9600);
 }

// Loop for testing, final revision will be run once per power cycle
void loop() {
  //turn pin 9 on to full power
  analogWrite(9, 255);
  //wait 5ms for sensor to settle
  delay(5);
  // read the input on analog pin A5:
  int sensorValue = analogRead(A5);
  // Convert the analog reading to a voltage and multiply by 100 to remove decimal point:
  float voltage = sensorValue * (5.0 / 10.23);
   //turn pin 9 off
  analogWrite(9, 0);
  //build array
  char voltread[10];
  sprintf (voltread, "V;RB0%03i;",(int)voltage);
  //add checksum to transmission
  byte checksum = 0;
    for(int i = 0; i < 9; i++) checksum ^= voltread[i];
  char transmission[13];
  sprintf (transmission, "%s%x;", voltread, checksum);
  Serial.println(transmission);
  //transmit message serially
  //need serial parameters still
  //delay X seconds between reads REMOVE WHEN COMPLETE
  delay(3000);
}