Hi All,
I'v cobbled together a scale connected to an rfid scanner. The basic premise of the application is I have a heap of boxes with rfid tags (mifare) attached and I want to weigh them intermittently, get their ID using the rfid tag and then push their weight and ID to a mysql database using http POST. At the same time, the weight and box info gets displayed on an LCD
The program works fine for the first few scans and then the LCD screen output becomes garbeld, with random characters jumping all over the place. Serial output confirms the program is still running as expected, however it does become unresponsive at times which corresponds to but not always concomitant with LCD output being garbled.
Each component works appropriately when using basic sketches without changing wiring, but when I try to run everything together I can't get a stable result. The project is powered via usb for now, but I get the same result when powered by a 5V/2A power supply with + to ESP32-VIN and LCD-VCC with a 1000uF cap bridging supply.
I don't have any experience in troubleshooting something like this. Would really appreciate some guidance on how to proceed.
Schematic and code below.
Thanks.
/*
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 honey supers.
The weight of the honey super 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>
// ===== Network credentials and Config =====
const char* localHostname = "nodeboxout" ; // UDV // The hostname of this device
const char* ssid = "Apollo" ; // UDV // Your WiFi network NAME.
const char* password = "XXXXXXXX" ; // UDV // Your WiFi network PASSWORD.
const char* regType = "OUT" ; // UDV // Role if this NODE = "IN" or "OUT"
const char* sqlServer = "raspberrypi" ; // UDV // Hostname of the SQL Server.
const char* apiKeyValue = "SdnSO87hjJYS9S8" ; // 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 = 2000 ; // 2 seconds between scans. Don't want to keep scanning while there is weight on the scale.
const char* ver = "beta2.5_DOS";
const char* blank = " "; // 16 ch empty char for clearing lcd
// ==== HX711 ====
const int LOADCELL_DOUT_PIN = 25 ;
const int LOADCELL_SCK_PIN = 26 ;
HX711 scale ;
float weight ;
char weightChar[4] ;
const int weightThresh = 7.5 ; // UDV // How many kg on scale before RFID reader fires UP ?
const int calFact = -20810.00; // UDV // Calibration factor for the scales.
// ==== LCD ====
int lcdColumns = 16 ;
int lcdRows = 2 ;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows) ;
const int errorDelay = 5000 ; // How long to print Error Messages to LCD
const int printDelay = 3000 ; // How long to print regular Messages to LCD
// Char arrays for storing credentials
char sqlUID[18] ;
char brandID[4] = "UA1" ;
char loadID[10] ;
char hiveNum[4] ;
char boxNum[4] ;
char rfidMatchChar[18] ;
void setup() {
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(ver); // print current sketch version
delay(printDelay);
lcd.setCursor(0, 0);
lcd.print("Welcome! ") ;
lcd.setCursor(0, 1) ;
lcd.print("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
for (byte i = 0; i < 6; i++) { // Fill key with empty values
key.keyByte[i] = 0xFF ;
}
init_scale() ; // initialize load cells
lcd.clear() ; lcd.print("All Services OK!") ;
lcd.clear() ;
Serial.println(F("End Setup"));
//end setup
}
void loop() {
// commented for testing in isolation from load cells
//weight = scale.get_units(5) ;
weight = 51.11 ;
lcd.setCursor(0, 0);
lcd.print("Scan Box");
lcd.setCursor(0, 1) ;
lcd.print(weight) ;
lcd.print(" kg ") ;
if (int(weight) >= weightThresh ) { //if more than X kg on scale, fire up the rfid reader and look for a new tag
if (!newTag && millis() - newTagReg > tagRegThresh ) { // cond. No new tag and X(tagRegThresh) seconds has passed since last tag reg
delay(5);
readTag() ;
if (newTag) { // if we got a new tag from the reader...
// get matching record from the LAMP Server
Serial.println(F("Matching sqlUID: ")) ;
getRfidMatch() ;
if (tagMatch) { // if the tag exists in db and credentials were obtained...
splitCredentials(); // split rfidMatchChar to populate box credentials
// print box info to lcd
lcd.clear();
lcd.setCursor(0, 0) ; lcd.print(F("BOX: ")); lcd.print(loadID); lcd.print(F(" ")); lcd.print(hiveNum);
//report to Serial Monitor too
Serial.print(F("BOX: ")); Serial.print(loadID); Serial.print(F(" ")); Serial.println(hiveNum);
// weight = scale.get_units(10) ; // get weight again this time average of 10 reads
lcd.setCursor(0, 1) ;
lcd.print("Posting DATA") ;
postData() ; // Send data to SQL server using POST
delay(printDelay);
}
}
}
}
newTag = false ; // Finished with the tag so reset flags.
tagMatch = false ;
}
void getRfidMatch() {
// Empty Credentials
loadID[0] = '\0' ;
hiveNum[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
http.begin(GET_URL);
int httpResponseCode = http.GET() ;
String payload = http.getString(); //Get the response payload
if (payload == "0 results") { // if there's no match, report and bail
lcd.setCursor(0, 1); lcd.print("No Match in DB!!");
Serial.println(F("No match for this ID!"));
delay(printDelay);
lcd.clear();
return ;
}
// If sql reqcord returned form server
tagMatch = true ;
// assign variable values from payload
int payload_len = payload.length() + 1;
payload.toCharArray(rfidMatchChar, payload_len); // place returned ":" delimeted data into char
//Serial.print(F("Server response code : ")); Serial.println(httpResponseCode);
// print the matching credentials
Serial.println(rfidMatchChar);
lcd.setCursor(0, 1); lcd.print(rfidMatchChar);
http.end();
}
void postData() {
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("®type=", regType, POST_URL) ;
append2("&brand=", brandID, POST_URL) ;
append2("&rfid=", sqlUID, POST_URL) ;
append2("&loadid=", loadID, POST_URL) ;
append2("&hivenum=", hiveNum, POST_URL) ;
//append2("&boxnum=", boxNum, POST_URL) ;
append2("&weight=", weightChar, POST_URL) ;
// report what is being sent in POST request
// Serial.print(F("PHP loc: ")); Serial.println(php_loc) ;
// Serial.print(F("POST_URL: ")); Serial.print(POST_URL);
HTTPClient http ; //Declare object of class HTTPClient
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) {
Serial.println(F("Data was written to db.")) ;
lcd.setCursor(0, 0); lcd.print(blank) ; lcd.setCursor(0, 0) ; lcd.print(rfidMatchChar);
lcd.setCursor(0, 1) ;
lcd.print("DATA WRITE OK! ") ;
delay(printDelay) ;
lcd.clear() ;
}
else {
Serial.println(F("Data Post Error")) ;
lcd.clear() ; lcd.print(F("DATA WRITE FAIL!")) ;
delay(errorDelay) ;
lcd.clear() ;
}
}
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(500) ;
Serial.print(F(". ")) ;
}
lcd.setCursor(14, 0); lcd.print(F("OK"));
Serial.print(F("Local IP: ")) ;
Serial.println(WiFi.localIP()) ;
// Start the mDNS responder for "nodeboxin.local"
if (!MDNS.begin(localHostname)) {
Serial.println(F("mDNS fail!")) ;
}
else {
Serial.println(F("mDNS Up")) ;
}
// Resolve the IP Address for the mySQL Server
serverIp = MDNS.queryHost(sqlServer) ;
lcd.setCursor(0, 1) ; lcd.print("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("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 none found function will exit immediately
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] ) {
Serial.println(F("A new card has been detected.")) ;
lcd.setCursor(0, 0); lcd.print(F("Tag Registered "));
// Set newTag and newTagReg(ms)
newTag = true ;
newTagReg = millis() ;
// Store NUID into nuidPICC array
for (byte i = 0; i < 4; i++) {
nuidPICC[i] = rfid.uid.uidByte[i] ;
}
// concatenate nuidPICC to ":" delimeted char "sqlUID"
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.")) ;
// Don't report to lcd
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(hiveNum, ptr);
}
else if (i == 2) {
strcpy(boxNum, ptr);
}
ptr = strtok(NULL, ":");
i++;
}
}
type or paste code here