Reading an electronic balance using RS232 and a MAX3232 and displaying the mass on an LCD, along with reading temperature and humidity

Hi there,

I am currently working on a project that will eventually read a mass from an RS232 output to a MAX3232 from a scale (US Solid electronic balance SLSC Series) to an Arduino UNO R3. Based on the mass that the scale outputs, a humidifier and/or a fan/heater will either turn on (or off) using an IoT relay (ac/dc control relay) and will stop when the DHT22 reads a certain temperature or humidity. I have successfully gotten the DHT22 to produce results (both temperature and humidity) on a 20x4 LCD. I have also gotten a fan (dummy test) to turn on based on the humidity controls. This part I have a pretty good understanding how to control with my code. However, I am struggling to produce readable results from the balance (the rs232). I have tried many different things but the reproduced data is showing up as zero whereas the raw data shows as the correct mass. However, the mass will not show up on the LCD with the humidity and temperature results. I am (sort of) new to Arduino but have researched a lot for this project but I just cannot seem to get it to print correctly. Below I have attached photo schematics, the user manual to the scale, and my rough code. Not shown are the humidifier, fan, or the heater. The main concern is being able to read the mass from the scale in a readable format on the LCD. Half of the time the mass shows on the serial monitor correctly, but other times it will just show as zero.

Side note: So far it looks very ugly, but once I figure out how to make it fully work, I will set up the heater and other accessories nicer.










#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11);

#define LCD_ADDRESS 0x27
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

LiquidCrystal_I2C lcd(0x27, 20, 4);

const int controlPin = 3; // Adjust this to the pin connected to the "IN" on the IoT Relay

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  dht.begin();

  lcd.init();
  lcd.begin(20, 4); // Adjust the LCD size to 20 columns and 4 rows
  lcd.backlight();

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(controlPin, OUTPUT);
  digitalWrite(controlPin, LOW); // Ensure the relay is initially in the off state
}

void loop() {
  // Read and display mass from the scale
  while (mySerial.available()) {
    String receivedData = mySerial.readStringUntil('\n'); // Read until newline character

    if (receivedData.length() > 0) {
      Serial.print("Raw Data: ");
      Serial.println(receivedData); // Debugging print

      // Extract numerical part and parse as float
      String numericalPart = receivedData.substring(1, receivedData.length() - 1); // Remove the '+' and 'g'
      float mass = numericalPart.toFloat();

      Serial.print("Parsed Mass: ");
      Serial.println(mass); // Debugging print

      // Read temperature and humidity from DHT sensor
      float temperature = dht.readTemperature();
      float humidity = dht.readHumidity();

      // Display on LCD
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Mass: ");
      lcd.print(mass);
      lcd.print(" g");

      lcd.setCursor(0, 1);
      lcd.print("Temperature: ");
      lcd.print(temperature);
      lcd.print(" C");

      lcd.setCursor(0, 2);
      lcd.print("Humidity: ");
      lcd.print(humidity);
      lcd.print(" %");

      // Control the IoT Relay based on the mass (example: turn on for mass > 100)
      if (mass > 100) {
        digitalWrite(controlPin, HIGH); // Turn on the relay
      } else {
        digitalWrite(controlPin, LOW); // Turn off the relay
      }

      // For debugging, print to serial monitor
      Serial.print("Received mass: ");
      Serial.println(mass);
    }
  }

  // Other tasks can be performed here without a long delay

  delay(5000);
}


I have never used this forum besides reading them so please bear with me.

You need to clearly describe what goes wrong. Very likely, your approach is interpreting empty lines due to confusion about line endings, and failure to identify valid data frames.

As a general rule, solve problems like that with simple code, with just the scale and LCD connected. Do that for each of the sensors and other connected devices, also independently, then add in the other parts, one by one, testing as you go.

Incidentally, you should not use Strings on the Arduino Uno R3 and related processors. Strings cause program crashes due to poor memory management.

To receive serial data using safer and more reliable approaches, follow this tutorial:

Issues reading RS232 data from Digital Scales - Using Arduino / Networking, Protocols, and Devices - Arduino Forum

Hi there,

Sorry for the late response. For the code posted originally, my problem is a glitch on the LCD every new reading. This may be due to the Strings, which you mentioned. So, attempting to restart my code, I tried to follow the tutorial you posted and this is the code I have tried.

#include <Adafruit_Sensor.h>

#include <DHT.h>
#include <DHT_U.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#include <SoftwareSerial.h>

const byte rxPin = 10;
const byte txPin= 11; 

SoftwareSerial mySerial (rxPin, txPin);

const byte numChars = 16;
char receivedChars[numChars];

boolean newData = false;

float number = 0;


#define LCD_ADDRESS 0x27
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

LiquidCrystal_I2C lcd(0x27, 20, 4);


void setup() {

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  Serial.begin(9600);
  mySerial.begin(9600);
 
 dht.begin();

 lcd.init();
 lcd.begin(16,2);
 lcd.backlight();

  pinMode(LED_BUILTIN, OUTPUT);

}

void loop(){

  recvWithEndMarker();
  
  if (newData){
    showNewData();
    number = atof(receivedChars);
    // Serial.print("the number is =");
    // Serial.println(number);
   

    newData = false;
  }
 
 Serial.println(char(mySerial.read()));

  delay(2000);

  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  if (isnan(temperature) || isnan(humidity)){
    Serial.println("Failed to read from DHT sensor!");
    return;

  }
  
  if (temperature > 22.0){
    digitalWrite(LED_BUILTIN, HIGH);

  } 

  else {
    digitalWrite(LED_BUILTIN, LOW);

  }
  
  if (humidity > 60.5){
    digitalWrite(LED_BUILTIN, HIGH);

  }
  else {
    digitalWrite(LED_BUILTIN, LOW);

  }
  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temperature);
  lcd.print(" C");

  lcd.setCursor(0,1);
  lcd.print("Humidity: ");
  lcd.print(humidity);
  lcd.print(" %");

  lcd.setCursor(0,2);
  lcd.print("Mass = ");
  lcd.print(receivedChars);
  lcd.print(" g");

  delay(5000);

  
}

void recvWithEndMarker()
{
  static byte ndx = 0;
  char endMarker = '\r';
  char rc;

  while (mySerial.available() > 0 && newData == false)
  {
    rc = mySerial.read();
    if (rc != endMarker)
    {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars)
      {
        ndx = numChars - 1;

      }

    }
    else
    {
      receivedChars[ndx] = '\0';
      ndx = 0;
      newData = true;
    }
  }
}

void showNewData()
{
  Serial.print(" Mass = ");
  Serial.println(receivedChars);
  
}



However, now the LCD and the monitor are printing out extra spaces/extra + every so often. Attached are photos of the LCD and the Serial.
image



Please help me understand how/where in my code I can fix that issue and/or fix the glitch in the my original posted code.

Attached below is the LCD from my original code. It shows what I want the LCD to look like but it glitches and maybe it is because of the string. The original code seems to produce better results than the one without a string which is where I am lost.

You are printing out the variable "receivedChars" without removing extraneous bits of data or checking the validity of the contents.

The page you posted above shows this output from the scale:
image

My guess is that the most interesting parts are the sign, and the contents of bytes 3-9. So a valid data frame will start with the character '+' or '-', and that needs to be decoded, then the contents of bytes 3-9 (numbering from 1) printed or decoded.

Try something like this (untested):

else
    {
     if (receivedChars[0] == '+' or receivedChars[0]== '-' ) {
     // valid frame, terminate at byte 10 counting from 1, or 9 counting from zero
      receivedChars[9] = '\0';
      ndx = 0;
      newData = true;
    }

If that works, you can convert the data to a float variable as follows (untested);

else
    {
     if (receivedChars[0] == '+' or receivedChars[0]== '-' ) {
     // valid frame, terminate at byte 10 counting from 1, or 9 counting from zero
      receivedChars[9] = '\0';
      float value = atof(&receivedChars[2]); //ignore sign and blank, in case atof() complains
      if (receivedChars[0] == '-') value = -value; //change sign
      Serial.print(" converted value = ");
      Serial.println(value);
      ndx = 0;
      newData = true;
    }

Finally, it would be good to know what bytes 10 to 13 contain, in particular, what is "END"?
Presumably "units" would be kg, lb, oz, g, etc. and you should take appropriate action for each.
You can determine the contents of those bytes by printing them out using hexadecimal notation.


Serial.print("END byte in hex notation: ");
Serial.println(receivedChars[12],HEX);

Hi!

The additional codes produced this:


and the serial monitor only showed

I may have put them in the wrong spot but I replaced the else statement with the example you gave. I assume that is the correct spot but maybe it is not since is produced such bizarre results.

void recvWithEndMarker()
{
  static byte ndx = 0;
  char endMarker = '\r';
  char rc;

  while (mySerial.available() > 0 && newData == false)
  {
    rc = mySerial.read();
    if (rc != endMarker)
    {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars)
      {
        ndx = numChars - 1;

      }

    }
    else
    {
      receivedChars[ndx] = '\0';
      ndx = 0;
      newData = true;
    }
  }
}

Additionally, the bytes printed out like this
Screenshot 2024-02-06 151645

Thank you so much for your help, I have been stuck on this for a couple weeks now and now I feel like I can make progress.

Get rid of the delay(2000) and delay(5000), those are going to lead to the receive buffer overflowing.
Instead, use a combination of millis() timing and only updating the display when the data changes.

Also get rid of this line in loop(), it reads a character from the scale data and throws it away, corrupting whatever is currently being received.

  Serial.println(char(mySerial.read()));

The atof() conversion is also not going to work, although you do not appear to be using the result in the code. The space after the sign is a problem, the atof() function will not allow whitespace characters (space, tab, etc) within the number, or between the sign and the number. One way around this is to convert the number without the sign, then test for a positive/negative sign yourself.

void loop() {
  recvWithEndMarker();
  if (newData) {
    showNewData();
    number = atof(&receivedChars[1]);
    if (receivedChars[0] == '-'){
      number = -number;
    }
    // Serial.print("the number is =");
    // Serial.println(number);
    newData = false;
  }

The code you posted does not seem to show any changes. Post the complete code you are actually using.

From post #7:

The space after the sign is a problem, the atof() function will not allow whitespace characters (space, tab, etc) within the number

My code, posted in reply #5, specifically skips those characters, with a comment explaining why.

    float value = atof(&receivedChars[2]); //ignore sign and blank, in case atof() complains

This is the full code which is giving me errors still.

#include <Adafruit_Sensor.h>

#include <DHT.h>
#include <DHT_U.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#include <SoftwareSerial.h>

const byte rxPin = 10;
const byte txPin= 11; 

SoftwareSerial mySerial (rxPin, txPin);

const byte numChars = 16;
char receivedChars[numChars];

boolean newData = false;

float number = 0;


#define LCD_ADDRESS 0x27
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

LiquidCrystal_I2C lcd(0x27, 20, 4);


void setup() {

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  Serial.begin(9600);
  mySerial.begin(9600);
 
 dht.begin();

 lcd.init();
 lcd.begin(16,2);
 lcd.backlight();

  pinMode(LED_BUILTIN, OUTPUT);

}

void loop(){

  recvWithEndMarker();
  
  if (newData){
    showNewData();
    number = atof(receivedChars);
    // Serial.print("the number is =");
    // Serial.println(number);
   

    newData = false;
  }
 
 Serial.println(char(mySerial.read()));

  delay(2000);

  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  if (isnan(temperature) || isnan(humidity)){
    Serial.println("Failed to read from DHT sensor!");
    return;

  }
  
  if (temperature > 22.0){
    digitalWrite(LED_BUILTIN, HIGH);

  } 

  else {
    digitalWrite(LED_BUILTIN, LOW);

  }
  
  if (humidity > 60.5){
    digitalWrite(LED_BUILTIN, HIGH);

  }
  else {
    digitalWrite(LED_BUILTIN, LOW);

  }
  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temperature);
  lcd.print(" C");

  lcd.setCursor(0,1);
  lcd.print("Humidity: ");
  lcd.print(humidity);
  lcd.print(" %");

  lcd.setCursor(0,2);
  lcd.print("Mass = ");
  lcd.print(receivedChars);
  lcd.print(" g");

  delay(5000);

  
}

void recvWithEndMarker()
{
  static byte ndx = 0;
  char endMarker = '\r';
  char rc;

  while (mySerial.available() > 0 && newData == false)
  {
    rc = mySerial.read();
    if (rc != endMarker)
    {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars)
      {
        ndx = numChars - 1;

      }

    }
    else
    {
     if (receivedChars[0] == '+' or receivedChars[0]== '-' ) {
     // valid frame, terminate at byte 10 counting from 1, or 9 counting from zero
      receivedChars[9] = '\0';
      float value = atof(&receivedChars[2]); //ignore sign and blank, in case atof() complains
      if (receivedChars[0] == '-') value = -value; //change sign
      Serial.print(" converted value = ");
      Serial.println(value);
      ndx = 0;
      newData = true;
    }
  
  }
}

void showNewData()
{
  Serial.print(" Mass = ");
  Serial.println(receivedChars);
  Serial.print('End byte in hex notation: ');
  Serial.println(receivedChars[12], HEX);
}

Try this. I rearranged the most important parts, following the recommendation of the Serial Input Basics tutorial. The signs +/- are start markers.

The result returned by the serial input, "value", is global and accessible to all functions.

I advise you to determine what the three characters Unit1, Unit2 and Unit3 represent, by printing out their values in hexadecimal notation.

There are still problems with the code, like the handling of receivedChars buffer overflow, and the delay(5000).

Not tested!

#include <Adafruit_Sensor.h>

#include <DHT.h>
#include <DHT_U.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#include <SoftwareSerial.h>

const byte rxPin = 10;
const byte txPin = 11;

SoftwareSerial mySerial (rxPin, txPin);

const byte numChars = 16;
char receivedChars[numChars];

boolean newData = false;

float number = 0;
float value = 0;


#define LCD_ADDRESS 0x27
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

LiquidCrystal_I2C lcd(0x27, 20, 4);


void setup() {

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  Serial.begin(9600);
  mySerial.begin(9600);

  dht.begin();

  lcd.init();
  lcd.begin(16, 2);
  lcd.backlight();

  pinMode(LED_BUILTIN, OUTPUT);

}

void loop() {

  recvWithEndMarker();

  if (newData) {
    showNewData();
    newData = false;
  }

  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Failed to read from DHT sensor!");
    return;

  }

  if (temperature > 22.0) {
    digitalWrite(LED_BUILTIN, HIGH);

  }

  else {
    digitalWrite(LED_BUILTIN, LOW);

  }

  if (humidity > 60.5) {
    digitalWrite(LED_BUILTIN, HIGH);

  }
  else {
    digitalWrite(LED_BUILTIN, LOW);

  }

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(temperature);
  lcd.print(" C");

  lcd.setCursor(0, 1);
  lcd.print("Humidity: ");
  lcd.print(humidity);
  lcd.print(" %");

  lcd.setCursor(0, 2);
  lcd.print("Mass = ");
  lcd.print(value);
  //lcd.print(" g");  //this would depend on scale setting

  delay(5000);   // NOT a good idea


}

void recvWithEndMarker()
{
  static byte ndx = 0;
  char endMarker = '\r';
  char rc;

  while (mySerial.available() > 0 && newData == false)
  {
    rc = mySerial.read();

    if (rc == '+' or rc == '-' )
    { //valid start character received, start collecting bytes
      indx = 0;
    }

    if (rc != endMarker)
    {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars)    // NOTE: if this happens, the frame is invalid and will not be interpreted correctly.
      {
        ndx = numChars - 1;
      }

    }
    else  //endMarker detected
    {
      // valid frame, terminate at byte 10 counting from 1, or 9 counting from zero
      receivedChars[9] = '\0';
      value = atof(&receivedChars[2]); //ignore sign and blank, in case atof() complains
      if (receivedChars[0] == '-') value = -value; //change sign
      ndx = 0;
      newData = true;
    }

  }
}

void showNewData()
{
  Serial.print(" Mass = ");
  Serial.println(value);
  Serial.print(" End byte in hex notation: ");
  Serial.println(receivedChars[12], HEX);
}

What if you send the data to an FTDI-based UART and look at that COM port with TeraTerm ?

Hi there,

The code worked wonderfully. However, if I get rid of the delay, the data comes in so quickly that the LCD does not know what to do. I am also unfamiliar with millis() but maybe that will fix that issue. To add, the mass is printing out as only 2 decimals and it should have 3. The hex values that is printed with "12" is 20 and with the others it was outputting 1488037.

Most people update the LCD only when some data have changed. Keep track of the "old" value, and if the new value is different, print that on the display and update the "old" value.

Avoid clearing the screen and printing everything anew. Examples and tutorials showing various methods are posted on line.

.print() takes a second argument to control the number of digits past the decimal point.

Serial.print(value,3);

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.