RFID scales project. LCD display garbled intermittently and RFID unresponsive for short times

I am developing a project that scans rfid tags and weights the asset the tag is attached to, outputting relevant data to an LCD and posting to a mySQL server.

I'm getting some unwanted/unexpected behavior and was hoping to get some help in troubleshooting.

While the system runs as expected for some time, there are intermittent problems occurring anywhere from 5 minutes to 30 minutes of run time that lead me to believe there might be some issue with the code and in particular memory allocation (although I'm only guessing). I'm very inexperienced in C so might be approaching dealing with strings and char arrays improperly, particularly when formatting GET and POST requests over and over.

Main issues are;
1.LCD randomly becomes garbled at times (random characters and cursor jumping around. It does recover sometimes if left running, but usually needs a hard reset of the system. (pic below)
2. rfid scanner becomes unresponsive and won't read tags for random period of time (seconds to a few minutes) then goes back to normal, scanning as expected every time a tag is placed in front of the scanner.

I've attached a schematic of the system and a photo of the PCB I made. It might be that my PCB design is poor or improper. Cables to LCD, MFRC522 and load cells is shielded data cable with sheath connected to GND.

Code also shown below. I hope it's a software issue as it will be easier to resolve, but if the hardware needs a significant redesign I will do this as per suggestions.

/*

  Version 2.d
  
  This sketch utilises an ESP32 hooked up to a HX711 with 4 load cells and an MFRC522 RFID scanner to read RFID tags attached to assets.
  Identifying credentials are obtained from a mySQL server using http.GET and the weight of the asset is recorded and trasmitted via http.POST to a local SQL server.

*/

#include <soc/rtc.h>
#include "esp_wifi.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <ESPmDNS.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <MFRC522.h>
#include <HX711.h>

// ===== User Defined Variables =====
const char* localHostname = "nodeboxin" ;        // UDV // The hostname of this device
const char* ssid     = "Apollo" ;                // UDV // Your WiFi network NAME.
const char* password = "xxxxxxxxx" ;          // UDV // Your WiFi network PASSWORD.
const char* regType = "IN" ;                     // UDV // Role if this NODE = "IN" or "OUT"
const char* sqlServer = "raspberrypi" ;          // UDV // Hostname of the SQL Server.
const char* apiKeyValue = "xxxxxxxxxxx" ;    // UDV // apiKey use for posting data.
const char* sqlPass = "xxxxxxxx" ;                // UDV // SQL Server password
IPAddress serverIp(0, 0, 0, 0) ;

// ==== MFRC522 ====
#define SS_PIN 4
#define RST_PIN 5
MFRC522 rfid(SS_PIN, RST_PIN) ;
MFRC522::MIFARE_Key key ;
byte nuidPICC[4] ;                                // array that will store new UID (NUID)
boolean newTag = false ;
int newTagReg ;                                   // when was the tag registered in millis()
boolean tagMatch = false;
const int tagRegThresh = 1000 ;                    // 2 seconds between scans. Don't want to keep scanning while there is weight on the scale.
const char* ver = "2.d";

// ==== HX711 ====
const int LOADCELL_DOUT_PIN = 25 ;
const int LOADCELL_SCK_PIN = 26 ;
HX711 scale ;
float weight ;
char weightChar[4] ;
const int weightThresh = 5 ;                            // UDV //  How many kg on scale before RFID reader fires UP ?
const int calFact = -23040.00; //Assets IN                              // UDV // Calibration factor for the scales.
//const int calFact = -19830.00; //Assets OUT                              // UDV // Calibration factor for the scales.

// ==== LCD ====
int lcdColumns = 16 ;
int lcdRows = 2 ;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows) ;
const int errorDelay = 2500 ; // How long to print Error Messages to LCD
const int printDelay = 1000 ; // How long to print regular Messages to LCD

// Char arrays for storing credentials
char sqlUID[18] ;
char brandID[4] = "UA1" ;
char loadID[10] ;
char boxnum[4] ;
char boxNum[4] ;
char rfidMatchChar[18] ;

// LED pins
const int RLED = 16 ; 
const int GLED = 14 ; 
const int BLED = 13 ; 

void setup() {
  pinMode(RLED, OUTPUT);  digitalWrite(RLED, LOW);
  pinMode(GLED, OUTPUT);  digitalWrite(GLED, LOW);
  pinMode(BLED, OUTPUT);  digitalWrite(BLED, LOW);
  // When setup runs, cycle RGB LED 3x
  for (int i = 0 ; i < 3 ; i++) {
    digitalWrite(BLED, HIGH); delay(200); digitalWrite(BLED, LOW);
    digitalWrite(RLED, HIGH); delay(200); digitalWrite(RLED, LOW);
    digitalWrite(GLED, HIGH); delay(200); digitalWrite(GLED, LOW);
  }

  rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M) ;   // slow ESP32 to 80 mHz to prevent HX711 issues
  Serial.begin(115200) ;
  Serial.println(F("Begin Setup"));

  lcd.init() ;  // Inititalise LCD
  lcd.backlight() ;
  lcd.print(F("Ver: "));
  lcd.print(ver);   // print current sketch version
  delay(printDelay);
  lcd.setCursor(0, 0);
  lcd.print(F("Welcome!        ")) ;
  lcd.setCursor(0, 1) ;
  lcd.print(F("Node role: ")) ; lcd.print(regType) ;
  delay(printDelay) ;
  lcd.clear();

  init_network() ; // connect to wifi and locate IP address of sql server

  // initialise rfid scanner
  SPI.begin() ;
  delay(50) ;
  rfid.PCD_Init() ;                         // Init  MFRC522
  rfid.PCD_SetRegisterBitMask(rfid.RFCfgReg, (0x07<<4));  // set gain to max (48 dB)
  for (byte i = 0; i < 6; i++) {            // Fill key with empty values
    key.keyByte[i] = 0xFF ;
  }

  init_scale() ; // initialize scale

  lcd.clear() ; lcd.print(F("All Services OK!")) ;
  lcd.clear() ;
  //end setup
}

void loop() {
  lcd.home();
  //check wifi connected
  if ( !(WiFi.status() == WL_CONNECTED) ){
    init_network();
  }

  weight = scale.get_units(3) ; // get weight at start of each loop
  if (weight < 0 ){ // if scales report negative value report 0.00
    weight = 0.00;
  }
  
  lcd.setCursor(0, 0);
  if (strlen(boxnum) == 0){ // if this is the first box, ask for asset
    lcd.print(F("Scan Box"));
  }
  else {
    lcd.print(F("Prev BoxID: ")); lcd.print(boxnum);
  }
  
  lcd.setCursor(0, 1) ;
  lcd.print(weight) ;
  lcd.print(F(" kg         ")) ;

  if (int(weight) >= weightThresh ) {                       //if more than X kg on scale, fire up the rfid reader and look for a new tag
    readTag();
    if (!newTag){ // if tag wasn't read exit loop after short delay
      delay(50);
      return;
    }
    if (newTag) { // if we got a new tag from the reader...
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("New Tag Read"));
      RGBVis(3); // flash blue LED 3 times        
      // get matching record from the mySQL Server
      getRfidMatch() ;
      if (tagMatch) { // if the tag exists in db and credentials were obtained...
        splitCredentials(); // split rfidMatchChar to populate box credentials
        weight = scale.get_units(5); // obtain stable weight
        // print box info to lcd
        lcd.clear();
        lcd.setCursor(0, 0) ; lcd.print(F("BOX: ")); lcd.print(loadID); lcd.print(F(" ")); lcd.print(boxnum);
        lcd.setCursor(0,1) ; lcd.print(weight); lcd.print(F(" kg"));
        delay(printDelay);
        lcd.clear();
        lcd.setCursor(0,0); lcd.print(F("Posting DATA")) ;
        postData() ; // Send data to SQL server using POST
      }
      
      else {
        lcd.setCursor(0, 1); lcd.print(F("No Match in DB!!"));
        Serial.println(F("No match for this ID!"));
        delay(printDelay);
        RGBVis(2); // flash LED RED 
        return;
      }
    }
  }
  // Finished with the tag so reset flags.
  newTag = false ;                      
  tagMatch = false ;
}
  

void getRfidMatch() {

  // Empty Credentials
  loadID[0] = '\0' ;
  boxnum[0] = '\0' ;
  boxNum[0] = '\0' ;
  rfidMatchChar[0] = '\0' ;
  tagMatch = false ;

  char GET_URL[150] = "" ;
  append("http://", GET_URL) ;
  char IPChar[16] ;
  serverIp.toString().toCharArray(IPChar, 16) ;
  append(IPChar, GET_URL) ;
  append("/rfid2hiveid_0.3_secure.php?", GET_URL) ;

  // Format GET request
  append2("api_key=", apiKeyValue, GET_URL) ;
  append2("&pass=", sqlPass, GET_URL) ;
  append2("&rfid=", sqlUID, GET_URL) ;

  // *DEBUGGING report what is being sent in GET request
  //  Serial.print(F("GET Request: ")) ;
  //  Serial.println(GET_URL) ;

  // Send formatted GET request
  HTTPClient http ;  // Declare object of class HTTPClient
  boolean httpResponse = false; // flag to check for result
  while (!httpResponse){
    http.begin(GET_URL);
    int httpResponseCode = http.GET() ;
    if (httpResponseCode == 200){
      httpResponse = true;
    }  
  }
    
  String payload = http.getString();    //Get the response payload
  http.end(); // cleanup
  
  if (payload == "0 results") { // if there's no match, report.
    tagMatch = false;
    return;
  }
  else {
    tagMatch = true ; // Sql record returned form server 
    int payload_len = payload.length() + 1; 
    payload.toCharArray(rfidMatchChar, payload_len); // place returned ":" delimeted data into rfidMatchChar char
    Serial.println(rfidMatchChar); // print the matching credentials
  }
}

void postData() {
  boolean dataWrite = false;
  char POST_URL[200] ;
  char php_loc[50] = {0} ;
  dtostrf(weight, 5, 2, weightChar) ;
  append("http://", php_loc) ;
  char IPChar[16] ;
  serverIp.toString().toCharArray(IPChar, 16) ;
  append(IPChar, php_loc) ;

  // Format POST request
  append("/post-extraction-data.php?", php_loc) ;
  append2("api_key=", apiKeyValue, POST_URL) ;
  append2("&regtype=", regType, POST_URL) ;
  append2("&brand=", brandID, POST_URL) ;
  append2("&rfid=", sqlUID, POST_URL) ;
  append2("&loadid=", loadID, POST_URL) ;
  append2("&boxnum=", boxnum, POST_URL) ;
  append2("&weight=", weightChar, POST_URL) ;

  HTTPClient http ;                               //Declare object of class HTTPClient
  while (!dataWrite) {
    http.begin(php_loc) ;
    http.addHeader("Content-Type", "application/x-www-form-urlencoded") ;
    int httpResponseCode = http.POST(POST_URL) ;
    Serial.print(F("Response code from POST request: ")) ; Serial.println(httpResponseCode) ;
    if (httpResponseCode == 200) {
      lcd.setCursor(0, 1) ; lcd.print("DATA WRITE OK!  ") ;
      RGBVis(1);
      delay(printDelay) ;
      dataWrite = true ;
    }
    
    else {
      Serial.println(F("Data Post Error")) ;
      lcd.setCursor(0,1) ; lcd.print(F("DATA WRITE FAIL!")) ;
      RGBVis(2);
      delay(errorDelay) ;
    }
  }
}

void init_network() { // Initialise network interface
  esp_wifi_set_max_tx_power(20);            // Set to low to reduce brown-out issues
  lcd.clear(); lcd.setCursor(0, 0) ; lcd.print(F("Join WiFi       ")) ;
  WiFi.setHostname(localHostname) ;         // set hostname
  WiFi.begin(ssid, password) ;
  while (WiFi.status() != WL_CONNECTED) {
    delay(50) ;
    Serial.print(F(". ")) ;
  }
  lcd.setCursor(14, 0); lcd.print(F("OK"));
  
  // Start the mDNS responder for "nodeboxin.local"
  while (!MDNS.begin(localHostname)) {
    Serial.println(F("mDNS fail!")) ;
    delay(500);
  }
  
  // Resolve the IP Address for the mySQL Server
  serverIp = MDNS.queryHost(sqlServer) ;
  lcd.setCursor(0, 1) ;  lcd.print(F("Locate Server   "));
  while (serverIp.toString() == "0.0.0.0") {
    Serial.println(F("Can't find sqlServer")) ;
    Serial.println(F("Retrying")) ;
    delay(50) ;
    serverIp = MDNS.queryHost(sqlServer) ;
  }
  lcd.setCursor(14, 1); lcd.print(F("OK")); 
  delay(printDelay);
  Serial.print(F("Server IP: ")) ;
  Serial.println(serverIp.toString()) ;
}

void init_scale() {
  Serial.println(F("Initializing Scale"));
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN) ;
  scale.set_scale(calFact);
  lcd.clear() ;
  lcd.print(F("Tare function.")) ;
  lcd.setCursor(0, 1) ;
  lcd.print(F("Empty Scale:    ")) ;
  for (int i = 5; i > 0; i--) {
    lcd.setCursor(13, 1) ;
    lcd.print(i) ;
    delay(200) ;
  }
  scale.tare() ;                // reset the scale to 0
}


void readTag() { // scans for a new tag. 
  
  if ( ! rfid.PICC_IsNewCardPresent()) // Reset the loop if no new card present
    return ;
  if ( ! rfid.PICC_ReadCardSerial())   // Verify if the NUID has been read
    return ;
  MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak) ;
  if (rfid.uid.uidByte[0] != nuidPICC[0] ||
      rfid.uid.uidByte[1] != nuidPICC[1] ||
      rfid.uid.uidByte[2] != nuidPICC[2] ||
      rfid.uid.uidByte[3] != nuidPICC[3] ) {

    newTag = true ; // Set newTag flag
    Serial.println(F("A new card has been detected.")) ;
    lcd.setCursor(0, 0); lcd.print(F("Tag Registered  "));
      
    for (byte i = 0; i < 4; i++) { // Store NUID into nuidPICC array
      nuidPICC[i] = rfid.uid.uidByte[i] ;
    }
    
    array2delim(nuidPICC, sqlUID) ;  // push uid byte array to delimeted char sqlUID
    Serial.print(F("delimeted UID: "));  Serial.println(sqlUID); // print delimeted ID to the console
  }
  else {
    Serial.println(F("Card read previously.")) ;
    newTag = false;
  }

  // cleanup and exit func
  rfid.PICC_HaltA() ;   // Halt PICC
  rfid.PCD_StopCrypto1() ; // Stop encryption on PCD
}

void array2delim(unsigned char* b, char* s) // converts byte array to delimeted char array
{
  int i = 4 ;
  for (;;) {
    unsigned char f1 = *b % 10 ;
    unsigned char f100 = *b / 10 ;
    unsigned char f10 = f100 % 10 ;
    f100 /= 10 ;
    if (f100) {
      *s++ = '0' + f100 ;
      *s++ = '0' + f10 ;
    } else if (f10)
      *s++ = '0' + f10 ;
    *s++ = '0' + f1 ;
    if (!--i) {
      *s = '\0' ;
      return ;
    }
    *s++ = ':' ; // delimeter
    b++ ;
  }
}

void append (const char *s, char *c) {                  // append string s to char c
  strncat (c, s, strlen(s)) ;
}

void append2 (const char *s, const char *t, char *c) {  // append string s and char t to char c
  strncat (c, s, strlen(s)) ;
  strncat (c, t, strlen(t)) ;
}

void splitCredentials() { // helper func to split rfidMatch payload
  char* ptr = strtok(rfidMatchChar, ":");
  byte i = 0;
  while (ptr) {
    if (i == 0) {
      strcpy(loadID, ptr);
    }
    else if (i == 1) {
      strcpy(boxnum, ptr);
    }
    else if (i == 2) {
      strcpy(boxNum, ptr);
    }
    ptr = strtok(NULL, ":");
    i++;
  }
}


void RGBVis(int t) {
  if (t ==  1) { // data transmitted successfully > Flash Green LED 5x
    for (int i = 0 ; i < 5; i++) {
      digitalWrite(GLED, HIGH);
      delay(100);
      digitalWrite(GLED, LOW);
      delay(100);
    }
  }
  if (t == 2) { // There is an error code. > Flash Red LED 10x
    for (int i = 0 ; i < 10 ; i++) {
      digitalWrite(RLED, HIGH);
      delay(100);
      digitalWrite(RLED, LOW);
      delay(100);
    }
  }

  if (t == 3) { // New Card captured >> flash Blue 3 times
    for (int i = 0 ; i < 3 ; i++) {
      digitalWrite(BLED, HIGH);
      delay(50);
      digitalWrite(BLED, LOW);
      delay(50);
    }
  }
}

--- Schematic ---

--- Garbled Screen ---

--- PCB ---


Does the project actually run after the trouble is seen? I mean, does the controller react on RF id changes?
Complete garbidge like that might be comming from an intermittent connection of any cabel to/from the display.

The first thing I look for when someone has a problem that manifests after a while is use of String objects and sure enough, you have a few in your code. My first attempt at a fix would be to get rid of them.

Hi Railroader,

Yes the system keeps scanning and posting data to the server even when the screen is garbled. Problems with scanning don't seem to concurrent with the screen issues.

Thanks wildbill,

Is there any chance you could give some instruction on one of two instances in the code that may be problematic and suggest an alternative method?

As far as I can see the only two instances where I'm using the String class is
String payload = http.getString()
and
serverIp.toString().toCharArray(IPChar, 16) ;

I'm not sure if there's any way to avoid this?

Thanks

Are you powering the 3.3v ESP32 line with 5v ?

Are those actually 3.3k pull-up resistors on the SDA / SCL lines?

Are your devices (LCD, RFID, Load cell) 3.3v compatible with the ESP32 ? I don't see any logic level shifting anywhere?

@red_car has a good point.
Personally I know that any intermittent contact in Vcc, GND, SCL or SDA creates trouble like this.
Your build looks rather solid so maybe @red_car is on the right track.

Thanks for your reply.

The system is powered with a 5V 2.5A transformer. The 5V+ is only connected to VCC on the ESP32, I2C-LCD board, and the HX711. The ESP32 3.3V line is connected to VDD on the HX711 and the MFRC522.

Unless the pack they came in was mislabelled then yes these should be 3.3k resistors. They are connected between the SDA and SCL lines and 3.3V for both the MFRC522 and I2C-LCD board. The LCD is powered directly through 5V though.. Might be an issue? But I thought as the the data lines are on 3V logic the resistors should be connected to 3.3V

I'm sure the MFRC522 and HX711 are 3.3V compatible. The LCD is just a cheap chinese generic, but AFAIK it should be compatible and I have use the same one for other projects with the ESP32 with no issues.

So is this pin mis-labelled in your schematic?
image

These don't look like 3.3k resistors (although what looks like gold could be orange?)
image

image
image

:face_with_hand_over_mouth:
So sorry, the part in Circuitmaker is mislabelled. This is in fact VIN. I'll repost below in reply.

As for the resistors.. This is a big oversight.. I didn't even think to look at the bands and just blindly trusted what was written on the pack.. I'm off to the electronics store >>> :face_with_diagonal_mouth:

Thanks so much for pointing this out to me. I really apreciate you taking the time to help !! Will report back once the mods have been completed and I've had a chance to run for a while.

Seems pretty stable now.

Thanks for all the help!

1 Like

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