How to convert client.print statements to a char array or message buffer?

Hello,

I have several client.print statements that I'd like to do a CRC8 calculation on. I'm thinking I should change those print statements to a char array which can then be processed by a CRC8 function and I also still want to transmit the char array with the client object.

What's the best way to build a char array message buffer?

I have several client.print statements that I'd like to do a CRC8 calculation on.

That doesn't make sense. Doing the CRC8 calculation on the data being printed does, sort of.

What's the best way to build a char array message buffer?

const byte howBigItNeedsToBe = someValue;

char message[howBigItNeedsToBe];

PaulS:
That doesn't make sense. Doing the CRC8 calculation on the data being printed does, sort of.

Yes, the data being printed.

Maybe another way of asking is:

How do you recommend I rewrite the following code to allow for CRC checking with the OneWire library's CRC function?

void sendTempSensorData(Print *destination, boolean debug){
  if (debug) destination->print(F("\r\n"));
  
  destination->print(F("ESPID:"));
  destination->print(ESP.getChipId());
  destination->print(F(",RSSI:"));
  destination->print(WiFi.RSSI());
  destination->print(",VCC:");
  destination->print(int(float(ESP.getVcc()) / 9.2857));
  destination->print(F(",tempupdate:"));
  if (tempSensorCount > 0){
    for (byte i=0;i<tempSensorCount;i++){
      destination->print(tempSensorReadings[i].bin);
      destination->print(F(";"));
      destination->print(tempSensorReadings[i].sensor);
      destination->print(F(";"));
      destination->print(tempSensorReadings[i].tempC);
      if (i < tempSensorCount-1) destination->print(F(":"));
    }
  } else {
    destination->print(F("nosensors"));
  }
  destination->print(F(",CRC:"));
  byte data[] = {'E','S','P','I','D'};  // test data
  destination->print(OneWire::crc8(data,5));

  destination->print(F("\n"));
  if (debug) destination->print(F("\r"));
  delay(50);
  
}

From OneWire.cpp

#if ONEWIRE_CRC
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
//

#if ONEWIRE_CRC8_TABLE
// This table comes from Dallas sample code where it is freely reusable,
// though Copyright (C) 2000 Dallas Semiconductor Corporation
static const uint8_t PROGMEM dscrc_table[] = {
      0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
    157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
     35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
    190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
     70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
    219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
    101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
    248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
    140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
     17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
    175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
     50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
    202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
     87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
    233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
    116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};

//
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
// and the registers.  (note: this might better be done without to
// table, it would probably be smaller and certainly fast enough
// compared to all those delayMicrosecond() calls.  But I got
// confused, so I use this table from the examples.)
//
uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
{
 uint8_t crc = 0;

 while (len--) {
 crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
 }
 return crc;
}
#else
//
// Compute a Dallas Semiconductor 8 bit CRC directly.
// this is much slower, but much smaller, than the lookup table.
//
uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
{
 uint8_t crc = 0;

 while (len--) {
#if defined(__AVR__)
 crc = _crc_ibutton_update(crc, *addr++);
#else
 uint8_t inbyte = *addr++;
 for (uint8_t i = 8; i; i--) {
 uint8_t mix = (crc ^ inbyte) & 0x01;
 crc >>= 1;
 if (mix) crc ^= 0x8C;
 inbyte >>= 1;
 }
#endif
 }
 return crc;
}
#endif

After much discussion, this post shows how to derive a new class from EthernetClient that performs a checksum on everything written. To use it, you declare a variable of your new class instead of EthernetClient.

Then you reset the CS at some point, send the special data, and then ask for the checksum. It always performs the CS calculation on every byte written.

To avoid performing a CRC on everything, I would suggest adding a bool doCRC to your class that is:

  • set when you reset the CRC,

  • checked when ::write is called and performs the CRC if the flag is set,

  • cleared when you ask for the current CRC value.

Initialize it to false, and write won't waste any time calculating a CRC until you call ResetCRC.

Is that enough detail?

Cheers,
/dev

-dev:
After much discussion, this post shows how to derive a new class from EthernetClient that performs a checksum on everything written. To use it, you declare a variable of your new class instead of EthernetClient.

Then you reset the CS at some point, send the special data, and then ask for the checksum. It always performs the CS calculation on every byte written.

To avoid performing a CRC on everything, I would suggest adding a bool doCRC to your class that is:

  • set when you reset the CRC,

  • checked when ::write is called and performs the CRC if the flag is set,

  • cleared when you ask for the current CRC value.

Initialize it to false, and write won't waste any time calculating a CRC until you call ResetCRC.

Is that enough detail?

Cheers,
/dev

Thanks, this gives me an idea of how to go about it. I'm having difficulty finding the best place to write my CRC calculating code. I'm using the Client object in the ESP WiFiClient library, which appears to use the Print library's write function. I'm finding it hard to follow where the data is actually sent. With the write function being overloaded by multiple libraries, I'm not sure placing my CRC code in the WiFiClient library will be the correct spot yet.

Client.h in Arduino15\packages\esp8266\hardware\esp8266\2.2.0\libraries

using Print::write;

I know only enough to be dangerous, a little more guidance would be appreciated.

With the write function being overloaded by multiple libraries, I'm not sure placing my CRC code in the WiFiClient library will be the correct spot yet.

Unless you want Serial.print(), EthernetServer.print(), EthernetClient.print(), etc. to also use your CRC implementation, the only logical place to put it is in the WiFiClient class.

I'm having difficulty finding the best place to write my CRC calculating code.

Put it in your derived class. You will be deriving from the WiFiClient class.

PaulS & m_elias:
the only logical place to put it is in the WiFiClient
❝ I'm not sure placing my CRC code in the WiFiClient library will be the correct spot yet.

Putting it in WiFiClient is not the correct place. You will derive a class from WiFiClient:

    class WiFiClientCRC8 : public WiFiClient

It will only have the things you want to add to WiFiClient. It "inherits" everything else from the base class, WiFiClient. Then, instead of declaring a client variable of type WiFiClient, use your type:

    WiFiClientCRC8 client;

Your derived class will pretty short, because you're not adding very much to the base class. It will look something like the ExtEthernetClient in the linked post above.

I'm finding it hard to follow where the data is actually sent.

Yes, print methods use the write methods, and the various write methods will usually use one method that actually sends something (a byte array or just one byte).

Cheers,
/dev