Serial Comms with RC Receiver Serial data Stream

Hi all, I'm looking for a little guidance on the attached.

I'm using a Mega2560, RX1/TX1 and attempting to read a serial data stream from an RC receiver that has a special serial output port for a variety of purposes. This is NOT a PPM stream that has been a common RC data stream capture technique. Attached is the spec (pdf document) from the manufacturer that details the header structure, format of data, baud rates and how to calculate servo pulse widths etc, etc. Also attached is a small code loop for the 2560 and a sample of the output I am getting. I'm running the serial port at 125K Baud, 250K must be for the 24 channel data stream which I have not yet enabled in the RC Transmitter nor tested.

The issue is that the output is not consistent with how I have interpreted the specification. I'm looking ONLY for the packet that has 0x3E 03 header which should contain ONLY the servo signal pulse width for the 16 servo channels contained in the data stream.

Here's my code:

const byte len = 40;
byte inBuffer1_16[len] {0};

void setup() {
  Serial.begin(9600);
  Serial1.begin(125000);
  delay (200);
}

void loop() {
  byte inByte1;
  if (Serial1.available() > 0){
    inByte1 = Serial1.read();
    inBuffer1_16[0] = inByte1;
    if (inByte1 == 0x3E){
      byte inByte2 = Serial1.read();
      if (inByte2 == 0x03){
        inBuffer1_16[1] = inByte2;
        serialPort1();
      }
    }
  }
}

void serialPort1(){
  for (byte i = 2; i < len; i++){
    inBuffer1_16[i] = Serial1.read();
  }
  printEXPacket();
  return;
}

void printEXPacket(){
  for (byte i = 0; i < len; i++){
    Serial.println(inBuffer1_16[i], HEX);
  }
  return;
}

and here's an example of the output I get which I have annotated.

3E   // the 0x3E header
3     // followed by the 0x03
28   // the length of the packet  (40 bytes)
4D   // the packet ID
31   // The data type - channel data
20   // # of bytes of channel data - 32 bytes, 16 words
68
1F
40
1F
40
1F
40
1F
1E
30
D9
2E
B9
2E
E8
20
E0
2E
E0
2E
4D
31
20
68
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
FF   // this appears to be bad data
1F   // this appears to be bad data
FF   // this appears to be bad data

3E
3
28
4D
31
20
68
1F
40
1F
40
1F
40
1F
1E
30
D9
2E
BB
2E
E8
20
E0
2E
E0
2E
E0
2E
E0
2E
E0
FF
FF
FF
FF
FF
FF
FF
FF
FF

3E  // this appears to be a reasonable packet have not check the last two CRC-CCITT bytes to verify
3
28
4D
31
20
68
1F
40
1F
40
1F
40
1F
1D
30
D9
2E
BB
2E
E8
20
E0
2E
E0
2E
E0
2E
E0
2E
E0
2E
E0
2E
E0
2E
E0
2E
A1
27

Of particular note is the packet ID, the forth byte in the stream. It is the same value which surprises me since I'm assuming that every packet should be different.

I'm wondering it I have a timing issue with the speed at which data is being transmitted from the receiver and the speed at which I am trying to push it to the serial monitor or whether I simply have mode code all screwed up - BTW, I've never done serial communications before this so have no idea if my approach is even correct. I assume the serial "register" is cleared and loaded with the next byte in the serial buffer after each read().

Any guidance would be greatly appreciated.

EX_Bus_protokol_v121_EN.pdf (633 KB)

Your code assumes that your messages are always 40 bytes in length.

Does the RC receiver send out other messages of different length that also start with 0x3e 0x03 ?

The third byte is the packet length byte, in the cases I'm searching for, 0x28, or 40 bytes. There may be other packets that are sent with 0x3E + 0x03 that are less than 40 bytes but the spec that I attached doesn't specifically mention them.

After pondering further on this, and with the help of a couple German Hefeweizens, I realized that the fifth byte is the real key because 0x31 here means that the data type IS the channel data and that HAS a fixed length of 40 bytes in the 16 channel mode (there's also a 24 channel mode that I have yet to enable in the Tx - that is probably more than 40 bytes and may weel be at 250kBaud). If the fifth byte was 0x3A, it would be the telemetry data and if 0x3B, the Jetibox data.

The 0x31 is the key to differentiate between the data type but the 0x3E and either 0x01 or 0x03 is the header trigger for the code.

What concerns me is that I'm simply not getting consistent data from the serial stream - at least that's what I believe. The code I present above ONLY generated the three "packets" I noted in the second code block above and then stopped altogether and produced no further output. Hence my question on timing.

I have seen packets that are much shorter than 40 bytes and these would obviously fail the CRC check but to have 0xFF in the data stream is not correct. The transmitter sending the data is sat on my bench and I'm not wiggling sticks or switches so these values should not change appreciably, if at all. The three data stream outputs as I presented above change when technically, they shouldn't. If there were other 0x3E followed by either 0x01 or 0x03 in the above noted output packets, I'd be asking the same questions you did, but I don't see them.

So to recap, the first two packets of data should really be the same as the third. They start off that way and the header indicates they should all be 40 bytes long (third byte = 0x28)but the 0xFF is not correct and I have no idea why that is the case. Combine that with the fact that the packet ID is the same for all three packets and I get the distinct feeling that I'm not reading this data correctly, but I could be wrong about this point.

Set Arduino serial baud rate above 115200 ->230400, 256000,307200,614400 - Project Guidance - Arduino Forum may be of interest

I wanted to add another visual to this.

I modified the code a little to read in the fifth byte and print ONLY those packets with 0x31.

In the attached file (png image) is a different representation of what is being output to the serial monitor. On the monitor this is one continuous stream of data from the reads but in this presentation I've cut and pasted each "packet" (as defined by it's header start value of 0x3E) and placed in the spreadsheet as columns. They are in order as they were generated by the code. I've put 25 separate packets and you'll see that ALL of them indicate a packet length of 40 bytes (third byte 0x28). However, what is sent to the monitor is packets of varying lengths. Some are complete packets (those shown as green columns). Many of the packets are NOT complete even though they should be 40 bytes long. Some have FF's where they shouldn't have. The red locations shows an offset in value for column 13 (value D9), etc etc.

I feel this may be related to baud rate and the error% associated with 125k vs the 115200 but I have no idea how to validate that statement.

Lastly, after a short period of time, generally less than a minute, the whole thing hangs and ceases to function.

Can you try at 250K?
125K is non-standard.

EDIT:
According to higher speed data transfers - Project Guidance - Arduino Forum, 125K is a zero-error baud rate for the Atmega328

Thanks ieee448, I read that post as part of my research into this. It makes a series of statements that leads me to ask the questions I do. However, I'm locked to 125K Baud since this is what the manufacturer has implemented. What I'm trying to do is find out if baud rates ARE the cause and if there are ways to work around this.

I think I mentioned that I have never done serial communication before and I did spend a couple days researching methods of implementation but unfortunately, I've hit a wall and have no clue where to go from here.

I have read the ATMega manual and know that UBBRn can be changed to get certain bit rates for the port and wonder whether this might be a solution. Standard baud rates are listed in the manual but it looks as though a UBBR value of 7 seems to create a baud of 125K but I don't know if this might be the right approach and how it might be implemented. 125K is not a standard but it appears that it is feasible withing the architecture.

The receiver selects the speed between 125K and 250K. I can't control this and I have selected 250k several times with NO resulting output at all. 250K doesn't work, there is no comms at this speed.

125K is the only speed I can set.

I do have an Uno. I didn't think to try that. I will try and see what I get - thanks for the tip. Since the 328 only has one USART, I assume in need to go the SoftwareSerial route.

So after looking at the 328 manual, it's the same as the 2560 in this regard. What I think I need to do first is set the UBBR to 7 vs it's current setting of 8 for 115200. A UBBR of 7 creates an exact match to 125K which, if my interpretation of the manual is correct, should create the 0% error rate. I would have assumed that the Serial1.begin(125000) would have taken care of that but perhaps it doesn't.

According to the last link, you shouldn't need to do anything except set the baud rate.

You are trying to read chars when there may not be any available. You must check available before each read, or wait until available > the number of chars you want to read consecutively.

Cheers,
/dev

So, I updated the code to the following:

const byte len = 40;
byte inBuffer1_16[len] {0};

void setup() {
  Serial.begin(9600);
  Serial1.begin(125000);
  delay (200);
}

void loop() {
  byte inByte1, inByte2, inByte3;
  if (Serial1.available() > 0){
    inByte1 = Serial1.read();
    if (inByte1 == 0x3E){
      inBuffer1_16[0] = inByte1;
      if (Serial1.available() > 0){
        inByte1 = Serial1.read();
        if (inByte1 == 0x03 || inByte1 == 0x01){
          inBuffer1_16[1] = inByte1;
          if (Serial1.available() > 0){
            inByte1 = Serial1.read();
          }
          if (Serial1.available() > 0){
            inByte2 = Serial1.read();
          }
          if (Serial1.available() > 0){
            inByte3 = Serial1.read();
            if (inByte3 == 0x31){
              inBuffer1_16[2] = inByte1;
              inBuffer1_16[3] = inByte2;
              inBuffer1_16[4] = inByte3;
              serialPort1();
            }
          }
        }
      }
    }
  }
}

void serialPort1(){
  for (byte i = 5; i < len; i++){
    if (Serial1.available()>0){
      inBuffer1_16[i] = Serial1.read();
    }
  }
  printEXPacket();
  return;
}

void printEXPacket(){
  for (byte i = 0; i < len; i++){
    Serial.println(inBuffer1_16[i], HEX);
  }
  return;
}

If that's what you meant /dev - there was no change in the output - same issue persists. If this was not what you meant, could you please re-state?

So I made another small tweak and removed the selector for 0x01. Now I am getting consistent, full length packets but only about 15 or 16 of them before the whole thing hangs and sits there dumb - can anyone see something obvious in this code that would cause this to hang solid?

const byte len = 40;
byte inBuffer1_16[len] {0};

void setup() {
  Serial.begin(9600);
  Serial1.begin(125000);
  delay (200);
}

void loop() {
  byte inByte1, inByte2, inByte3;
  if (Serial1.available() > 0){
    inByte1 = Serial1.read();
    if (inByte1 == 0x3E){
      inBuffer1_16[0] = inByte1;
      if (Serial1.available() > 0){
        inByte1 = Serial1.read();
        if (inByte1 == 0x03){
          inBuffer1_16[1] = inByte1;
          if (Serial1.available() > 0){
            inByte1 = Serial1.read();
          }
          if (Serial1.available() > 0){
            inByte2 = Serial1.read();
          }
          if (Serial1.available() > 0){
            inByte3 = Serial1.read();
            if (inByte3 == 0x31){
              inBuffer1_16[2] = inByte1;
              inBuffer1_16[3] = inByte2;
              inBuffer1_16[4] = inByte3;
              serialPort1();
            }
          }
        }
      }
    }
  }
}

void serialPort1(){
  for (byte i = 5; i < len; i++){
    if (Serial1.available()>0){
      inBuffer1_16[i] = Serial1.read();
    }
  }
  printEXPacket();
  return;
}

void printEXPacket(){
  for (byte i = 0; i < len; i++){
    Serial.println(inBuffer1_16[i], HEX);
  }
  return;
}

You don't need the selector for 0x01 because you are not sending a response. You are simply reading.

I would increase the baud rate of the Serial Monitor.

const byte len = 40;
byte inBuffer1_16[len] {0};

void setup() {
  Serial.begin(12500);
  Serial1.begin(125000);
  delay (200);
}

void loop() 
{
    byte inByte;
    int count = 0;
    int messageLength = 0;


    if (Serial1.available())
    {
        inByte1 = Serial1.read();
        if (inByte == 0x3E)
        {
            count += 1;
            inBuffer1_16[0] = inByte;
            Serial.println(inByte);
        }
        else if (inByte == 0x03)
        {
            if (count == 1)
            {
                inBuffer1_16[1] = inByte;
                count += 1;     //byte 2
                Serial.println(inByte);
            }
        }
        else if (count == 2)
        {
            inBuffer1_16[2] = inByte;
            messageLength = inByte;
            count += 1;         //byte 3
            Serial.println(inByte);
        }
        else if (count == 3)
        {
            // Packet_ID
            inBuffer1_16[3] = inByte;
            count += 1;         //byte 4
            Serial.println(inByte);
        }
        else if (inByte == 0x31)
        {
            //
            inBuffer_16[4] = inByte;
            count += 1;         //byte 5
            Serial.println(inByte);
        }
        else if (count == 5)
        {
            //SUB_LEN
            inBuffer1_16[5] = inByte;
            count += 1;         //byte 6
            Serial.println(inByte);

            //read the data bytes + checksum
            serialPort1(messageLength + 2)
        }
    }
}

void serialPort1(int msgLength)
{
    for (byte i = 6; i < msgLength; i++)
    {
        if (Serial1.available())
        {
            inBuffer1_16[i] = Serial1.read();
            Serial.println(inBuffer1_16[i]);
        }
    }
}

ieee488, thanks for that alternate code. When I first ran it, it only produced the serial prints of 3E and 31. After adding count values in the if else statements, I eventually got it to cycle through counts 1 through 5. After changing messageLength I finally got serialPort1 to work but alas, the results are no different than what I'm getting other than it doesn't hang up now. So that's good!!

The serial monitor window didn't have 12500 as a baud rate so I tried all other values up to 250k and still no better results.

I am still concerned that maybe serial1/2/3 or serial for that matter, don't support "non-standard" bauds. If that's the case, I may need to find a custom solution to support 125K.

TonyEll:
I am still concerned that maybe serial1/2/3 or serial for that matter, don't support "non-standard" bauds. If that's the case, I may need to find a custom solution to support 125K.

According to the datasheet and to the person who wrote that other post that I linked, it is suppose to work at 125K, but I haven't personally done it since I use the serial for monitoring data.

You may want to look at SiliconLabs CP2103EK evaluation kit. http://www.silabs.com/products/interface/Pages/CP2103EK.aspx if you are looking for 3rd-party hardware.

If this was not what you meant, could you please re-state?

Reading data on the Arduino is different from most PC applications. Calling Serial.read() does not always return data. Well, it returns -1 (or 0xFF) if no characters are available. In a PC app, calling read will usually wait until a data has been received: the PC app is "blocked" until a character comes in. That's not very noticeable because the PC has a nice multitasking OS so your pointer keeps moving and other windows respond, even though the one app is waiting for a character.

On the Arduino, you can make it block the same way, but there's no OS that will let you do something else while you're waiting for data (yes, there are multitasking solutions, but let's avoid that for now). So blocking on the Arduino can keep you from doing other things you may need to do, like blinking an LED or catching a button press.

First, lets look at this snippet:

  if (Serial1.available() > 0){
    inByte1 = Serial1.read();
    if (inByte1 == 0x3E){
      inBuffer1_16[0] = inByte1;
      if (Serial1.available() > 0)
        inByte1 = Serial1.read();
        if (inByte1 == 0x03){

This starts out great, because it checks to see if any data is available. If not, it just skips the rest, and loop finishes and gets called again. If there is data, it reads the first character and saves it.

But then it checks to see if a second character is available. Guess what... 999 times out of 1000, it won't be there. At 125000 baud, it takes 80us for each character to arrive. If you have a 16MHz processor clock, that's ~1200 instructions, or several hundred lines of code in your sketch. The Arduino is orders of magnitude faster than the data is being sent.

There are a few common ways to write an Arduino sketch that can cope with this speed disparity:

1) Force the Arduino to wait for each character (i.e., block), and write the sketch in a very linear, top-to-bottom fashion:

  while (Serial1.available() == 0)
    ; // wait
    inByte1 = Serial1.read();

    if (inByte1 == 0x3E){
      inBuffer1_16[0] = inByte1;

      while (Serial1.available() == 0)
        ; // wait
      inByte1 = Serial1.read();

      if (inByte1 == 0x03){

2) Save each character in a buffer as it arrives until you have received an entire packet. Then parse the packet all at once with similar linear code:

  static uint8_t count = 0; // keeps its value across calls to loop

  if (Serial1.available()) {
    inByte1 = Serial1.read();

    if (inByte1 == 0x3E) {

      // Next packet starting, process the current one we've been saving,
      //   if we have saved enough bytes (skips small/bad packets).

      if ((count > 6) && (inBuffer1_16[0] == 0x3E)) {

        if (inBuffer1_16[1] == 0x03) {
          uint8_t len = inBuffer1_16[2];
          uint8_t id  = inBuffer1_16[3];
          uint8_t typ = inBuffer1_16[4];
          if (typ == 0x31) {
            uint8_t channelDataWords = inBuffer1_16[5];
                ...etc
          }

        } else if (inBuffer1_16[1] == 0x01) {
        }
      }

      // Reset the count to start a new packet with the 0x3E we just received.
      count = 0;
    }

    // Save the current byte
    if (count < sizeof(inBuffer1_16))
      inBuffer1[ count ] = inByte1;
    count++;
  }

3) Parse each character as it arrives, picking up where you left off after doing other things. This is close to what ieee488 was suggesting:

void loop()
{
    // statics keep values across calls to loop
    static uint8_t count         = 0;
    static bool    skipping      = true;
    static uint8_t messageLength = 0;
  
    if (Serial1.available()) { // or 'while'

        uint8_t inByte = Serial1.read();

        if (inByte == 0x3E) {
        
            // Starting a new packet!

            inBuffer1_16[0] = inByte;
            count    = 1;
            skipping = false;

        } else if (!skipping) {

            // Saving a packet, store the current byte

            if (count < sizeof(inBuffer1_16))
              inBuffer1_16[ count ] = inByte;

            // Use the current byte, depending on 'count'
            switch (count) {
              case 1:
                if (inByte != 0x03)
                  skipping = true; // ignore other packet selector values
                break;

              case 2:
                messageLength = inByte;
                // Error check?
                if (messageLength != 40)
                  skipping = true;
                break;

              case 3:
                // packetID = inByte; ?
                inBuffer1_16[3] = inByte;
                break;

              case 4:
                if (inByte != 0x31)
                  skipping = true; // ignore if not channel data
                break;

              case 5:
                channelDataWords = inByte;
                // Error check?
                if (channelDataWords > 32) // != messageLength-8 ?
                  skipping = true;
                break;

              default:
                if (count == messageLength-1) {
                  // All packet bytes received, verify the checksum

                  if (CRC( inBuffer1_16, messageLength-2 ) == ??) {
                    // Packet ok, do something with it (NB: could also
                    //    accumulate CRC as bytes are received)

                    printEXPacket(); // ???
                  }
                  
                  // Wait for the next packet to start...
                  skipping = true;
                }
                break;

            }

            count++;
        }
    }

    // Do other things, too?
}

Choice 1 is sometimes easier for newbies to understand, as it is very linear. The disadvantage is that it blocks and does not recover from data errors very well.

Choice 2 is pretty easy to understand and it doesn't block; other tasks can do things while waiting for a complete packet. The disadvantage is that all the packet processing happens when the last byte is received (actually, the first byte of the next packet in that code). This is similar to the Serial Input Basics example sketches.

Choice 3 is more difficult to understand, because the entire section is executed for each character, either starting a new packet or using the switch statement to handle each char. Different cases do a little work, depending on the count. The advantage is that the processing is spread out over each character, and you can decide to skip the rest of a packet at any point. This is actually a Finite-State machine approach, and it's worth searching here for recent posts on FSMs (my posts here and here). Nick Gammon has a great description, too.

Cheers,
/dev

/dev, I got it. My previous comments on the packet ID being the same for many packets received gave me a clear sense that timing was the key and that is what you clearly spelled out. My lack of experience simply didn't get me to what youve just spelled out. I am a newbie at this (Arduino) and have never done serial comms before but your explanation is clear, concise with a variety of pointers. It is very much appreciated and I'll report back once I've worked through this. Thank you for spending the time on your extensive reply.

Hi all, so firstly, thanks to /dev for his FSM - this is working a treat with the EX Bus serial stream. I've integrated the CRC as well as the I2C for the PWM drivers and the system is working extremely well.
From my perspective, this inquiry is resolved - thanks again /dev!! FSM's will be something I will use greatly in the future!!

Hi all again, I'm looking for some more expert guidance on the project noted in this thread. I've worked to get this FSM fully functional with the data packets I'm interested in using for servo deflection data (per the original EX Bus protocol attachment) and have a configuration that's now running 80+ servo's.

The Jeti systems allows the connection of TWO RC receivers to a slave device via a configuration called "Dual Path". In this configuration both receivers are enabled to transmit the EX data to the slave device (the device I'm designing is the slave device) - it's a redundancy feature in case one receiver looses connection with the transmitter.

So, I need to switch between two Mega serial ports (Serial1 and Serial2), IF one receiver has missing frames. I've attached a picture of what the output from each receiver looks like with timings. I can live with up to two frames missing from Serial1 before needing to switch to Serial2 and vice versa.

As I indicated previously, my serial communications knowledge is very minimal. I've attached my state machine loop (thanks /dev), with a modification to the front end designed to switch between the two serial streams IF Serial1 or Serial2 do not receive valid data after exTimeOut has expired (set to zero after the CRC check of the EX data stream ) - the timeout is currently set at 45ms. The code does not work and I don't have the experience to determine why this is the case. I suspect I have oversimplified it and need more validation somewhere or I've simply misinterpreted what Serial.available() means.

Note that the specs for the EX data stream specifically notes that NO EX data packet is sent to the slave device IF the master (receiver) did not get it's data from the transmitter - hence my assumption that Serialx.available will be 0.

Can anyone provide guidance please?

void loop()
{
  // statics keep values across calls to loop
  
  static uint8_t count            = 0;
  static uint8_t msgLength        = 0;
  static uint8_t chDataBytes      = 0;
  static uint8_t errorCount       = 0;
  static bool    skipping         = true;
  static bool    servoWrite       = false;
  static bool    is24Ch           = false;
  uint8_t        inByte;

  if (Serial1.available() == 0 && timeElapsed > exTimeOut){
    inPort = 1;           // Goto Serial2
  }
  else if (Serial2.available() == 0 && timeElapsed > exTimeOut){
    inPort = 0;           // Go back to Serial1
  }
  if (inPort == 0){
    if (Serial1.available()) {
      inByte = Serial1.read();
    }
    else if (inPort == 1){
      if (Serial2.available()) {
        inByte = Serial2.read();
      }
    }
    if (inByte == chHeader) {           // 0x3E - Starting a new packet!
      inBuffer[0] = inByte;
      count    = 1;
      skipping = false;                 // Flag that 0x3E found
    } 
    else if (!skipping) {               // handles all after 0x3E
      if (count < sizeof(inBuffer)) 
        inBuffer[ count ] = inByte;

      switch (count) {            // Use current byte based on 'count'
        case 1:
          if (inByte != listenOnly){     // Check for 0x03 and 0x01 (use both)
            if (inByte != slaveResp)
            skipping = true;
          }
          break;

        case 2:
          msgLength = inByte;
          if (msgLength != len16Ch){     // Check message length byte
            if (msgLength != len24Ch){
              skipping = true;
            }
            else if (msgLength == len24Ch){
              is24Ch = true;
            }
          }
          break;

        case 3:
          inBuffer[3] = inByte;         // Packet ID - store for CRC
          break;

        case 4:
          if (inByte != chData)         // Check for 0x31
            skipping = true;            // if not correct, restart
          break;

        case 5:
          chDataBytes = inByte;
            if (chDataBytes != chBytes16){ // check for 0x20 (32) bytes
              if (chDataBytes != chBytes24 && is24Ch){// check 0x28 (48)
                skipping = true;            // if not correct, restart
              }
            }
          break;
          
        default:
          if (count == msgLength-1) {     // All packet bytes received - CRC check next
            uint8_t crcMSB, crcLSB;
            if (msgLength == len16Ch){    // 16 channel CRC variable assignment
              crcMSB = crcMSB16; crcLSB = crcLSB16;
            }
            else if (msgLength == len24Ch){// 24 channel CRC variable assignment
              crcMSB = crcMSB24; crcLSB = crcLSB24;
            }
            // Convert 2 x CRC bytes into a single word
            uint16_t crc16z = inBuffer[crcMSB] * 256 + inBuffer[crcLSB];
            if (get_crc16z(inBuffer, msgLength-2) == crc16z) {
              timeElapsed = 0;             // Start frame timer at end of current frame
              byteToWordToTick(msgLength); // CRC Good, convert data to words & ticks
              servoWrite=true;             // Ready to write servo
              
              }
            skipping = true;               // This is the reset to wait next packet
          }
          break;
      }
      count++;
    }
  }

  if (servoWrite){
    if (loopPon){
      loopPon = false;
      if (msgLength == len16Ch){
        init16(msgLength);
      }
      else if (msgLength == len24Ch){
        init24(msgLength);
      }
    }
    if (msgLength == len16Ch){
      write16(msgLength);
    }
    else if (msgLength == len24Ch){
      write24(msgLength);
    }
    servoWrite=false;
  }
}