0

I Have a Liquid Level Project that Currently uses an Oled display. I have just discovered the existence of the nRF24L01 wireless communication module; and it is exactly what I need to make my project more user friendly.

Eventually I want to have a dashboard that brings all my info together in one place, but for now I just want to figure out how to get my Liquid Level Project transmitting, and receive the data on another Arduino and view it on the Serial monitor. As is all it does is print out "?" in the Serial monitor.

I have this Sketch on the Liquid Level Project Arduino:

#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"
SSD1306AsciiAvrI2c oled;
#define I2C_ADDRESS 0x3C
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>


double SensorAnalogValue;
uint8_t calculation=1;
String Show_value; 

//create an RF24 object
RF24 radio(9, 8);  // CE, CSN

//address through which two modules communicate.
const byte address[6] = "00001";

void setup()
{
  
  Serial.begin(9600);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(CalLite24);
  oled.clear();
  
  radio.begin();
  
  //set the address
  radio.openWritingPipe(address);
  
  //Set module as transmitter
  radio.stopListening();
  
}


void loop()
{

  
  SensorAnalogValue = analogRead(A0) * 5.0 / 1023.0;
  if (SensorAnalogValue < 1.726) {
    calculation=0;
    Show_value="Low";
  }
  if (SensorAnalogValue > 4.04) {
    calculation=0;
    Show_value="High";
  }
  if(calculation!=0){
  //  Serial.println(SensorAnalogValue);
    Show_value=(String)Liquid_Volume(SensorAnalogValue);
  }
  Serial.println(SensorAnalogValue);

oled.setCursor(20, 0);
  oled.print(Show_value);
  oled.print(" L");
  oled.println(" ");
  oled.print(Show_value);
  oled.print("L");
  oled.println(" ");
delay(500);
   oled.clear();
  
  radio.write(&Show_value, sizeof(Show_value));
  delay(1000);
   
  calculation=1;
}
int Liquid_Volume(double Volt) {
  double A =   2.81842;
  double B = -11.00151 ;
  double C =  14.4765 ;
  double D =  28.73579 ;

  return A * Volt * Volt * Volt + B * Volt * Volt + C * Volt + D;
}

And the Receiver has this:

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//create an RF24 object
RF24 radio(9, 8);  // CE, CSN

//address through which two modules communicate.
const byte address[6] = "00001";

void setup()
{
  while (!Serial);
    Serial.begin(9600);
  
  radio.begin();
  
  //set the address
  radio.openReadingPipe(0, address);
  
  //Set module as receiver
  radio.startListening();
}

void loop()
{
  //Read the data if available in buffer
  if (radio.available())
  {
    char Show_value[32] = {0};
    radio.read(&Show_value, sizeof(Show_value));
    Serial.println(Show_value);
  }
}

I think the problem is in:

radio.write(&Show_value, sizeof(Show_value));

I believe "radio.write" is used kinda like Serial.print, or oled.print, But I don't really understand the "sizeof" part, maybe that is the problem?

0

1 Answer 1

1
radio.write(&Show_value, sizeof(Show_value));

I believe "radio.write" is used kinda like Serial.print, or oled.print, But I don't really understand the "sizeof" part, maybe that is the problem?

Sort of. There's kind of two interrelated problems. The major one is that the actual data of a String is not contained with the actual String type itself. It's allocated on the heap. What the String class contains is a pointer to it and some numbers for tracking the allocated size and amount used.

So the first problem is you're not reading the memory that actually contains the text inside the string.

sizeof(Show_value) evaluates to the size in bytes of the String class itself, which is the size of a pointer and a couple of integers, about 6 bytes, and not the size of the content of the string.

So you're sending a pointer (a memory address) that the receiver wouldn't know what to do with, and some information about how much was allocated and used and nothing of the String content.

Try:

radio.write(Show_value.c_str(), Show_value.length());

.c_str() gets a pointer to the actual string content itself. And .length() is the actual size of the used part of the String's allocated area on the heap.

That said, it's better to avoid String where you reasonably can.


Regarding:

Does this also mean I need to replace radio.read(&Show_value, sizeof(Show_value));

It would. Doing that is a bit more complicated since .c_str() yields const char *; String isn't designed to make write-access to its underlying data as easy.

Without hacking your way around String's expectations, that would require using a temporary buffer. Something like:

char buffer[33] = "";  // Note: this will actually fill the entire buffer with '\0', which some find a surprise.
// The idea in the above is to accommodate the maximum RH24L01 packet size 32 bytes plus room for a null terminator.

radio.read(buffer, sizeof buffer - 1);  // We're only making the 32 byte chunk available to the .read() call, and leaving the last byte to it's '\0' value to force null termination in any case.

String s = buffer;

This assuming that you're either filling the packet with bytes, or including the null terminator in the packet, or both. If you're using a dynamic payload size, rather than initializing the array with = "";, it could be left uninitialized and the received packet size could be used to place the null terminator at the location needed.

You could wrap this in a function if you plan on doing it a lot. But again, it's probably better if you can just use the array and not work with String.

3
  • Thanks timemage, Does this also mean I need to replace radio.read(&Show_value, sizeof(Show_value)); With: radio.read(Show_value.c_str(), Show_value.length()); In the receive sketch? Commented Apr 30, 2021 at 3:47
  • I've updated to try to address that, which is more or less "yes" to the first part, "no" to the second. I'm going to be done for a bit, so it's written hastily, but it should give some shape to the answer. Your second part to the above comment didn't exist when I started updating the question.
    – timemage
    Commented Apr 30, 2021 at 4:09
  • I missed the fact that you're more or less doing the above already with respect to having a buffer and filling it, just not adding the extra byte room or assigning it to a String. If the only thing you're doing with the string is sending it out through Serial, you can switch from println() to using write() with the size argument set to the correct number, in which case you don't need to terminate at all.
    – timemage
    Commented Apr 30, 2021 at 4:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.