Go Down

Topic: Not sure how to fill char array (Read 169 times) previous topic - next topic

MaxG

Aug 21, 2019, 01:23 pm Last Edit: Aug 21, 2019, 01:27 pm by MaxG
I am reading data from a CAN bus.
The following function is an example of how I convert data read into its values. This works when I Serial.print the numbers, but when I put them into a char array I end up with garbage.
What am I doing wrong?

Code: [Select]

void operatingStatus() {
  /*
    Byte 0 Error code Status code (bit 7-3 = error code; bit 2-0 = status code)
      Error:
         0 No error             |
         1 Corrupt settings     | Invalid settings value detected in memory
         2 Overcurrent warning  | Current has exceeded the warning threshold
         3 Overcurrent shutdown | Current has exceeded fault threshold (drive shut down)
         4 Low cell warning     | One or more cells below minimum voltage threshold
         5 BMS shutdown         | Vehicle shutdown due to undervoltage cell for 10+ seconds
         6 High cell warning    | One or more cells above maximum voltage threshold
         7 BMS ended charge     | Charger has been stopped due to overvoltage cell for >1 sec
         8 BMS over-temp        | The temperature has exceeded upper limit
         9 BMS under-temp       | The temperature is below lower limit
        10 Low SoC warning      | Battery state of charge has passed the low warning level
        11 Reserved             |
        12 Reserved             |
        13 Reserved             |
        14 Reserved             |
        15 Reserved             |
        16 CAN error            | A CAN communications error was detected

      Status:
        0 Idle
        1 Reserved
        2 Running
        3 Reserved
        4 Stopped
        5 Reserved

    Byte 1 Battery amp-hours remaining, 0.1Ah resolution, high byte
    Byte 2 Battery amp-hours remaining, 0.1Ah resolution, low byte
    Byte 3 Battery voltage, 0.1V resolution, high byte
    Byte 4 Battery voltage, 0.1V resolution, low byte
    Byte 5 Reserved
    Byte 6 Reserved
    Byte 7 Temperature (˚C+40)
  */
  long ampHrsRemaining = 0;
  long batteryVoltage = 0;
  int batteryTemperature = 0;
  int errorCode = 0;
  int statusCode = 0;

  // 0.1V resolution, e.g.  100.0 = 1000 = 4
  ampHrsRemaining  = (long)receiveBuffer[1] << 8;
  ampHrsRemaining += (long)receiveBuffer[2];

  // 0.1V resolution
  batteryVoltage   = (long)receiveBuffer[3] << 8;
  batteryVoltage  += (long)receiveBuffer[4];

  errorCode  = (receiveBuffer[0] >> 3);
  statusCode = (receiveBuffer[0] << 5) / (int)pow(2, 5);

  batteryTemperature = receiveBuffer[7];

  #ifdef DEBUG
    // write decoded data
    Serial.print(receiveTime);
    Serial.print("\t\t");
    Serial.print(packetId, DEC);

    // Error
    Serial.print("\t");
    Serial.print("E: ");
    Serial.print(errorCode);

    // Status
    Serial.print("\t");
    Serial.print("S: ");
    Serial.print(statusCode);

    // Ampere hours remaining
    Serial.print("\t");
    Serial.print("Ah: ");
    Serial.print(ampHrsRemaining);

    // Battery Voltage
    Serial.print("\t");
    Serial.print("V: ");
    Serial.print(batteryVoltage);

    // Battery temperature
    Serial.print("\t");
    Serial.print("T: ");
    Serial.print(batteryTemperature);

    Serial.println();
  #endif

  char buffer[22];                                // 3 1 1 1 1 1 3 1 5 3 \0
  buffer[0] = '\0';                               // initialise buffer properly

  // https://linux.die.net/man/3/strncat
  strncat(buffer, (char*)packetId, sizeof(packetId));                     // 3 byte
  strncat(buffer, ",", 1);                                                // 1 byte
  strncat(buffer, (char*)statusCode, sizeof(statusCode));                 // 1 byte
  strncat(buffer, ",", 1);                                                // 1 byte
  strncat(buffer, (char*)errorCode, sizeof(errorCode));                   // 1 byte
  strncat(buffer, ",", 1);                                                // 1 byte
  strncat(buffer, (char*)batteryVoltage, sizeof(batteryVoltage));         // 3 byte
  strncat(buffer, ",", 1);                                                // 1 byte
  strncat(buffer, (char*)ampHrsRemaining, sizeof(ampHrsRemaining));       // 5 byte
  strncat(buffer, ",", 1);                                                // 1 byte
  strncat(buffer, (char*)batteryTemperature, sizeof(batteryTemperature)); // 3 byte

  // check what's in the buffer
  for (int i = 0; i < sizeof(buffer); i++) {
    Serial.print(buffer[i]);
  }
  Serial.println();

  publishTopicAndMessage(MQTT_PUB_STATUS, buffer);
}
Mainly using UNOs. Everything needs to be defined.

septillion

Shows once again snippets are useless ;)

But I assume all things are int's. When you call Serial.print() with an int the function transforms it to an ascii string. So for example, the int 1234 because the four ascii characters "1234".

If you want to do the same with the the buffer array, use sprintf() or snprintf() if you want to check on NOT overflowing the buffer ;)
Use fricking code tags!!!!
I want x => I would like x, I need help => I would like help, Need fast => Go and pay someone to do the job...

NEW Library to make fading leds a piece of cake
https://github.com/septillion-git/FadeLed

UKHeliBob

Code: [Select]
 strncat(buffer, (char*)packetId, sizeof(packetId));                     // 3 byte
I suspect that you are trying to cast an int to a pointer to a char ?
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

GoForSmoke

#3
Aug 21, 2019, 01:54 pm Last Edit: Aug 21, 2019, 01:56 pm by GoForSmoke
Max, if you are buffering a line of text to print to anything (monitor, SD, wifi) then just stop since you are missing a concept or two.

1) Arduino has a serial output buffer 64 chars long.
2) Serial only sends one char from the buffer at a time and does that one bit at a time. It cannot send a line of text.. whoosh.
3) Arduino is about 1000 times faster than even 115200 baud serial. Your code can overfill the output buffer in much less time than a single char takes to send.

So what you do is forget the extra buffering and print your vars one at a time knowing that they will get sent in order. The receiver only sees a string of uninterrupted text. It's all one line until the end marker (what println() adds).

..............

Code: [Select]

  strncat(buffer, (char*)packetId, sizeof(packetId));                     // 3 byte


Is packetId an int?
Is that int an address to a string?
Because (char*)packetId at worst points to the address packetId would be the value of and at best will not compile.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

johnwasser

To put all of your values into a character buffer to publish, you will need to convert each value into a string using something like itoa() (Integer to ASCII).  You will need a second temporary buffer.

Code: [Select]
  char buffer[22];                                // 3 1 1 1 1 1 3 1 5 3 \0
  buffer[0] = '\0';                               // initialise buffer properly
  char valBuff[10];


  itoa(packetId, valBuff, 10);
  strncat(buffer, valBuff, 3);  // packetId is less than 1000
  strcat(buffer, ",");


  itoa(statusCode, valBuff, 10);
  strncat(buffer, valBuff, 1);  // statusCode is less than 10
  strcat(buffer, ",");


  itoa(errorCode, valBuff, 10);
  strncat(buffer, valBuff, 1);  // errorCode is less than 10
  strcat(buffer, ",");


  itoa(batteryVoltage, valBuff, 10);
  strncat(buffer, valBuff, 3);  // batteryVoltage is less than 1000
  strcat(buffer, ",");
  
  itoa(ampHrsRemaining, valBuff, 10);
  strncat(buffer, valBuff, 5);  // ampHrsRemaining is less than 32768
  strcat(buffer, ",");
  
  itoa(batteryTemperature, valBuff, 10);
  strncat(buffer, valBuff, 3);  // batteryTemperature is less than 1000
  
  // check what's in the buffer
    Serial.println(buffer);


  publishTopicAndMessage(MQTT_PUB_STATUS, buffer);
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

MaxG

#5
Aug 22, 2019, 12:21 pm Last Edit: Aug 23, 2019, 09:58 am by MaxG
Thank you for the replies! :)

@septillion: I thought the 'snippet' was self explanatory... (emphasis on "I thought") :)

@UKHeliBob: I am probably a vandal; the compiler complained about something and putting a star in there removed the error... but most likely produces an undesired result. I acknowledge it is most likely the laziest excuse I have ever given. I have to read up on char and char*, or is it char *... I program once or twice a year, for a few days, then remember most, but not all, getting old (and forgetful too).

@GoForSmoke: thank you for sharing your wisdom; much appreciated.
Also read quite a bit on Nick's site, but some things do not want to stick, or are just above my head.


To put all of your values into a character buffer to publish, you will need to convert each value into a string using something like itoa() (Integer to ASCII).  You will need a second temporary buffer.

Code: [Select]

  char buffer[22];                                // 3 1 1 1 1 1 3 1 5 3 \0
  buffer[0] = '\0';                               // initialise buffer properly
  char valBuff[10];


  itoa(packetId, valBuff, 10);
  strncat(buffer, valBuff, 3);  // packetId is less than 1000
  strcat(buffer, ",");

...

Spot on! Thank you.
I modified your code:

Code: [Select]

  char buffer[22];                                // 3 1 1 1 1 1 3 1 5 3 \0
  buffer[0] = '\0';                               // initialise buffer properly
  char valBuff[10];


  itoa(packetId, valBuff, 10);
  strncat(buffer, valBuff, strlen(valBuff));  // packetId is less than 1000
  strcat(buffer, ",");

...


... since I do not know, whether e.g. packetId has 1, 2, or more digits. Some for all others.

I had sizeof(), which worked too, but did not seem right, as returns the amount of memory allocated to that data type, not the length of the string.


I will try to stick this 'snippet' buffer this and that, into a function, as I need it in other places as well.
Mainly using UNOs. Everything needs to be defined.

septillion

Code: [Select]
strncat(buffer, valBuff, strlen(valBuff));
Using the length of 'valBuff' isn't really useful. This way is the same as:
Code: [Select]
strcat(buffer, valBuff);

Idea of the 'n' part is you can prevent overflow of buffer (and/or copy just a part of 'valBuff').
Code: [Select]
const byte BufferSize = 22;
char buffer[BufferSize];

strncat(buffer, valBuff, BufferSize - strlen(buffer) - 1);
Use fricking code tags!!!!
I want x => I would like x, I need help => I would like help, Need fast => Go and pay someone to do the job...

NEW Library to make fading leds a piece of cake
https://github.com/septillion-git/FadeLed

MaxG

#7
Aug 22, 2019, 01:49 pm Last Edit: Aug 23, 2019, 09:58 am by MaxG
Idea of the 'n' part is you can prevent overflow of buffer (and/or copy just a part of 'valBuff').
I saw this before, and thought what a weird construct.
Now that you pointed it out, I made an effort to understand it.
And it makes sense! :)
Thanks.


I also thought only johnwasser's reply was useful.
But I was wrong.

What GoForSmoke was eluding to with speed of data, is my next problem :)
I have 6 Can IDs I need to read, but they come to fast... masks and filters is one solution, plus a timer to slow things down. But that;s for another post in case I can't solve it.
Mainly using UNOs. Everything needs to be defined.

GoForSmoke

I hope that you don't think that I am Nick Gammon just because I show links to his blogs.

What John Wasser showed is that if you're not serial printing then you need your own buffer. I was the first code fill a buffer just to print it but if it has to go elsewhere as well then buffer it up!

Code: [Select]

  publishTopicAndMessage(MQTT_PUB_STATUS, buffer);


Possibly this isn't the only way to send messages to whatever it sends them. I'm not familiar with the library.



1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

MaxG

I hope that you don't think that I am Nick Gammon just because I show links to his blogs.
...
Possibly this isn't the only way to send messages to whatever it sends them. I'm not familiar with the library.
Yes, I did (think you're Nick; my bad). Updated my posts to prevent perpetuating this perception.

MQTT is an ISO standard publish-subscribe-based messaging protocol. It works on top of the TCP/IP protocol suite. It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth is limited. The publish-subscribe messaging pattern requires a message broker. Wikipedia

Yes, than k you all, and for the helpful posts... it certainly got rid of the garbage I produced before. :)
Mainly using UNOs. Everything needs to be defined.

Go Up