Calculate CRC8 checksum from Byte Array

Hey guys,

I'm trying to calculate an XOR checksum from a Byte array. It should have 20 Bytes where the 20th byte is the checksum.

The idea is to send a message to my car that will turn on the lights. Which lights depends on the message. I want to power on multiple lights, but have to combine the messages. If I don't, the second message will turn off the lights that were turned on with the first message.

As I'm doing calculations with these arrays, the checksum should of course change.
For example, I want to combine Low Beams with Fog Lights:

const byte LIGHTS_LOW [20] PROGMEM = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x02 , 0x08 , 0x00 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7B
}; 

const byte LIGHTS_FOG [20] PROGMEM = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x01 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x70
};

Only some of the bytes in the array have to be added up, so the result would be this:

const byte NEW_MESSAGE [20] PROGMEM = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x02 , 0x08 , 0x01 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7A
};

The problem is that I don't know how to calculate the new XOR checksum (0x7A).

Should I use custom code to compare bitwise? Or is there a library that can do it for me?
I've read about the crc16 library but I'm not sure if that's what I'm looking for.

Any help greatly appreciated!

array[19] = 0;
for(byte i = 0; i < 19; i++)
  array[19] ^= array[i];
}

Not including the thing being in PROGMEM.

Also, I would not store all the arrays in PROGMEM but I would try to figure out what the bit's tat change actually do. So you can build you message fully dynamic.

Hi septillion,

thanks for your help.

Also, I would not store all the arrays in PROGMEM but I would try to figure out what the bit's tat change actually do. So you can build you message fully dynamic.

I was indeed planning on doing this after the basics work to save some memory.

With your suggestion, I assume my code should look something like this?

NEW_CAN_MESSAGE is the newly calculated message.

  NEW_CAN_MESSAGE[19] = 0;
  for (byte i = 0; i < 19; i++) {
    NEW_CAN_MESSAGE[19] ^= NEW_CAN_MESSAGE[i];
  }

The ^= is the equilevant of an XOR operation?

Bitwise operators ( &, |, ^, ~, <<, >> )
Bitwise operators modify variables considering the bit patterns that represent the values they store.

operator asm equivalent description
& AND Bitwise AND
| OR Bitwise inclusive OR
^ XOR Bitwise exclusive OR
~ NOT Unary complement (bit inversion)
<< SHL Shift bits left

SHR Shift bits right

I'll check to see if this works as soon as I get home.
Many thanks for your help!

Do you want a CRC8 checksum or just a simple XOR checksum? They are not even similar.

jremington:
Do you want a CRC8 checksum or just a simple XOR checksum? They are not even similar.

According to the cars documentation it's a simple XOR.
For example this website will give the correct checksum. The first one called CheckSum8 Xor

3F 12 D0 0C 00 00 FF FF 62 3E 00 80 00 80 80 00 00 00 00 2D (Enter without 2D)

LuckHermsen:
With your suggestion, I assume my code should look something like this?

Yeah, but 'NEW_CAN_MESSAGE' is a very screamy name. And doubt there is anything new about it :wink: Aka, I would make a (non PROGMEM) array called canMessageBytes'. Plural to remind me it's an array.

And in real code I would probably make it flexible. Aka, not hard code the 19 and split it into functions.

Something like:

void setup() {
  
}

void loop() {
  byte canMessageBytes[20];
  appendCRC(canMessageBytes, sizeof(canMessageBytes));
}

//Function to calculate CRC over an array 'ar' up to element 's'
byte calculateCRC(byte ar[], byte s){
  byte rtn = 0;;
  for(byte i =0; i < s; i++){
    rtn ^= ar[i]; 
  }
  return rtn;
}

//append CRC in last element or array 'ar' with size 's'
void appendCRC(byte ar[], byte s){
  ar[s - 1] = calculateCRC(ar, s - 2);
}

LuckHermsen:
The ^= is the equilevant of an XOR operation?

^ IS (like you posted) the XOR operator. ^= is the compound assignment operator aka

a ^= b;
//is equal to
a = a ^ b;

Hey Guys,

The code is running great now! I can still improve the code here and there, per your suggestion. After that I can implement it in my project.

Many thanks for the help, I greatly appreciate it!
Really love the Arduino Forums.

Cheers!

Hey guys,

I'm left with another problem and have a feeling you can help me out.

Here's the thing.
Usually (up till now at least) all canbus message I would send to the car were pre defined in ibusGlobals.h
All would be const byte [xx] = {xxxx, xxxx, etc};

Now when I create a new array (not const of course) and calculate the checksum afterwards, I want to send the new message to the car.
When printing the new array after the calculation and checksum have been done, everything will show up as expected.
If I manually send the calculated array to the car with a different program, the correct lights will turn on. So the calculation and checksum are absolutely fine.

However, if I call the function to send the newly created array to the car, random characters will be sent.
The array that will show up is even longer!

I'm guessing this is a memory related issue...

I've tried defining the array as a global, because at first I defined it inside the function that does the calculation. But that didn't solve the problem.

I also tried defining the array next to the other const byte arrays, but then the compiler will throw an error:

sketch\IbusSerial.cpp.o (symbol from plugin): In function `messageBuffer':

(.text+0x0): multiple definition of `DYNAMIC_CAN_MESSAGE_BYTES'

sketch\1.12.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Fout bij het compileren voor board Arduino Nano

Here's a screenshot from my Serial output:
http://gofile.me/6B0FO/kljUp4rkX

Is this where I get into stuff like pointers and such?
Hoping you can help me out!

Post the problematic code (all of it), using code tags.

okay, here we go.

// This is the code which calculates the new array, depending on which lights should be turned on

void do_Welcome_Step_2() {


  bool standing = true;
  bool fog = true;


  //Create new Canbus Message Array
  byte DYNAMIC_CAN_MESSAGE_BYTES [20];


  // Print LIGHTS_OFF to show default
  curr_Time();
  debugSerial.println(F("LIGHTS OFF (BASE):"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();





  // Overwrite old DYNAMIC_CAN_MESSAGE_BYTES values stored in memory
  curr_Time();
  debugSerial.println(F("Overwriting old DYNAMIC_CAN_MESSAGE_BYTES values stored in memory"));
  for (byte i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++) {
    DYNAMIC_CAN_MESSAGE_BYTES[i] = 0;
  }




  // Print DYNAMIC_CAN_MESSAGE_BYTES after reset to 0000
  curr_Time();
  debugSerial.println(F("DYNAMIC_CAN_MESSAGE_BYTES (should be all 00):"));

  curr_Time();
  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)
  {
    debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_OFF
  curr_Time();
  debugSerial.println(F("LIGHTS_OFF:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_STANDING
  curr_Time();
  debugSerial.println(F("LIGHTS_STANDING:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_STANDING); i++)
  {
    debugSerial.print(LIGHTS_STANDING[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_FOG
  curr_Time();
  debugSerial.println(F("LIGHTS_FOG:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_FOG); i++)
  {
    debugSerial.print(LIGHTS_FOG[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();





  // Calculate DYNAMIC_CAN_MESSAGE_BYTES
  curr_Time();
  debugSerial.println(F("For loop setting DYNAMIC_CAN_MESSAGE_BYTES:"));

  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)   {

    curr_Time();
    debugSerial.print(i);
    debugSerial.print(": ");


    // First 4 bytes are always the same. Just copy from LIGHTS_OFF
    if (i < 4) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    // Byte 4 and 5 have to be added
    if (i == 4 || i == 5) {
      if (standing) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_STANDING[i];
      }
      if (fog) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_FOG[i];
      }

      debugSerial.print(LIGHTS_OFF[i], HEX);
      debugSerial.print("+");
      debugSerial.print(LIGHTS_STANDING[i], HEX);
      debugSerial.print("=");
      debugSerial.println(LIGHTS_OFF[i] + LIGHTS_STANDING[i], HEX);
    }


    // Bytes 6 is always the same. Just copy from LIGHTS_OFF
    if (i == 6) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    if (i == 7) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i]; // This one has to be changed. It should also calculate, but these default values are FF... instead of 00. Will do this later.
      debugSerial.print("Copy Base: ");
      debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
      debugSerial.println(" (FOR NOW...)");
    }


    // Byte 8 - 10 have to be added
    if (i >= 8 && i <= 10) {

      if (standing) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_STANDING[i];
      }
      if (fog) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_FOG[i];
      }

      debugSerial.print(LIGHTS_OFF[i], HEX);
      debugSerial.print("+");
      debugSerial.print(LIGHTS_STANDING[i], HEX);
      debugSerial.print("=");
      debugSerial.println(LIGHTS_OFF[i] + LIGHTS_STANDING[i], HEX);
    }


    // Last 8 bytes are always the same. Just copy from LIGHTS_OFF
    if (i >= 11 && i <= 18) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    // Calculate and Add Checksum to DYNAMIC_CAN_MESSAGE_BYTES
    if (i == 19) {
      debugSerial.print("Checksum: ");
      for (byte i = 0; i < 19; i++) {
        DYNAMIC_CAN_MESSAGE_BYTES[19] ^= DYNAMIC_CAN_MESSAGE_BYTES[i];
      }
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[19], HEX);
    }

  }



  // Print LIGHTS_OFF
  curr_Time();
  debugSerial.println(F("LIGHTS_OFF:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print LIGHTS_STANDING
  curr_Time();
  debugSerial.println(F("LIGHTS_STANDING:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_STANDING); i++)
  {
    debugSerial.print(LIGHTS_STANDING[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print LIGHTS_FOG
  curr_Time();
  debugSerial.println(F("LIGHTS_FOG:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_FOG); i++)
  {
    debugSerial.print(LIGHTS_FOG[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print DYNAMIC_CAN_MESSAGE_BYTES
  curr_Time();
  debugSerial.println("DYNAMIC_CAN_MESSAGE_BYTES:");
  curr_Time();
  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)
  {
    debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // SEND DYNAMIC_CAN_MESSAGE_BYTES over BUS
  curr_Time();
  debugSerial.println(F("SENDING: DYNAMIC_CAN_MESSAGE_BYTES"));
  ibus.write(DYNAMIC_CAN_MESSAGE_BYTES, sizeof(DYNAMIC_CAN_MESSAGE_BYTES));


}

// This is the ibus.write function

void IbusSerial::write(const byte message[], byte size)
{
  ibusSendBuffer.write(size);
  for (unsigned int i = 0; i < size; i++)
  {
    ibusSendBuffer.write(pgm_read_byte(&message[i]));
  }
}

Oh and here are the const arrays

// LIGHTS //
const byte LIGHTS_OFF [20]  = { //BASE
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x71
};

const byte LIGHTS_STANDING [20]  = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x02 , 0x08 , 0x00 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7B
};
const byte LIGHTS_FOG [20]  = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x01 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x70
};

What part of "all of it" do you not understand?

The error message tells that you are defining a variable named DYNAMIC_CAN_MESSAGE_BYTES more than once. Don't do that.

Data with all-upper-case names are traditionally definitions of constants (usually instructions to the compiler, not part of the actual code).

jremington:
What part of "all of it" do you not understand?

The error message tells that you are defining a variable named DYNAMIC_CAN_MESSAGE_BYTES more than once. Don't do that.

I'm not defining that twice.
And sorry, it's quite a lot of code, didn't want to spam the whole topic.
I think this is all the code that matters, let me know if you're missing anything.

ibusGlobals.h - This is where the 'regular' ibus messages are stored

#ifndef IbusGlobals_h
#define IbusGlobals_h
#include <Arduino.h>

// Comment line to disable debug messages
#define IBUS_DEBUG
#define SEND_DEBUG
#define DISCARDED_DEBUG

// These tell the library which interface you're using. Only one can be selected.
#define MELEXIS
//#define MCP

extern const byte senSta; // sen/Sta output from Melexix TH3122.
extern const byte enable; // HIGH enables IBUS trasceiver chip.
extern volatile boolean clearToSend;
//extern const byte ledPin;
extern unsigned long ibusLoopTime;
extern unsigned long packetTimer;


//////////
//OUTPUT//
//////////


// LIGHTS //
const byte LIGHTS_OFF [20]  = { //BASE
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x71
};

const byte LIGHTS_STANDING [20]  = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x02 , 0x08 , 0x00 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7B
};
const byte LIGHTS_FOG [20]  = {
  0x3F , 0x12 , 0xD0 , 0x0C , 0x00 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x01 , 0x80 , 0x00 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x70
};

//etc etc etc...


#endif

Then the Welcome Lights function is called:

void do_Welcome_Step_2() {

  bool standing = true;
  bool fog = true;

  //Create new Canbus Message Array
  byte DYNAMIC_CAN_MESSAGE_BYTES [20];


  // Print LIGHTS_OFF to show default
  curr_Time();
  debugSerial.println(F("LIGHTS OFF (BASE):"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();





  // Overwrite old DYNAMIC_CAN_MESSAGE_BYTES values stored in memory
  curr_Time();
  debugSerial.println(F("Overwriting old DYNAMIC_CAN_MESSAGE_BYTES values stored in memory"));
  for (byte i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++) {
    DYNAMIC_CAN_MESSAGE_BYTES[i] = 0;
  }




  // Print DYNAMIC_CAN_MESSAGE_BYTES after reset to 0000
  curr_Time();
  debugSerial.println(F("DYNAMIC_CAN_MESSAGE_BYTES (should be all 00):"));

  curr_Time();
  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)
  {
    debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_OFF
  curr_Time();
  debugSerial.println(F("LIGHTS_OFF:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_STANDING
  curr_Time();
  debugSerial.println(F("LIGHTS_STANDING:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_STANDING); i++)
  {
    debugSerial.print(LIGHTS_STANDING[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // Print LIGHTS_FOG
  curr_Time();
  debugSerial.println(F("LIGHTS_FOG:"));

  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_FOG); i++)
  {
    debugSerial.print(LIGHTS_FOG[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();





  // Calculate DYNAMIC_CAN_MESSAGE_BYTES
  curr_Time();
  debugSerial.println(F("For loop setting DYNAMIC_CAN_MESSAGE_BYTES:"));

  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)   {

    curr_Time();
    debugSerial.print(i);
    debugSerial.print(": ");


    // First 4 bytes are always the same. Just copy from LIGHTS_OFF
    if (i < 4) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    // Byte 4 and 5 have to be added
    if (i == 4 || i == 5) {
      if (standing) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_STANDING[i];
      }
      if (fog) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_FOG[i];
      }

      debugSerial.print(LIGHTS_OFF[i], HEX);
      debugSerial.print("+");
      debugSerial.print(LIGHTS_STANDING[i], HEX);
      debugSerial.print("=");
      debugSerial.println(LIGHTS_OFF[i] + LIGHTS_STANDING[i], HEX);
    }


    // Bytes 6 is always the same. Just copy from LIGHTS_OFF
    if (i == 6) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    if (i == 7) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i]; // This one has to be changed. It should also calculate, but these default values are FF... instead of 00
      debugSerial.print("Copy Base: ");
      debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
      debugSerial.println(" (FOR NOW...)");
    }


    // Byte 8 - 10 have to be added
    if (i >= 8 && i <= 10) {

      if (standing) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_STANDING[i];
      }
      if (fog) {
        DYNAMIC_CAN_MESSAGE_BYTES[i] += LIGHTS_FOG[i];
      }

      debugSerial.print(LIGHTS_OFF[i], HEX);
      debugSerial.print("+");
      debugSerial.print(LIGHTS_STANDING[i], HEX);
      debugSerial.print("=");
      debugSerial.println(LIGHTS_OFF[i] + LIGHTS_STANDING[i], HEX);
    }


    // Last 8 bytes are always the same. Just copy from LIGHTS_OFF
    if (i >= 11 && i <= 18) {
      DYNAMIC_CAN_MESSAGE_BYTES[i] = LIGHTS_OFF[i];
      debugSerial.print("Copy Base: ");
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    }


    // Calculate and Add Checksum to DYNAMIC_CAN_MESSAGE_BYTES
    if (i == 19) {
      debugSerial.print("Checksum: ");
      for (byte i = 0; i < 19; i++) {
        DYNAMIC_CAN_MESSAGE_BYTES[19] ^= DYNAMIC_CAN_MESSAGE_BYTES[i];
      }
      debugSerial.println(DYNAMIC_CAN_MESSAGE_BYTES[19], HEX);
    }

  }



  // Print LIGHTS_OFF
  curr_Time();
  debugSerial.println(F("LIGHTS_OFF:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_OFF); i++)
  {
    debugSerial.print(LIGHTS_OFF[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print LIGHTS_STANDING
  curr_Time();
  debugSerial.println(F("LIGHTS_STANDING:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_STANDING); i++)
  {
    debugSerial.print(LIGHTS_STANDING[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print LIGHTS_FOG
  curr_Time();
  debugSerial.println(F("LIGHTS_FOG:"));
  curr_Time();
  for (int i = 0; i < sizeof(LIGHTS_FOG); i++)
  {
    debugSerial.print(LIGHTS_FOG[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  // Print DYNAMIC_CAN_MESSAGE_BYTES
  curr_Time();
  debugSerial.println("DYNAMIC_CAN_MESSAGE_BYTES:");
  curr_Time();
  for (int i = 0; i < sizeof(DYNAMIC_CAN_MESSAGE_BYTES); i++)
  {
    debugSerial.print(DYNAMIC_CAN_MESSAGE_BYTES[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();



  // SEND DYNAMIC_CAN_MESSAGE_BYTES over BUS
  curr_Time();
  debugSerial.println(F("SENDING: DYNAMIC_CAN_MESSAGE_BYTES"));
  ibus.write(DYNAMIC_CAN_MESSAGE_BYTES, sizeof(DYNAMIC_CAN_MESSAGE_BYTES));


}

The screenshot I provided earlier shows the difference between the print of DYNAMIC_CAN_MESSAGE_BYTES and the actual message being sent with ibus.write(DYNAMIC_CAN_MESSAGE_BYTES, sizeof(DYNAMIC_CAN_MESSAGE_BYTES));

ibus.write is a function in a library I use.

will post remaining code in 5min (time restriction)

I'm not defining that twice.

If you disagree with the compiler, the compiler wins. Always.

Here's the libary:

ibusSerial.h

#ifndef IbusSerial_h
#define IbusSerial_h

#include "Arduino.h"
#include "IbusGlobals.h"
#include "RingBuffer.h"


#define TIE 0x2
#define TEN 0x1
#if defined(__AVR_ATmega2560__)
#define Rx 15
#define Tx 14
#elif defined(__AVR_ATmega328P__) || defined(__MK20DX256__)
#define Rx 0
#define Tx 1
#endif

class IbusSerial
{

  public:

    IbusSerial();
    ~IbusSerial();
    void setIbusSerial(HardwareSerial &newIbusSerial);
#ifdef IBUS_DEBUG
    void setIbusDebug(Stream &newIbusDebug);
#endif
    void run();
    void write(const byte message[], byte size);
    void printDebugMessage(const char * debugPrefix);
    typedef void IbusPacketHandler_t(byte *packet);
    void setIbusPacketHandler(IbusPacketHandler_t newHandler);
    void contentionTimer();
    void startTimer();
    void sleepEnable(unsigned long sleepTime);
    void sleep();
    byte transceiverType;
    boolean sleepEnabled;
    boolean debugEnabled;
    HardwareSerial *pSerial;
    void setScrollSpeed(int, int, int, byte);
    void radioWrite(const char *);
    void displayCurrentMessage();
 

  private:

    enum ScrollStates
    {
      SCROLL_IDLE,
      SCROLL_START,
      SCROLL_START_DELAY,
      SCROLL_SCROLLING,
      SCROLL_SCROLLING_DELAY,
      SCROLL_END,
      SCROLL_END_DELAY,
      SCROLL_END_DISPLAY
    }
    scrollState;

    enum IbusFsmStates
    {
      FIND_SOURCE,              // Read source byte
      FIND_LENGTH,              // Read length byte
      FIND_MESSAGE,             // Read in main body of message
      GOOD_CHECKSUM,            // Process message if checksum good
      BAD_CHECKSUM,             // Debug print bad message if checksum bad
    }
    ibusState;

    void readIbus();
    void compareIbusPacket();
    void sendIbusPacket();
    void sendIbusMessageIfAvailable();

#ifdef IBUS_DEBUG
    Stream *pIbusDebug;
#endif
    byte source;
    byte length;
    byte destination;
    byte ibusByte[40];
    boolean busInSync;
    static const byte packetGap = 10;
    byte *pData;
    IbusPacketHandler_t *pIbusPacketHandler;

    void scroll();
    void buildStaticMessage(const char *);
    void buildScrollMessage(byte);
    void sendMessageIfAvailable();
    void writeMessage();
    unsigned int startDelay; // Delay before scrolling starts
    unsigned int endDelay; // Delay before scrolling re-starts
    unsigned int scrollSpeed; // Time in milliseconds between scroll steps
    unsigned int loops; // Number of scroll loops to display complete message
    unsigned long scrollTimer;
    byte maxChars; // Max number of characters to display on headunit before scrolling is required
    byte maxScrollRepeats; // No of scroll repeats
    byte scrollRepeatCounter; // Used to remember how many scroll repates we have done
    byte loopCounter; // Keep track of how many loops we have done
    byte startPos; // Index position into scrollStrng
    char scrollStrng[64]; // Used to hold messages to be scrolled on radio display
    static byte IBUS_RADIO_MESSAGE[32];


};
#endif

ibusSerial.cpp

#include "IbusSerial.h"

#define len 1
const byte senSta = 3; // Pin 3 on Teensy 3.1 - sen/Sta output from Melexix TH3122.
const byte enable = 4; // HIGH enables Melexix TH3122 chip
volatile boolean clearToSend = false;
unsigned long ibusTimeToSleep;
unsigned long ibusSleepTime = millis();
unsigned long packetTimer = millis();
//const byte ledPin = 11;
RingBuffer ibusReceiveBuffer(128);
RingBuffer ibusSendBuffer(64);
RingBuffer messageBuffer(64);
byte IbusSerial::IBUS_RADIO_MESSAGE[32] = { 0xC8 , 0x00 , 0x80 , 0x23 , 0x42 , 0x32 };


IbusSerial::IbusSerial() // constructor
{
  busInSync = false;
  ibusSleepTime = millis();
  packetTimer = millis();
  source = 0;
  length = 0;
  destination = 0;
  ibusState = FIND_SOURCE;
  scrollState = SCROLL_IDLE;
#if defined(MCP)
  pinMode(Rx, INPUT_PULLUP);
#endif
  pinMode (enable, OUTPUT);
  digitalWrite (enable, HIGH); // enable MCP/Melexis transceiver
  contentionTimer();
  sleepEnabled = false;
  debugEnabled = false;
  startDelay = 2000; // set default value
  endDelay = 1000; // set default value
  scrollSpeed = 200; // set default value
  maxScrollRepeats = 2; // set default value
  loops = 0;
  maxChars = 11;
  scrollRepeatCounter = 0;
  loopCounter = 0;
  startPos = 0;
  scrollTimer = millis();
}

//deconstructor - not really used, as we never destroy this instance
IbusSerial::~IbusSerial()
{

}



void IbusSerial::setIbusSerial(HardwareSerial &newIbusSerial)
{
  pSerial = &newIbusSerial;
  pSerial->begin(9600, SERIAL_8E1); // ibus always 9600 8E1
}

#ifdef IBUS_DEBUG
void IbusSerial::setIbusDebug(Stream &newIbusDebug)
{
  pIbusDebug = &newIbusDebug;
}
#endif

void IbusSerial::setIbusPacketHandler(IbusPacketHandler_t newHandler)
{
  pIbusPacketHandler = newHandler;
}

void IbusSerial::readIbus()
{
  if (pSerial->available() )
  {
    ibusReceiveBuffer.write(pSerial->read() );
  }
  switch (ibusState)
  {
    case FIND_SOURCE:
      if (ibusReceiveBuffer.available() >= 1)
      {
        source = ibusReceiveBuffer.peek(0);
        ibusState = FIND_LENGTH;
      }
      break;

    case FIND_LENGTH:
      if (ibusReceiveBuffer.available() >= 2)
      {
        length = ibusReceiveBuffer.peek(1);
        if (length >= 0x03 && length <= 0x24) { // Check if length byte between decimal 3 & 36
          ibusState = FIND_MESSAGE;
        }
        else {
          // remove source byte and start over
          ibusReceiveBuffer.remove(1);
        }
      }
      break;

    case FIND_MESSAGE:
      if (ibusReceiveBuffer.available() >= length + 2) // Check if enough bytes in buffer to complete message (based on length byte)
      {
        byte checksumByte = 0;
        for (int i = 0; i <= length; i++)
        {
          checksumByte ^= ibusReceiveBuffer.peek(i);
        }
        if (ibusReceiveBuffer.peek(length + 1) == checksumByte)
        {
          ibusSleepTime = millis();  // Restart sleep timer
          ibusState = GOOD_CHECKSUM;
        }
        else
        {
          ibusState = BAD_CHECKSUM;
        }
      }
      break;

    case GOOD_CHECKSUM:
      // only process messages we're interested in
      if (source != 0x50      //MFL
          && source != 0x3F  //DIA
          && source != 0xD0  //LCM
          && source != 0x00   //GM5
          && source != 0x9C   //CVM
          && source != 0x80   //IKE
          && source != 0x9B   // Mirror module
         )
      {
        // remove unwanted message from buffer and start over
#ifdef DISCARDED_DEBUG
        pIbusDebug->print(F("BAD:  "));
        for (int i = 0; i <= length + 1; i++)
        {
          if ( ibusReceiveBuffer.peek(i) < 0x10)
          {
            pIbusDebug->print(F("0"));
          }
          pIbusDebug->print(ibusReceiveBuffer.peek(i), HEX);
          pIbusDebug->print(F(" "));
        }
        pIbusDebug->println();
#endif
        ibusReceiveBuffer.remove(length + 2);
        ibusState = FIND_SOURCE;
        return;
      }
      // read message from buffer
      for (int i = 0; i <= length + 1; i++)
      {
        ibusByte[i] = ibusReceiveBuffer.read();
      }
#ifdef IBUS_DEBUG
      // debug print good message
      printDebugMessage("GOOD: ");
#endif
      busInSync = true;
      compareIbusPacket();
      ibusState = FIND_SOURCE;
      break;

    case BAD_CHECKSUM:
#ifdef IBUS_DEBUG
      // debug print bad message
      printDebugMessage("Message Bad -> ");
#endif
      // remove first byte and start over
      ibusReceiveBuffer.remove(1);
      ibusState = FIND_SOURCE;
      break;

  } // end of switch
} // end of readIbus

#ifdef IBUS_DEBUG
void IbusSerial::printDebugMessage(const char * debugPrefix)
{
  pIbusDebug->print(debugPrefix);
  for (int i = 0; i <= length + 1; i++)
  {
    if ( ibusByte[i] < 0x10)
    {
      pIbusDebug->print(F("0"));
    }
    pIbusDebug->print(ibusByte[i], HEX);
    pIbusDebug->print(F(" "));
  }
  pIbusDebug->println();
}
#endif

void IbusSerial::compareIbusPacket()
{
  //pIbusDebug->print(F("IbusSerial::compareIbusPacket"));
  byte *pData = &ibusByte[0];
  pIbusPacketHandler((byte *) pData);
}

void IbusSerial::write(const byte message[], byte size)
{
  ibusSendBuffer.write(size);
  for (unsigned int i = 0; i < size; i++)
  {
    ibusSendBuffer.write(pgm_read_byte(&message[i]));
  }
}

void IbusSerial::sendIbusMessageIfAvailable()
{
  if (clearToSend && ibusSendBuffer.available() > 0 )
  {
    if (millis() - packetTimer >= packetGap)
    {
      sendIbusPacket();
      packetTimer = millis();
    }
  }
}
void IbusSerial::sendIbusPacket()
{
int Length = ibusSendBuffer.read();
#if defined(__MK20DX256__) && defined(MELEXIS)
if (digitalReadFast(senSta) == LOW && Length <= 32)
#elif defined(__MK20DX256__) && defined(MCP)
if (digitalReadFast(Rx) == HIGH && Length <= 32)
#elif (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)) && defined(MELEXIS)
if (digitalRead(senSta) == LOW && Length <= 32)
#elif (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)) && defined(MCP)
if (digitalRead(Rx) == HIGH && Length <= 32)
#endif
{
#ifdef SEND_DEBUG
  pIbusDebug->print(F("TRANSMIT: "));
#endif
  for ( int i = 0; i < Length; i++)
  {
#ifdef SEND_DEBUG
    if ( ibusSendBuffer.peek(0) < 0x10)
    {
      pIbusDebug->print(F("0"));
    }
    pIbusDebug->print(ibusSendBuffer.peek(0), HEX);
    pIbusDebug->print(F(" "));
#endif
    pSerial->write(ibusSendBuffer.read()); // write byte to IBUS.
  }
#ifdef SEND_DEBUG
  pIbusDebug->println();
#endif
}
else
{
  ibusSendBuffer.remove(Length);
  return;
}
}

void IbusSerial::sleepEnable(unsigned long sleepTime)
{
ibusTimeToSleep = sleepTime * 1000;
sleepEnabled = true;
}

void IbusSerial::sleep()
{
if (sleepEnabled == true)
{
  if (millis() - ibusSleepTime >= ibusTimeToSleep)
  {
#if defined(MELEXIS)
    digitalWrite (enable, LOW); // Shutdown TH3122
#elif defined(MCP)
    digitalWrite (enable, LOW); // Shutdown MCP
    pSerial->write (0x00); // write Tx pin low - only required on MCP2025.
#endif
  }
}
}

void IbusSerial::run()
{
//pIbusDebug->print(F("run()"));
scroll();
sendMessageIfAvailable();
readIbus();
sendIbusMessageIfAvailable();
sleep();
}

// Tennsy 3.1 with Melexis TH3122
#if defined(__MK20DX256__)&& defined(MELEXIS)
void IbusSerial::contentionTimer()
{
pinMode(senSta, INPUT);
SIM_SCGC6 |= SIM_SCGC6_PIT;    // Enables/disables clock used by PIT timers
PIT_MCR = 0x00;                // Enables and disables the PIT timers. Writing zero enables the timers and writing 1 disables them.
NVIC_ENABLE_IRQ(IRQ_PIT_CH0);  //
PIT_LDVAL0 = 0x1193F;          // Set timer period for 1.5ms
//pIbusDebug->println(F("Teensy/Melexis"));
}

void IbusSerial::startTimer()
{

if (digitalReadFast(senSta) == LOW )
{
  PIT_TCTRL0 = TIE;             // enable Timer 0 interrupts
  PIT_TCTRL0 |= TEN;            // enable Timer 0
  PIT_TFLG0 |= 1;               // clear Timer Interrupt flag
}

else if (digitalReadFast(senSta) == HIGH )
{
  clearToSend = false;
  PIT_TCTRL0 &= ~TEN;           // disable Timer 0
  PIT_TCTRL0 &= ~TIE;           // disable Timer 0 interrupts
  PIT_TFLG0 = 1;                // clear Timer Interrupt flag
}
}

void pit0_isr(void)
{
clearToSend = true;
PIT_TFLG0 = 1;                  // clear Timer Interrupt flag
PIT_TCTRL0 &= ~TEN;             // disable Timer 0
PIT_TCTRL0 &= ~TIE;             // disable Timer 0 interrupts
}
#endif

// Tennsy 3.1 with Microchip MCP2003/2004/2025
#if defined(__MK20DX256__)&& defined(MCP)
void IbusSerial::contentionTimer()
{
SIM_SCGC6 |= SIM_SCGC6_PIT;    // Enables/disables clock used by PIT timers
PIT_MCR = 0x00;                // Enables and disables the PIT timers. Writing zero enables the timers and writing 1 disables them.
NVIC_ENABLE_IRQ(IRQ_PIT_CH0);  //
PIT_LDVAL0 = 0x1193F;          // Set timer period for 1.5ms
//pIbusDebug->println(F("Teensy/MCP"));
}

void IbusSerial::startTimer()
{

if (digitalReadFast(Rx) == HIGH && digitalReadFast(Tx) == HIGH) // check Rx high and Tx high
{ // start/enable timer
  PIT_TCTRL0 = TIE;             // enable Timer 0 interrupts
  PIT_TCTRL0 |= TEN;            // enable Timer 0
  PIT_TFLG0 |= 1;               // clear Timer Interrupt flag
}

else if (digitalReadFast(Rx) == LOW && digitalReadFast(Tx) == HIGH) // check Rx high and Tx low
{ // stop/disable timer
  clearToSend = false;
  PIT_TCTRL0 &= ~TEN;           // disable Timer 0
  PIT_TCTRL0 &= ~TIE;           // disable Timer 0 interrupts
  PIT_TFLG0 = 1;                // clear Timer Interrupt flag
}
}

void pit0_isr(void)
{
clearToSend = true;
PIT_TFLG0 = 1;                  // clear Timer Interrupt flag
PIT_TCTRL0 &= ~TEN;             // disable Timer 0
PIT_TCTRL0 &= ~TIE;             // disable Timer 0 interrupts
}
#endif


// Mega or Atmega328P based Arduino with Melexis TH3122.
#if (defined (__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)) && defined(MELEXIS)

void IbusSerial::contentionTimer() {
#define START_TIMER2 TCCR2B |= (1 << CS22)|(1 << CS21) // //Set CS22 and CS21 bits for 256 prescaler
#define STOP_TIMER2  TCCR2B &= 0B11111000

// initialize Timer1
TCCR2A = 0;     // set entire TCCR2A register to 0
TCCR2B = 0;     // same for TCCR2B

// set compare match register to desired timer count:
OCR2A = 94; // Set timer to fire CTC interrupt after approx 1.5ms

// turn on CTC mode:
TCCR2B |= (1 << WGM21);

// enable timer compare interrupt:
TIMSK2 |= (1 << OCIE2A);
pinMode(senSta, INPUT);
//pIbusDebug->println(F("Arduino / Melexis"));

}

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

void IbusSerial::startTimer() {

if (digitalRead(senSta) == LOW)
{
  START_TIMER2;
}
else if (digitalRead(senSta) == HIGH )
{
  clearToSend = false;
  //digitalWrite(ledPin, HIGH);
  STOP_TIMER2;
}
}

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


ISR(TIMER2_COMPA_vect) { // this function runs when the Timer2A compare expires

clearToSend = true;
//digitalWrite(ledPin, LOW);
STOP_TIMER2;

}
#endif

// Arduino Mega with Microchip MCP2003/2004/2025
#if defined(__AVR_ATmega2560__) && defined(MCP)
void IbusSerial::contentionTimer() {

#define START_TIMER2 TCCR2B |= (1 << CS22)|(1 << CS21) | (1 << CS20) // //Set CS22, CS21 & CS20 bits for 1024 prescaler
#define STOP_TIMER2  TCCR2B &= 0B11111000

// initialize Timer1
TCCR2A = 0;     // set entire TCCR2A register to 0
TCCR2B = 0;     // same for TCCR2B

// set compare match register to desired timer count:
OCR2A = 157; // Set timer to fire CTC interrupt after approx 10ms

// turn on CTC mode:
TCCR2B |= (1 << WGM21);

// enable timer compare interrupt:
TIMSK2 |= (1 << OCIE2A);

// enable pin change interrupt on pin 15 (Rx3)
PCICR  |= (1 << PCIE1);
PCMSK1 |= (1 << PCINT9); // Mega digital pin 15

//pIbusDebug->println(F("Mega / MCP"));
}

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

ISR (PCINT1_vect)
//void IbusSerial::startTimer()
{
if (digitalRead(Rx) == HIGH && digitalRead(Tx) == HIGH) // check Rx high and Tx high
{
  START_TIMER2;
}
else if (digitalRead(Rx) == LOW && digitalRead(Tx) == HIGH) // check Rx high and Tx low
{
  clearToSend = false;
  //digitalWrite(ledPin, HIGH);
  STOP_TIMER2;
}
}

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

ISR(TIMER2_COMPA_vect)
{
clearToSend = true;
//digitalWrite(ledPin, LOW);
STOP_TIMER2;
}
#endif

// Atmega328P based Arduino with Microchip MCP2003/2004/2025
#if defined(__AVR_ATmega328P__) && defined(MCP)
void IbusSerial::contentionTimer() {
#define START_TIMER2 TCCR2B |= (1 << CS22)|(1 << CS21) | (1 << CS20) // //Set CS22, CS21 & CS20 bits for 1024 prescaler
#define STOP_TIMER2  TCCR2B &= 0B11111000

// initialize Timer1
TCCR2A = 0;     // set entire TCCR2A register to 0
TCCR2B = 0;     // same for TCCR2B

// set compare match register to desired timer count:
OCR2A = 157; // Set timer to fire CTC interrupt after approx 10ms

// turn on CTC mode:
TCCR2B |= (1 << WGM21);

// enable timer compare interrupt:
TIMSK2 |= (1 << OCIE2A);

// enable pin change interrupt on pin 0 (Rx)
PCICR  |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT16);

//pIbusDebug->println(F("Uno, Nano or Pro Mini / MCP"));
}

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

ISR (PCINT2_vect)
{
if (digitalRead(Rx) == HIGH && digitalRead(Tx) == HIGH) // check Rx high and Tx high
{
  START_TIMER2;
}
else if (digitalRead(Rx) == LOW && digitalRead(Tx) == HIGH) // check Rx high and Tx low
{
  clearToSend = false;
  //digitalWrite(ledPin, HIGH);
  STOP_TIMER2;
}
}

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

ISR(TIMER2_COMPA_vect)
{
clearToSend = true;
//digitalWrite(ledPin, LOW);
STOP_TIMER2;
}
#endif
void IbusSerial::setScrollSpeed(int start, int end, int speed, byte repeats)
{
  startDelay = start;
  endDelay = end;
  scrollSpeed = speed;
  maxScrollRepeats = repeats;
}

//scroll state machine
void IbusSerial::scroll()
{
  switch (scrollState)
  {
    case SCROLL_IDLE: //0
      scrollRepeatCounter = 0;
      break;

    case SCROLL_START:  //1
      startPos = 0;
      loopCounter = 0;
      buildScrollMessage(startPos); // Build message and send it
      scrollState = SCROLL_START_DELAY;
      scrollTimer = millis();
      break;

    case SCROLL_START_DELAY: //2
      if (millis() - scrollTimer >= startDelay)
      {
        scrollState = SCROLL_SCROLLING;
      }
      break;

    case SCROLL_SCROLLING: //3
      if (loopCounter < loops)
      {
        loopCounter++;
        buildScrollMessage(startPos++); // Build next part of message and send it
        scrollState = SCROLL_SCROLLING_DELAY;
        scrollTimer = millis();
      }
      else
      {
        scrollState = SCROLL_END;
        startPos = 0;
        loopCounter = 0;
        scrollRepeatCounter++;
      }
      break;

    case SCROLL_SCROLLING_DELAY: //4
      if (millis() - scrollTimer >= scrollSpeed)
      {
        scrollState = SCROLL_SCROLLING;
      }
      break;

    case SCROLL_END: //5
      if (scrollRepeatCounter < maxScrollRepeats)
      { // Repeat for maxScrollRepeats
        scrollState = SCROLL_END_DELAY;
        scrollTimer = millis();
      }
      else
      {
        scrollState = SCROLL_END_DISPLAY;
        scrollTimer = millis();
      }
      break;

    case SCROLL_END_DELAY: //6
      if (millis() - scrollTimer >= endDelay)
      {
        scrollState = SCROLL_START;
      }
      break;

    case SCROLL_END_DISPLAY: //7
      if (millis() - scrollTimer >= endDelay)
      {
        startPos = 0;
        buildScrollMessage(startPos);
        scrollState = SCROLL_IDLE;
      }
      break;
  }
}

//receives message and decides if it needs scrolling or not
void IbusSerial::radioWrite(const char * instrng)
{
  scrollState = SCROLL_IDLE;
  byte messageLen = strlen(instrng);
  if (messageLen <= maxChars) { //static message
    buildStaticMessage(instrng);
  }
  else { //scroll message
    sprintf(scrollStrng, instrng);
    scrollRepeatCounter = 0;
    scrollState = SCROLL_START;
  }
}

//builds static (non scrolling) messages
void IbusSerial::buildStaticMessage(const char * instrng)
{
  byte messageLen = strlen(instrng);
  memcpy(IBUS_RADIO_MESSAGE + 6, instrng, messageLen); //ibusSendBuffer.write(pgm_read_byte(&message[i]));
  IBUS_RADIO_MESSAGE[len] = messageLen + 5;
  int checksumPos = IBUS_RADIO_MESSAGE[1] + 1;
  int checksumByte = 0;
  for (unsigned int i = 0; i <= IBUS_RADIO_MESSAGE[1]; i++) {
    checksumByte ^= IBUS_RADIO_MESSAGE[i];
  }
  IBUS_RADIO_MESSAGE[checksumPos] = checksumByte;
  displayCurrentMessage();
}

//builds scrolling messages - driven by scroll state machine
void IbusSerial::buildScrollMessage(byte startPosn)
{
  byte messageLen = strlen(scrollStrng);
  loops = messageLen - (maxChars - 1);
  memcpy(IBUS_RADIO_MESSAGE + 6, scrollStrng + startPosn, maxChars);
  if (IBUS_RADIO_MESSAGE[6] == 0x20) IBUS_RADIO_MESSAGE[6] = 0x86;
  IBUS_RADIO_MESSAGE[len] = 5 + maxChars;
  int checksumPos = IBUS_RADIO_MESSAGE[1] + 1;
  int checksumByte = 0;
  for (unsigned int cs = 0; cs <= IBUS_RADIO_MESSAGE[1]; cs++) {
    checksumByte ^= IBUS_RADIO_MESSAGE[cs];
  }
  IBUS_RADIO_MESSAGE[checksumPos] = checksumByte;
  displayCurrentMessage();
}

void IbusSerial::displayCurrentMessage()
{
  messageBuffer.write(IBUS_RADIO_MESSAGE[1] + 2);
  for (unsigned int cs = 0; cs < IBUS_RADIO_MESSAGE[1] + 2; cs++)
  {
    messageBuffer.write(IBUS_RADIO_MESSAGE[cs]);
  }
}

void IbusSerial::writeMessage()
{
  unsigned int Length = messageBuffer.read();
#if defined(__MK20DX256__) && defined(MELEXIS)
  if (digitalReadFast(senSta) == LOW && Length <= 18)
#elif defined(__MK20DX256__) && defined(MCP)
  if (digitalReadFast(Rx) == HIGH && Length <= 18)
#elif (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)) && defined(MELEXIS)
  if (digitalRead(senSta) == LOW && Length <= 18)
#elif (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)) && defined(MCP)
  if (digitalRead(Rx) == HIGH && Length <= 18)
#endif
  {
    for (unsigned int i = 0; i < Length; i++)
    {
      pSerial->write(messageBuffer.read()); // write byte to IBUS.
    }
  }
  else
  {
    messageBuffer.remove(Length);
    return;
  }
}

//send static or scroll messages if available in buffers
void IbusSerial::sendMessageIfAvailable()
{
  if (clearToSend && messageBuffer.available() > 0 )
  {
    writeMessage();
  }
}

ringBuffer.h

#ifndef RingBuffer_h
#define RingBuffer_h

#include "Arduino.h"

class RingBuffer
{
private:

 int bufferSize;
 unsigned int bufferHead, bufferTail;
 byte *pBuffer;
 
public:
 RingBuffer(int size);
        ~RingBuffer();
 int available(void);
 int peek(void);
 int peek(int);
 void remove(int);
 int read(void);
 byte write(int);
};
#endif

ringBuffer.cpp

#include "RingBuffer.h"

RingBuffer::RingBuffer( int size )
{
 bufferSize = size;
 bufferTail = 0;
 bufferHead = 0;
 pBuffer = ( byte* )malloc( size );
        memset( pBuffer, 0, size );  
}

RingBuffer::~RingBuffer()
{
 if( pBuffer )
          free( pBuffer );
}

// public functions

int RingBuffer::available(void)
{
  int ByteCount = (bufferSize + bufferHead - bufferTail) % bufferSize;
  return ByteCount;
}

int RingBuffer::read(void) 
{
  if (bufferHead == bufferTail) {
    return -1;
  }
  else {
    byte c = pBuffer[bufferTail];
    bufferTail = (bufferTail + 1) % bufferSize;
    if(bufferHead == bufferTail) {
      bufferTail = 0;
      bufferHead = 0;
    }
    return c;
  }
}

byte RingBuffer::write( int c ) 
{ 
  if ((bufferHead + 1) % bufferSize == bufferTail) {
    return -1;
  }
  pBuffer[bufferHead] = c;
  bufferHead = (bufferHead + 1) % bufferSize;
  return 0;
}

void RingBuffer::remove(int n) 
{
  if(bufferHead != bufferTail) {
    bufferTail = (bufferTail + n) % bufferSize;
  }
}

int RingBuffer::peek(void) 
{
  if (bufferHead == bufferTail) {
    return -1;
  }
  else {
    return pBuffer[bufferTail];
  }
}

int RingBuffer::peek(int n) {

  if (bufferHead == bufferTail) {
    return -1;
  }
  else {
    return pBuffer[(bufferTail + n) % bufferSize];
  }
}

This seems like the right moment to work on a MCVE. Because frankly, like a lot of others, I'm out with that much snippets. And as an added bonus, in at least 50% of the time you will find the bug while making it :wink:

The problem is that I have not written the ibus library myself, so I wont be able to shrink that part of the code much.

Also the only way to read the debug output is by connecting a ttl converter to the arduino. I guess not everyone has one of those laying around.

I'll just create a simple version of the code for myself to begin with. And if I'm lucky I will indeed find the problem while doing that.

LuckHermsen:
The problem is that I have not written the ibus library myself, so I wont be able to shrink that part of the code much.

Then just post a link to the library. Because it's an already excising library we can almost certainly exclude that from the debug process, only need to know the interface of it.

LuckHermsen:
Also the only way to read the debug output is by connecting a ttl converter to the arduino. I guess not everyone has one of those laying around.

But I hope you do? And really, the guys answering here will all have a spare Serial-USB converter :wink: But at first we'll just go over the code "dry".

Okay, so I did some more testing while making an MCVE.

I'm guessing the library is not the problem, but that's with my limited understanding of it.

Here's what I know:

  • Up until now, hex messages were stored in ibusGlobals.h as const byte [] PROGMEM. I can send these from anywhere in my program and they will be transmitted correctly.
  debugSerial.println(F("SENDING: DYNAMIC_WELCOME_LIGHTS_BYTES"));
  ibus.write(DYNAMIC_WELCOME_LIGHTS_BYTES, sizeof(DYNAMIC_WELCOME_LIGHTS_BYTES));

Serial output will display: TRANSMIT xx xx xx etc. correctly. It will show the exact same bytes as defined in ibusGlobals.h

  • When filling a new byte[] that can be modified (so not const and not PROGMEM like the other arrays), calculations are done correctly. When looping through the array before sending it over the bus, everything seems fine. The array I'm using is created as a global:
byte DYNAMIC_WELCOME_LIGHTS_BYTES [20];
  • To test wehter the error occurs inside the library or after calling the function in my own code, I added a small for loop that will print the received array before the library actually does anything. It prints the wrong characters, so I assume the problems occurs between where I call the function and where it starts to execute.

So to make it a bit more clear...
Here's 'my arduino code (for loop shows bytes as expected):

  // Print DYNAMIC_WELCOME_LIGHTS_BYTES
  curr_Time();
  debugSerial.println("DYNAMIC_WELCOME_LIGHTS_BYTES:");
  curr_Time();
  for (int i = 0; i < sizeof(DYNAMIC_WELCOME_LIGHTS_BYTES); i++)
  {
    debugSerial.print(DYNAMIC_WELCOME_LIGHTS_BYTES[i], HEX);
    debugSerial.print(" ");
  }
  debugSerial.println();


  //static byte TEMP_BUS_MESSAGE = DYNAMIC_WELCOME_LIGHTS_BYTES;
  const byte TEMP_BUS_MESSAGE PROGMEM = DYNAMIC_WELCOME_LIGHTS_BYTES;

  debugSerial.println(F("SENDING: DYNAMIC_WELCOME_LIGHTS_BYTES"));
  ibus.write(DYNAMIC_WELCOME_LIGHTS_BYTES, sizeof(DYNAMIC_WELCOME_LIGHTS_BYTES));

and this is the called function inside the library (for loop shows bytes changed):

void IbusSerial::write(const byte message[], byte size)
{

  //For debugging purposes (3-8-2019)
  pIbusDebug->print("Received Array: ");
  for (unsigned int i = 0; i < size; i++)
  {
    // pIbusDebug->print(message[i], HEX); // displays different characters?
    pIbusDebug->print(pgm_read_byte(&message[i]), HEX);
    pIbusDebug->print(" ");
  }
  pIbusDebug->println();
  //For debugging purposes (3-8-2019)


  ibusSendBuffer.write(size);
  for (unsigned int i = 0; i < size; i++)
  {
    ibusSendBuffer.write(pgm_read_byte(&message[i]));
  }
}
  • I've tried creating a second array AFTER the calculation was done, and making it const PROGMEM just like all the 'usual' arrays that do work (in the code above).
 const byte TEMP_BUS_MESSAGE PROGMEM = DYNAMIC_WELCOME_LIGHTS_BYTES;

Then sending TEMP_BUS_MESSAGE instead of DYNAMIC_WELCOME_LIGHTS_BYTES
This doesn't change anything.

  • When sending the newly filled byte [] with the same command as above, 2 things seem to go wrong:
  1. The size of the array changes to 1 when creating the new const byte TEMP_BUS_MESSAGE PROGMEM.
    If I use DYNAMIC_WELCOME_LIGHTS_BYTES directly, the size in this example will stay 20.
  2. The bytes in the array change.

Here's a download link to the code including the library. (Not the fastest...)
I downloaded the library here . Arduino Sketches / Version 0.1.0-beta
Haven't made any changes besides LED output.

Also, I'm curious why these 2 prints (inside IbusSerial::write) display different character:

    // pIbusDebug->print(message[i], HEX); // displays different characters?
    pIbusDebug->print(pgm_read_byte(&message[i]), HEX); // Displays correct byte

I really appreciate all the help!