Char Array acting funky

Hello everyone!
This question is related to my topic HERE, but as this is a followup problem I choose to keep it separate - maybe this helps others researching in the future.

What I basically am doing is reading a Vehicle CAN-Bus and wanting to display the various information that can be included in the 8Byte HEX Message.
To do so I choose to bitRead() all the bits from the HEX-Message, and write it into a new Array (called Complete_Message in my Sketch).

Then I use a section that the user "cattledog" gave me to cut out a section of the Complete Array and convert this part into a decimal value, maybe representing the engine RPM.

The Problem:
If I Simply fill an Array with 1s and 0s (like I did in the Array "TestComplete_Message") everything works fine.
If I then let this Loop:

for(byte i = 0; i<len; i++)
          {
            byte u=0;
            for ( byte Bit=8; Bit>0; Bit--)
              {
                Complete_Message[(i*8)+u]=bitRead(rxBuf[i],Bit-1);  // Put all the 0s and 1s in ONE Array after each other

                Serial.print("Filling Position: ");       // Debugging to see if the Message is structured correctly
                Serial.print((i*8)+u);
                Serial.print(" with Digit: ");
                Serial.println(bitRead(rxBuf[i],Bit-1));
                u++;             
              }

          }

fill my array something goes wrong. I cant work with the array as with the Test.
I tried to terminate it, but no matter what I did, it don't work :frowning:

Here is my complete code:

// CAN Receive Example
//

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string
char Complete_Message[64]="";
char TestCompleteMessage[] = "1111100000111000111110000011100011111000001110001111100000111000";


#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(5);                               // Set CS to pin 10


void setup()
{
  Serial.begin(115200);
  
  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
    Serial.println("MCP2515 Initialized Successfully!");
  else
    Serial.println("Error Initializing MCP2515...");
  
  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.
  pinMode(CAN0_INT, INPUT);                            // Configuring pin for /INT input
  Serial.println("MCP2515 Library Receive Example...");
}

void loop()
{




  
  if(!digitalRead(CAN0_INT))                         // If CAN0_INT pin is low, read receive buffer
  {
    Serial.println("CAN-Message Arrived!");
    CAN0.readMsgBuf(&rxId, &len, rxBuf);            // Read data: len = data length, buf = data byte(s)
    
    if((rxId & 0x80000000) == 0x80000000)           // Determine if ID is standard (11 bits) or extended (29 bits)
      sprintf(msgString, "Extended ID: 0x%.8lX  DLC: %1d  Data:", (rxId & 0x1FFFFFFF), len);
    else
      sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);
  
    Serial.print(msgString);
  
    if((rxId & 0x40000000) == 0x40000000)
    {    // Determine if message is a remote request frame.
      sprintf(msgString, " REMOTE REQUEST FRAME");
      Serial.println(msgString);
    } else 
    for(byte i = 0; i<len; i++)
            {
              sprintf(msgString, " 0x%.2X", rxBuf[i]);
              Serial.print(msgString);
            }
      Serial.println();
      {
      //len


      for(byte i = 0; i<len; i++)
          {
            byte u=0;
            for ( byte Bit=8; Bit>0; Bit--)
              {
                Complete_Message[(i*8)+u]=bitRead(rxBuf[i],Bit-1);  // Put all the 0s and 1s in ONE Array after each other

                Serial.print("Filling Position: ");       // Debugging to see if the Message is structured correctly
                Serial.print((i*8)+u);
                Serial.print(" with Digit: ");
                Serial.println(bitRead(rxBuf[i],Bit-1));
                u++;             
              }

          }
          Serial.print("Size of Complete_Message: ");
          Serial.println(sizeof(Complete_Message));
          Serial.print("Complete Message= ");
          Complete_Message[strlen(Complete_Message)+1] = '\0';
          Serial.println(Complete_Message);
       
          
    }

// The follwing part is just for testing of the Bits are in the correct order!
  
    Serial.println();
    Serial.print("0-7: ");
    for ( byte i=0; i<8; i++)
    {
    Serial.println(Complete_Message[i]);
    }
 
// The following part should filter out the bits needed to assmeble the CAN-Value

         
  char * ptr = CompleteMessage;          // Pointer for array
  char input_binary_string[9] = "";
  strncpy(input_binary_string, ptr + 0, 7); //extract 8 bits from 6 chars in
  Serial.println(input_binary_string);
  long value = strtol(input_binary_string, NULL, 2);
  Serial.println(value);
  }




}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

and the Debug Info:

Entering Configuration Mode Successful!
Setting Baudrate Successful!
MCP2515 Initialized Successfully!
MCP2515 Library Receive Example...
CAN-Message Arrived!
Standard ID: 0x008       DLC: 8  Data: 0x12 0xFF 0x01 0x01 0x01 0x01 0x01 0x01
Filling Position: 0 with Digit: 0
Filling Position: 1 with Digit: 0
Filling Position: 2 with Digit: 0
Filling Position: 3 with Digit: 1
Filling Position: 4 with Digit: 0
Filling Position: 5 with Digit: 0
Filling Position: 6 with Digit: 1
Filling Position: 7 with Digit: 0
Filling Position: 8 with Digit: 1
Filling Position: 9 with Digit: 1
Filling Position: 10 with Digit: 1
Filling Position: 11 with Digit: 1
Filling Position: 12 with Digit: 1
Filling Position: 13 with Digit: 1
Filling Position: 14 with Digit: 1
Filling Position: 15 with Digit: 1
Filling Position: 16 with Digit: 0
Filling Position: 17 with Digit: 0
Filling Position: 18 with Digit: 0
Filling Position: 19 with Digit: 0
Filling Position: 20 with Digit: 0
Filling Position: 21 with Digit: 0
Filling Position: 22 with Digit: 0
Filling Position: 23 with Digit: 1
Filling Position: 24 with Digit: 0
Filling Position: 25 with Digit: 0
Filling Position: 26 with Digit: 0
Filling Position: 27 with Digit: 0
Filling Position: 28 with Digit: 0
Filling Position: 29 with Digit: 0
Filling Position: 30 with Digit: 0
Filling Position: 31 with Digit: 1
Filling Position: 32 with Digit: 0
Filling Position: 33 with Digit: 0
Filling Position: 34 with Digit: 0
Filling Position: 35 with Digit: 0
Filling Position: 36 with Digit: 0
Filling Position: 37 with Digit: 0
Filling Position: 38 with Digit: 0
Filling Position: 39 with Digit: 1
Filling Position: 40 with Digit: 0
Filling Position: 41 with Digit: 0
Filling Position: 42 with Digit: 0
Filling Position: 43 with Digit: 0
Filling Position: 44 with Digit: 0
Filling Position: 45 with Digit: 0
Filling Position: 46 with Digit: 0
Filling Position: 47 with Digit: 1
Filling Position: 48 with Digit: 0
Filling Position: 49 with Digit: 0
Filling Position: 50 with Digit: 0
Filling Position: 51 with Digit: 0
Filling Position: 52 with Digit: 0
Filling Position: 53 with Digit: 0
Filling Position: 54 with Digit: 0
Filling Position: 55 with Digit: 1
Filling Position: 56 with Digit: 0
Filling Position: 57 with Digit: 0
Filling Position: 58 with Digit: 0
Filling Position: 59 with Digit: 0
Filling Position: 60 with Digit: 0
Filling Position: 61 with Digit: 0
Filling Position: 62 with Digit: 0
Filling Position: 63 with Digit: 1
Size of Complete_Message: 64
Complete Message= 

0-7:

If I then just add ",BIN" to the line

Serial.println(Complete_Message[i],BIN);

the output is this:

0-7: 0
0
0
1
0
0
1
0

I tried what I could.
Where is the difference between assigning values to a char Array directly vs. filling it with data in a loop?
I also tried to initialize the array with 64 0s and then overwriting it in the loop. No luck either.

Glad for some insight...

Thanks for reading!

When you do

char TestCompleteMessage[] = "1111100000111000111110000011100011111000001110001111100000111000";

this is putting ASCII code into each cell of the array (and a trailing NULL '\0' character as you declare it as a cString with the double quotes)

In this representation each '1' is coded by the value 49decimal and each '0' is coded by the value 48decimal

When you fill in your array with the for loop and bitread() you actually put the numbers 0 or 1 in the array, not ASCII codes --> so it's not the same values you are looking at in both arrays.

To make the manual definition compatible with the bitread() initialization, you would need to declare it this way:char TestCompleteMessage[] = {1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0};then you are really have the same values

makes sense?

Thanks for the reply! Sounds understandable!
So I would need to add a '\0' into the loop so after it get added after each 1 or 0?

EDIT:
I tried this:

for(byte i = 0; i<len; i++)
          {
            byte u=0;
            for ( byte Bit=8; Bit>0; Bit--)
              {
                Complete_Message[(i*8)+u]=bitRead(rxBuf[i],Bit-1);  // Put all the 0s and 1s in ONE Array after each other

                Serial.print("Filling Position: ");       // Debugging to see if the Message is structured correctly
                Serial.print((i*8)+u);
                Serial.print(" with Digit: ");
                Serial.println(bitRead(rxBuf[i],Bit-1));        
                Complete_Message[(i*8)+u]+='\0';
                Serial.println("Added a NULL");
                u++;             
              }

          }

but it did not work either... how do I add the termination after each digit?

In a nutshell this is what your for loop with the bitread() is doing


The loop is going through the rxBuf array which contains the 8 bytes from your CAN-Bus and reads that bit by bit and copy the bits into individual entries of the Complete_Message[] array (what a loss of memory :slight_smile: )

I'm not sure what you are trying to achieve after that...

:slight_smile: Ok, that indeed does not sound like the most efficient way to do this.
To my defense - I am a self taught programmer, in real life I am a mechanical engineer :slight_smile: :slight_smile:

What I am trying to accomplish is simply reading out the Bits in the CAN-Message and then converting sections of this to decimal value. The Messages are not always using full Bytes, so I kind of have to piece them together.
I belive this can be done by bitshifting and probably many other ways...but the one I choose seemed the only understandable one for me.
Is there a way I can adapt my Loop so I can use my code or am a screwed? :confused:

I am a self taught programmer, in real life I am a mechanical engineer

No one can be perfect :slight_smile:
(kidding - I would be totally at a loss going anywhere deep in your field!)

Well what would help is to know which set of bits in the 8 bytes rxBuf array are representing what.

Do you have that description of the frame?

then an union / struct with bit fields (if it's static) will get you there easily

for example, on this web site I can see a description (but with more than 8 bytes potentially)

(kidding - I would be totally at a loss going anywhere deep in your field!)

:slight_smile: Well, I think if you know how things work they all seem easy, right?

Well, I need to make something that I can easy adapt...that's why I choose that way I did.
With this:

/char * ptr = Complete_Message;          // Pointer for array
  char input_binary_string[9] = "";
  strncpy(input_binary_string, ptr + 0, 7); //extract 8 bits from 6 chars in
  Serial.println(input_binary_string);
  long value = strtol(input_binary_string, NULL, 2);
  Serial.println(value);

BTW from another user here on the forum I could easily adapt which Bits I need to take from the message, convert it to Decimal and Offset/Multiply it. Of course, that didn't work for the reasons you told me earlier. :confused:

Perfect for me would be a function that I can call with the variables Startbit and length, then I could call it over and over for any information I need to get from the CAN Message. Also would be quite useful for reverse engineering...

Sme1986:
Perfect for me would be a function that I can call with the variables Startbit and length, then I could call it over and over for any information I need to get from the CAN Message. Also would be quite useful for reverse engineering...

I believe I gave you a function called GetField that does exactly that :slight_smile:

@Wildbill:

void getField(char* buffer, int index)

--> this correct? So I need to pointer to indicate the Startbit? So like:

char * ptr = Complete_Message;
Info1= getField(ptr + 0, 8);

Wont I then again have the same problem that I cant use

long value = strtol(Info1, NULL, 2);
  Serial.println(value);

I would be glad for an example. I now am searching the net for 2 Days nonstop on a probably total minor problem :confused: :confused:

here is an example using bits and bytes an no complicated function, may be that would give you some ideas

const byte binaryFrame[] = {0, 1, 2, 4, 8, 16, 32, 64, 128};
byte framSize = sizeof(binaryFrame) / sizeof(binaryFrame[0]); //  nb of elements in the array

void printByte(const byte b)
{
  for (int i = 7; i >= 0; i--) Serial.print(bitRead(b, i));
}


// extract all the bits in frame between 2 boundaries (included)
// first bit in frame is bit 0
// last bit in frame is framSize * 8 - 1

void extractBits(unsigned int startBit, unsigned int endBit)
{
  if ((endBit < startBit) || (endBit >= framSize * 8)) return; // that's an error


  int startByte = startBit / 8;
  int startBitInStartByte = 7 - (startBit % 8);

  int endByte = endBit / 8;
  int endBitInEndByte = 7 - (endBit % 8);

  Serial.print("Extracting from bit: "); Serial.print(startBit);
  Serial.print(" to bit: "); Serial.println(endBit);

  Serial.print("This is from byte: "); Serial.print(startByte);
  Serial.print(" starting from bit: "); Serial.print(startBitInStartByte);
  Serial.print(" to byte: "); Serial.print(endByte);
  Serial.print(" ending at bit: "); Serial.println(endBitInEndByte);

  if (startByte != endByte) {
    // we have 3 parts potentially to deal with. pieces if the first byte, then full bytes, then pieces of last byte

    // the bits from the firts byte
    for (int currentBit = startBitInStartByte; currentBit >= 0; currentBit--) Serial.print(bitRead(binaryFrame[startByte], currentBit));
    Serial.print(F(" "));

    // handle all the complete bytes in between
    for (int currentByte = startByte + 1; currentByte < endByte; currentByte++) {
      printByte(binaryFrame[currentByte]);
      Serial.print(F(" "));
    }

    // handle the last partial byte
    for (int currentBit = 7; currentBit >= endBitInEndByte; currentBit--) {
      Serial.print(bitRead(binaryFrame[endByte], currentBit));
    }
  } else {
    // we are extracting only from within one byte
    for (int currentBit = startBitInStartByte; currentBit >= endBitInEndByte; currentBit--) Serial.print(bitRead(binaryFrame[startByte], currentBit));
  }
  Serial.println();
}


void setup() {
  Serial.begin(115200);
  Serial.println(F("Frame content: "));
  for (byte i = 0; i < framSize; i++) {
    printByte(binaryFrame[i]);
    Serial.print(F(" "));
  }
  Serial.println(F("\n"));

  extractBits(0, framSize * 8 - 1); Serial.println();
  extractBits(4, 36); Serial.println();
  extractBits(22, 23); Serial.println();
  extractBits(29, 29); Serial.println();

}

void loop() {}

the function extractBits() will go into the binaryFrame array and get you all the relevant bits between a start and end bit index (inclusive of both ends)

You should see this in the console

[size=8pt][color=purple]
Frame content: 
0000[b][color=blue]0000 00000001 00000010 00000100 00001[/color][/b]000 00010000 00100000 01000000 10000000 

Extracting from bit: 0 to bit: 71
This is from byte: 0 starting from bit: 7 to byte: 8 ending at bit: 0
00000000 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000

Extracting from bit: 4 to bit: 36
This is from byte: 0 starting from bit: 3 to byte: 4 ending at bit: 3
[b][color=blue]0000 00000001 00000010 00000100 00001[/color][/b]

Extracting from bit: 22 to bit: 23
This is from byte: 2 starting from bit: 1 to byte: 2 ending at bit: 0
10

Extracting from bit: 29 to bit: 29
This is from byte: 3 starting from bit: 2 to byte: 3 ending at bit: 2
1[/color][/size]

(--> I highlighted in blue one example)

remember all is counted from 0:

  • in an array the first element is at index 0
  • in a byte, the bit to the left (most significant bit) is at position 7 and the one to the right (least significant bit) is at position 0

@J-M-L: Thank you so much for that example! Must have taken some time to write that up. Amazing!

Although just 32 I am old-school engineer - I will actually print this out and go through it step by step on paper :slight_smile:

One thing I noticed: You are referring to a BinaryFrame of 9 Bytes of Data - CAN Bus only transmits 8Bytes I believe. I know this probably does not matter, I just removed the 128 and is seems to be fine.

Thanks again for the help!

Hi

my pleasure - only took 10 minutes to type...

I just created a fake frame with some basic data that could be easily recognizable - the code would work just the same if you put 8 bytes or 20 bytes as it adapts to the size of the array as it's calculated with the line

byte framSize = sizeof(binaryFrame) / sizeof(binaryFrame[0]); //  nb of elements in the array

10 Minutes - makes me feel even more stupid :slight_smile: :confused: - I am working on this for days XD

So I went through it all and it seems very plausible to me :slight_smile:
Just one last question:
If I try to collect the extracted bits in a new char Array and then want to use

long value = strtol(input_binary_string, NULL, 2);
  Serial.println(value);

to get the Decimal value of the bits - I still don't get a result, similar to the problem I had with my own attempt.
Whats the key to this? Terminination, I belive - but how...I tried all reasonable variants and none seemed to work :frowning:

10 Minutes - makes me feel even more stupid :slight_smile:

~50 years of programing experience... that helps

I'm not sure what is in input_binary_string. ASCII ?

~50 years of programing experience... that helps

Ok, now I feel better :slight_smile:

I'm not sure what is in input_binary_string. ASCII ?

Well, what I finally have to do is convert those extracted Bits into a Decimal value, then scale and offset it. The later is not going to be a problem - but I cant get them to convert into the decimal system.

Like in your example this:

0000 00000001 00000010 00000100 00001

would be 2113665 in Decimal

Multiplied by 0.0001 this could be something like the vehicle speed (whereas of course this would be way to precise :slight_smile:

I don't know if you want to go back to the null terminated character array approach, but if you do, you need to modify the bitRead() routine to convert the bits to character 0's and 1's. You can use the itoa() function.Here's some test code with that conversion patched in.

void setup()
{
  Serial.begin(115200);
  char Complete_Message[65] = "";
  byte len = 8;
  byte rxBuf[8] = {0x12, 0xFF, 0x01 , 0x01, 0x01, 0x01, 0x01, 0x01};
  char msgString[5];
  Serial.print("rxBuf: ");
  for (byte i = 0; i < len; i++)
  {   
    sprintf(msgString, " 0x%.2X", rxBuf[i]);
    Serial.print(msgString);
  }
  Serial.println();

  for (byte i = 0; i < len; i++)
  {
    byte u = 0;
    for ( byte Bit = 8; Bit > 0; Bit--)
    {
      //bit reading routine with conversion to char for appending to Complete_Message
      char str[2]= "";//to hold converted bit
      itoa(bitRead(rxBuf[i],Bit-1),str,2);
      strcat(Complete_Message,str);
      
      //Complete_Message[(i * 8) + u] = bitRead(rxBuf[i], Bit - 1); // Put all the 0s and 1s in ONE Array after each other

      Serial.print("Filling Position: ");       // Debugging to see if the Message is structured correctly
      Serial.print((i * 8) + u);
      Serial.print(" with Digit: ");
      Serial.println(bitRead(rxBuf[i], Bit - 1));
      u++;
    }
  }
  Serial.println();
  Serial.println("Complete_Message as Null terminated character string");
  Serial.println(Complete_Message);
}
void  loop() {}

EDIT: There is a more simple way to convert the bitReads to chars and to null terminate terminate the array than using itoa and strcat.

//char str[2]= "";//to hold converted bit
//itoa(bitRead(rxBuf[i],Bit-1),str,2);
//strcat(Complete_Message,str);
      
Complete_Message[(i * 8) + u] = '0' +  bitRead(rxBuf[i], Bit - 1); // Put all the 0s and 1s in ONE Array after each other

Complete_Message[(i * 8) +u +1] = '\0'; //null terminate next position

:slight_smile: Yes, this works perfectly!

Just curious: Why do I need

Complete_Message[(i * 8) + u] = '0' + bitRead(rxBuf(I), Bit - 1);

that '0' in FRONT of my value? Is this like the tags? ('0') 1 ('/0') ?

Thanks for the help!

Complete_Message[(i * 8) + u] = '0' +  bitRead(rxBuf[i], Bit - 1);

The '0' is shifting all the zeros and ones to 48's and 49's so they are recognized as character 0 and character 1.

'0' means character 0 which is ascii 48. The line could have been written

Complete_Message[(i * 8) + u] = 48 +  bitRead(rxBuf[i], Bit - 1);

With the data finally in a null terminated character array, you can use the getField() function from @wildbill.

char CanMessage[64] = "010101011100010100101110101110111001010101010101010111100100101";

void setup()
{
  Serial.begin(115200);
  Serial.println("Example...");
  GetField(CanMessage, 0, 16);
  GetField(CanMessage, 16, 3);
  GetField(CanMessage, 19, 8);
}

void loop()
{
}

long GetField(char* Message, int start, int len)
{
  char buf[17];
  long number = 0;
  memmove(buf, &Message[start], len);
  buf[len] = 0;
  number = strtol(buf, NULL, 2);
  Serial.print("Binary: ");
  Serial.print(buf);
  Serial.print(" Decimal: ");
  Serial.println(number);
  return number;
}

Thanks. I missed the fact that it wasn't in usable form.

Edit: Grammar