CRC test, dont work

[upadate, see this post also: Drifting Load cell in a stable environment
Hi

Im trying to learn ow to make crc code to a lora sender and reciever:

code under is a test code that simulates temp readings.

I use a TTGO LoRa 32- Oled
Settings:
image

Serial:

12:43:42.181 -> ets Jun 8 2016 00:22:57
12:43:42.181 ->
12:43:42.181 -> rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
12:43:42.181 -> configsip: 188777542, SPIWP:0xee
12:43:42.181 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
12:43:42.181 -> mode:DIO, clock div:1
12:43:42.181 -> load:0x3fff0030,len:1184
12:43:42.181 -> load:0x40078000,len:13132
12:43:42.181 -> load:0x40080400,len:3036
12:43:42.181 -> entry 0x400805e4

Any idea why this make MCU reboot?

code:

#include <FastCRC.h>

// Function to generate a random temperature reading
float generateTemperature() {
  // Generate a random temperature between 0 and 100 degrees Celsius
  return random(0, 100);
}

// Function to extract the CRC value from the received message
uint32_t extractCRC(String message) {
  int crcIndex = message.lastIndexOf('*');
  String crcString = message.substring(crcIndex + 1);
  return strtoul(crcString.c_str(), NULL, 16);
}

// Function to verify the CRC of the received message
bool verifyCRC(String message, uint32_t receivedCRC) {
  FastCRC32 crc;
  uint32_t calculatedCRC = crc.crc32((const uint8_t*)message.c_str(), message.length() - 1);
  return (calculatedCRC == receivedCRC);
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0)); // Seed the random number generator with an analog input
}

void loop() {
  // Generate a random temperature reading
  float temperature = generateTemperature();

  // Create the LoRa message string
  String messagePayload = "Temperature: " + String(temperature) + " C";

  // Calculate the CRC of the message
  FastCRC32 crc;
  uint32_t calculatedCRC = crc.crc32((const uint8_t*)messagePayload.c_str(), messagePayload.length());

  // Append the CRC to the message
  messagePayload += " *";
  messagePayload += String(calculatedCRC, HEX);

  // Display the message and CRC for debugging purposes
  Serial.print("Message: ");
  Serial.println(messagePayload);
  Serial.print("CRC: ");
  Serial.println(calculatedCRC, HEX);

  // Simulate LoRa transmission and reception
  // ...

  // Receive the LoRa message
  String receivedMessage = ""; // Replace this with your code to receive the LoRa message

  // Extract the received CRC from the message
  uint32_t receivedCRC = extractCRC(receivedMessage);

  // Verify the CRC of the received message
  bool crcMatch = verifyCRC(receivedMessage, receivedCRC);

  if (crcMatch) {
    // CRC verification passed
    // Strip the CRC from the received message
    int crcIndex = receivedMessage.lastIndexOf('*');
    String messagePayload = receivedMessage.substring(0, crcIndex);

    // Process the message payload
    // ...
    Serial.println("Message payload: " + messagePayload);
  } else {
    // CRC verification failed
    // Handle the error or discard the message
    // ...
    Serial.println("CRC verification failed. Discarding message.");
  }

  // Delay or other operations
  delay(5000);
}

Why ?

The LoRa devices already have internal hardware options of applying and checking the CRC of packets.

you call extractCRC() with an empty String

  // Receive the LoRa message
  String receivedMessage = ""; // Replace this with your code to receive the LoRa message

  // Extract the received CRC from the message
  uint32_t receivedCRC = extractCRC(receivedMessage);

and your function assumes there is a valid message including a star '*' character followed by a uint32_t represented in HEX/ASCII... which is not the case.

your verifyCRC() will also fail as you should not take into account the star and what's after


but as @srnet said, why do you want to do that yourself ?....

so something like this should no longer fail and check the CRC correctly as long as the message is correct (I'm not testing for the actual well formed message which you should do, the star might not be there at all if you expect data corruption)

#include <FastCRC.h>

// Function to generate a random temperature reading
float generateTemperature() {
  // Generate a random temperature between 0 and 100 degrees Celsius
  return random(0, 100);
}

// Function to extract the CRC value from the received message
uint32_t extractCRC(String& message) {
  int crcIndex = message.lastIndexOf('*');
  String crcString = message.substring(crcIndex + 1);
  uint32_t crc = strtoul(crcString.c_str(), NULL, 16);
  Serial.print("crcString = "); Serial.println(crcString);
  Serial.print("crcString value = 0x"); Serial.println(crc, HEX);
  return crc;
}

// Function to verify the CRC of the received message
bool verifyCRC(String& message, uint32_t receivedCRC) {
  FastCRC32 crc;
  int crcIndex = message.lastIndexOf('*');

  uint32_t calculatedCRC = crc.crc32((const uint8_t*)message.c_str(), crcIndex - 1);
  return (calculatedCRC == receivedCRC);
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0)); // Seed the random number generator with an analog input
}

void loop() {
  // Generate a random temperature reading
  float temperature = generateTemperature();

  // Create the LoRa message string
  String messagePayload = "Temperature: " + String(temperature) + " C";

  // Calculate the CRC of the message
  FastCRC32 crc;
  uint32_t calculatedCRC = crc.crc32((const uint8_t*)messagePayload.c_str(), messagePayload.length());

  // Append the CRC to the message
  messagePayload += " *";
  messagePayload += String(calculatedCRC, HEX);

  // Display the message and CRC for debugging purposes
  Serial.print("Message: ");
  Serial.println(messagePayload);
  Serial.print("CRC: ");
  Serial.println(calculatedCRC, HEX);

  // Simulate LoRa transmission and reception
  // ...

  // Receive the LoRa message
  String receivedMessage = messagePayload; // Replace this with your code to receive the LoRa message

  // Extract the received CRC from the message
  uint32_t receivedCRC = extractCRC(receivedMessage);

  // Verify the CRC of the received message
  bool crcMatch = verifyCRC(receivedMessage, receivedCRC);

  if (crcMatch) {
    // CRC verification passed
    // Strip the CRC from the received message
    int crcIndex = receivedMessage.lastIndexOf('*');
    String messagePayload = receivedMessage.substring(0, crcIndex);

    // Process the message payload
    // ...
    Serial.println("CRC verification OK. Handling message.");
    Serial.println("Message payload: " + messagePayload);
  } else {
    // CRC verification failed
    // Handle the error or discard the message
    // ...
    Serial.println("CRC verification failed. Discarding message.");
  }

  // Delay or other operations
  delay(5000);
}

(I avoided duplicating the strings in function calls by adding the & to pass the string by reference)

but having your own CRC it's likely not needed.

1 Like

@J-M-L

Thanks!!!

My demo now works!

M

Hi

Can you tell me a bit more?
I did not know this.

Sender is a AI-Thinker module, receiver is this TTGO esp32-OLED

I experience on my already running system that I get values from my scale that is way off.

Load on my scale ca 47 kg, sometimes I get 40.000 kg readings, so on a graphical interface the graph is not readable...

This is why I will try to sort out invalid measures with CRC code.

image

glad if that helped :slight_smile:
have fun

1 Like

you likely got a wrong measure and sent it anyway, it's likely not the data transfer that corrupted the message

That can be, but I have a median calculation of 5 last readings before I send message, so some effort is added to avoid this.

To Do:
I must set up a log on sender, that runs parallel with my lora messages and compare to received values.

Also make an if statement to read if package is coming from my system and not from the Ether.

Read the documentation for the LoRa library you are using.

1 Like

You should detect 'corrupt' values at the transmiter before sending the packet.

they still might be bogus. One way to check it out is that you'll see the CRC is correct for those messages with wrong measures, because the CRC is only there to check the integrity of the message and won't fix an issue when performing the sampling

On the sender side, if you have an idea of the expected range, you could discard outliers

This is what I will do, the check will be before sending.

This is something I have in mind, but this scale (bee hive) is running all year, and weight is changing from 15 kg to 100 kg . So I was hoping to send "only correct" values.

I understand that crc dont help me with wrong values, but it will sort out some noise.

But I must find a way to detect and remove bogus values before I send them.

yeah, but it's unlikely to be 40 000 kg :slight_smile: or to move from 15Kg to 100Kg in One minute

your code should trap those.

also load cells / HX711 (if that's what you are using) are known for not being stable all the time (and drifting effect)

I'm working on it, step 1 is to look into my median calculus, if I can improve it.
Next is to filter, as you say, the worst range errors.

From http://beep.nl (millions developing money from EU) I have seen that they also use HX711, and they don't have this crazy readings presented, so yes, I must up the code.

If you have any tip pleas bring it on :slight_smile:

If you need a bee monitor, i will provide HW for you :wink: .... in return for "filtercode" :open_mouth:

In post from JLCPCB
image

if you post you acquisition code and details on the wiring, we can probably help with the filtering

(a quality power supply helps with stability of the sampling but heat also plays a role)

1 Like

Thanks. I will do that, but now its family time, take a bath, barbeque and some nice in the glass.
Here its SUMMER, cant sit inn all day :slight_smile:

same here :slight_smile:

have fun

1 Like

Hi

as mentioned before, I have some crazy values coming to my receiver system, loads up to 40000 kg and I'm not sure of the origin of these readings.

So If you manage to find my mistakes I'm happy to receive your help!

I use ESP32 WROOM.

Here is my schematic for my monitor, sender side:
image

Here is my code for reading a load cell ( I have condensed it to focus on my load readings and median calculations, also added a random function):

// monoitor

int LoRaIntervall = 10;          // Lora pkg sec
double data = 0;

#include <SPI.h>
#include <Wire.h>               // for å lese I2C, muligens for LoRa
#include <math.h>
#include <algorithm>            // Allows sort function
#include <LoRa.h>
#include "HX711.h"              // This library can be obtained here http://librarymanager/All#Avia_HX711 https://github.com/bogde/HX711
#include <DHT.h>                // DHT11  aidafruit

#define DHTPIN 12                   // Digital pin connected to the DHT sensor Pin 15 can work but DHT must be disconnected during program upload.
#define DHTTYPE DHT11               // DHT 11 eller 22
#define LB2KG  0.45352              // omregningsfaktor
#define CALWEIGHT 25.3              // kjent vekt i kg
#define DEFAULT_CALIFACTOR -9679,0    
#define number_of_digits 3          // Desimaler for vektavlesing

DHT dht(DHTPIN, DHTTYPE);

//Measuring runtime for when if mills kick in, this to delay sending of packets
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;  //slett?

// Function calls
// float medianFilter(float input)   // bør denne også med i calls?
void sendReadings();

// hx711 pins:
#define LOADCELL_DOUT_PIN 21
#define LOADCELL_SCK_PIN 22

HX711 scale;
float calibration_factor = -9669.59;  // testvekt
float currentOffset = -314123.00;
float calYesNo = 0;

//define the pins used by the LoRa transceiver module TTGO OLED ESP32:
#define SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DIO0 26

#define BAND 868E6

int packetID = 0; //packet counter, reset after boot

//Variables to store temperature, humidity, weight and tare switch status, hive name
float temperatureHive           = 0;
float temperatureOut            = 0;
float humidity                  = 0;
float weight                    = 0;
float tareValue                 = 0;
float alarmValue                = 0;           // Nullstilles kun ved å boot senderen
float hiveNr                    = 1;           // Kube nr UNIK verdi

// Define Switches
const int switchAlarm           = 36;     //17;    // select the input pin for the switch   Alarm må være interupt el for å gå ut av sleep

// const int switchReset           = EN;  // this is a physical input connection  high/low  How to make a

int switchTareValue             = 0;     //  variable to store the value coming from the sensor
unsigned long tareStartTime     = 0;  // variable to store the start time

int switchReset                  = 1;    //
int switchResetValue             = 2;
//int switchAlarm                 = 3;    // select the input pin for the switch

int previousState               = LOW;

//Median filtering of weight
float window[5];             // array to hold the sorted values in the window
int indeks = 0;              // indeks of the current value
int medianCount = 0;
float weight_median = 0;


//test
int teller = 1;
const int buttonPressTime = 5000;     // wait time in milliseconds
const int alarmPressTime  = 5000;
bool buttonPressed = false;           // Keep track of whether the button has been pressed

// #####################################################################

void setup() {
  Serial.begin(115200); delay(500);
  Serial.println();
  Serial.println("Starts...");
  //tone(piezoPin, 2000, 500);
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale();  delay(500);


  // for debug purpose
  randomSeed(analogRead(A0));

  //Setup for temperature and humidity functionality
  //dht.begin();

  //Setup for LoRa
  Serial.println("LoRa transivertest utføres");
  SPI.begin(SCK, MISO, MOSI, SS);
  LoRa.setPins(SS, RST, DIO0);

  if (!LoRa.begin(BAND)) {
    Serial.println("LoRa transivertest error!");
    while (1);
  }
  Serial.print("LoRa initisiert!, cal fact: ");
  Serial.println(calibration_factor);
  scale.set_scale(calibration_factor / LB2KG);    // denne omregningen tror jeg er unødig om en kalibrerer uten denne
  delay(1500);


}
// #####################################################################

void loop() {

  unsigned long currentTime = millis();

  if (currentTime - previousTime >= 3000) {   //Change this value to publish at a different interval. 4 pkg pr loop, 60sec/4 pkg = 15 sec
    previousTime = currentTime;
    
    // DEBUG Function to generate a random double number (Kg) within a given range
    weight_median = medianFilter((((double)random(1000000) / 1000000.0) * (65.34 - 44.45) + 44.45));   //
    //Serial.print("Variable weigth_median: ");
    //Serial.println(weight_median);

    Serial.println("'window' after sort: ");
    for (int i = 0; i < 5; i++) {   // display the values after median calculus
      Serial.print(" | ");
      Serial.print(window[i]);
    }
    Serial.println(" |");

    sendReadings();
    Serial.print(" ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ counter: ");
    Serial.println(teller);
    teller = teller + 1;
    delay(500);
  }
}


/**************************************************************************************************/
// FUNCTIONS
float medianFilter(float input) {
  float median = 0.0;

  // add the current value to the window:
  window[indeks] = input;
  //Serial.print("Input inni window array er: ");
  //Serial.println(window[indeks]);
  Serial.print("Variable weigth_median: ");
  Serial.println(weight_median);
  Serial.print("'window' before sort, index: ");
  Serial.println(indeks);
  for (int i = 0; i < 5; i++) {
    Serial.print(" | ");
    Serial.print(window[i]);
  }
  Serial.println(" |");

  // advance to the next position in the array:
  indeks = indeks + 1;

  // if we're at the end of the array, go back to the beginning:
  if (indeks >= 5) {
    indeks = 0;
  }

  // sort the values in the window:
  float* sortedArray = window;
  mySort(sortedArray, 5);

  // the median value is the middle value in the sorted array:
  if (medianCount == 0) {
    median = sortedArray[4];
    medianCount = medianCount + 1;
  }
  else if (medianCount == 1) {
    median = (sortedArray[4] + sortedArray[3]) / 2;
    medianCount = medianCount + 1;
  }
  else if (medianCount == 2) {
    median = sortedArray[3];
    medianCount = medianCount + 1;
  }
  else if (medianCount == 3) {
    median = (sortedArray[3] + sortedArray[2]) / 2;
    medianCount = medianCount + 1;
  }
  else {
    median = sortedArray[2];
  }
  // return the median value:
  //Serial.print("Median inside funksjonen is: ");
  //Serial.println(median);
  return median;
}

/**************************************************************************************************/

void mySort(float* array, int size) {
  for (int i = 0; i < size - 1; i++) {
    for (int j = i + 1; j < size; j++) {
      if (array[i] > array[j]) {
        // swap the elements if they are out of order:
        float temp = array[i];
        array[i] = array[j];
        array[j] = temp;
      }
    }
  }
}

/**************************************************************************************************/

void sendReadings() {
  //Serial.println("function send reading");

  //debug fix value when not connected to DHT sensor
  // temperatureHive = 17.2;
  temperatureOut  = 7.4;
  // humidity = 50.1;


  String LoRaMessage = String(packetID) + "/" + String(temperatureHive) + "&" + String(temperatureOut) + "@"  + String(humidity) + "£" + String(weight_median) + "#" +  String(alarmValue) + "%" + String(hiveNr) + "¤";

  //Send LoRa packet to receiver
  packetID++;  //incremets LoRa packet +1
  LoRa.beginPacket();
  LoRa.print(LoRaMessage);
  LoRa.endPacket();

  // debug

  // Print to serial monitor
  //Serial.println("LoRa pakke sendt");
  //Serial.println();
  // Serial.print  ("Kalibreringsfaktor: "); Serial.println(calibration_factor);
  Serial.println("LoRa msg in air:");
  //Serial.println((String)"Kubenr:             " + hiveNr);
  Serial.println((String)"Weight g (median):  " + weight_median);
  //Serial.println((String)"Temperatur kube:    " + temperatureHive);
  Serial.println((String)"Temperatur out:     " + temperatureOut);
  //Serial.println((String)"Luftfuktighet:      " + humidity);
  //Serial.println((String)"Alarm    1=Alarm:   " + alarmValue);
  delay(10);  // due to missing characters on serial print
  //Serial.print  ("Pakke ID:           "); Serial.println(packetID);
  //Serial.println("Mon versjon:    Sender_ESP32_v50.1");
  //Serial.println("--------------------------------------------");
  //Serial.println("ID  TH    TU     RH%  vekt   alrm kubeNr");                     // for debugging
  //Serial.println(LoRaMessage);                                                    // for debugging
  //Serial.println("============================================");
  Serial.println();

}