Communication between MEGA2560, ESP32C3 and Rainmaker

CUHenri
Posts: 4
Joined: Sun Mar 19, 2023 5:08 am

Communication between MEGA2560, ESP32C3 and Rainmaker

Postby CUHenri » Sun Mar 26, 2023 4:29 am

Hello, I am doing an automation project for an aquaponics system, and I installed six sensors inside the fish tank to collect the parameters. Firstly, the data will be received by the Bluno Mega 2560 and transmitted to ESP32C3 through the TX pin (2560) to the RX pin (C3). And the values should be self-updating on the apps under the RainMaker code. The values of each parameters were right for the first few times, but went wrong and started sending values with wrong position (i.e. temperature value went to water level value), wrong format (e.g., 25.000.000) and latter all 0.

I am not sure why this case happens, so I have attached my code and also the serial monitor output here for your guidance. Btw, I set set the time for getting sensors data for every 10s on MEGA, while every 30s on ESP for updating the parameters to the RainMaker.

Code for Bluno MEGA

Code: Select all

#include <OneWire.h> 
#include <DallasTemperature.h>
#include "DFRobot_PH.h"
#include <EEPROM.h>
#include "DFRobot_EC.h"
#include <Arduino.h>

#define ONE_WIRE_BUS 2
#define echoPin 3 
#define trigPin 4
#define EC_PIN A0
#define DO_PIN A1
#define PH_PIN A2
#define orpPin A3 


//DO Sensor
#define VREF 5000
#define ADC_RES 1024
#define READ_TEMP (sensors.getTempCByIndex(0))
#define CAL1_V (2949) //mv
#define CAL1_T (26)   //℃
#define CAL2_V (332) //mv
#define CAL2_T (15)   //℃

//ORP Sensor
#define VOLTAGE 5.00    
#define OFFSET 2       
#define LED 13 

//Temperature Sensor
OneWire oneWire(ONE_WIRE_BUS); 
DallasTemperature sensors(&oneWire);

//Water Level Sensor
long duration; 
int distance;
int TankDepth = 31;
int WaterLevel;
//Soil Moisture Sensor
const int AirValue = 609;   
const int WaterValue = 291;  
int soilMoistureValue = 0;
int soilmoisturepercent = 0;
//pH & EC Sensor
float  voltagePH,voltageEC,phValue,ecValue,temperature = 25;
DFRobot_PH ph;
DFRobot_EC ec;
//DO Sensor
const uint16_t DO_Table[41] = {
    14460, 14220, 13820, 13440, 13090, 12740, 12420, 12110, 11810, 11530,
    11260, 11010, 10770, 10530, 10300, 10080, 9860, 9660, 9460, 9270,
    9080, 8900, 8730, 8570, 8410, 8250, 8110, 7960, 7820, 7690,
    7560, 7430, 7300, 7180, 7070, 6950, 6840, 6730, 6630, 6530, 6410};

uint8_t Temperaturet;
uint16_t ADC_Raw;
uint16_t ADC_Voltage;
uint16_t DO;

int16_t readDO(uint32_t voltage_mv, uint8_t temperature_c)
{
#if TWO_POINT_CALIBRATION == 0
  uint16_t V_saturation = (uint32_t)CAL1_V + (uint32_t)35 * temperature_c - (uint32_t)CAL1_T * 35;
  return (voltage_mv * DO_Table[temperature_c] / V_saturation);
#else
  uint16_t V_saturation = (int16_t)((int8_t)temperature_c - CAL2_T) * ((uint16_t)CAL1_V - CAL2_V) / ((uint8_t)CAL1_T - CAL2_T) + CAL2_V;
  return (voltage_mv * DO_Table[temperature_c] / V_saturation);
#endif
}
//ORP Sensor
double orpValue;
#define ArrayLenth  40    
int orpArray[ArrayLenth];
int orpArrayIndex=0;

double avergearray(int* arr, int number){
  int i;
  int max,min;
  double avg;
  long amount=0;
  if(number<=0){
    printf("Error number for the array to avraging!/n");
    return 0;
  }
  if(number<5){   //less than 5, calculated directly statistics
    for(i=0;i<number;i++){
      amount+=arr[i];
    }
    avg = amount/number;
    return avg;
  }else{
    if(arr[0]<arr[1]){
      min = arr[0];max=arr[1];
    }
    else{
      min=arr[1];max=arr[0];
    }
    for(i=2;i<number;i++){
      if(arr[i]<min){
        amount+=min;        //arr<min
        min=arr[i];
      }else {
        if(arr[i]>max){
          amount+=max;    //arr>max
          max=arr[i];
        }else{
          amount+=arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount/(number-2);
  }//if
  return avg;
}

void setup() {
  Serial.begin(115200);
  pinMode(trigPin, OUTPUT); 
  pinMode(echoPin, INPUT); 
  pinMode(LED,OUTPUT);
  ph.begin();
  ec.begin();  
  sensors.begin(); 

}

void loop() {
  Serial.println("System Condition:");

   // Temperature Sensor 
  sensors.requestTemperatures();  
  Serial.print("Temperature is: "); 
  Serial.println(sensors.getTempCByIndex(0)+ String("℃")); 

  // Ultrasonic Sensor
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2; 
  WaterLevel = TankDepth - distance;
  Serial.print("Water level: ");
  Serial.print(WaterLevel);
  Serial.println(" cm"); 
  
  // Soil Moisture Sensor
  soilMoistureValue = analogRead(A4);  
  Serial.print("Soil Moisture Percent: ");
  soilmoisturepercent = map(soilMoistureValue, AirValue, WaterValue, 0, 100);
  if(soilmoisturepercent >= 100)
  {
  Serial.println("100 %");
  }
  else if(soilmoisturepercent <=0)
  {
  Serial.println("0 %");
  }
  else if(soilmoisturepercent >0 && soilmoisturepercent < 100)
  {
  Serial.print(soilmoisturepercent);
  Serial.println("%");
  }
 
  //pH & EC Sensor
  char cmd[10];
    static unsigned long timepoint = millis();
    if(millis()-timepoint>1000U){                            //time interval: 1s
        timepoint = millis();
        //temperature = readTemperature();                   // read your temperature sensor to execute temperature compensation
        voltagePH = analogRead(PH_PIN)/1024.0*5000;          // read the ph voltage
        phValue    = ph.readPH(voltagePH,temperature);       // convert voltage to pH with temperature compensation
        Serial.print("pH: ");
        Serial.println(phValue,2);
        voltageEC = analogRead(EC_PIN)/1024.0*5000;
        ecValue    = ec.readEC(voltageEC,temperature);       // convert voltage to EC with temperature compensation
        Serial.print("EC: ");
        Serial.print(ecValue,2);
        Serial.println(" ms/cm");
    }
    if(readSerial(cmd)){
        strupr(cmd);
        if(strstr(cmd,"PH")){
            ph.calibration(voltagePH,temperature,cmd);       //PH calibration process by Serail CMD
        }
        if(strstr(cmd,"EC")){
            ec.calibration(voltageEC,temperature,cmd);       //EC calibration process by Serail CMD
        }
    }
    
  //DO Sensor
  Temperaturet = (uint8_t)READ_TEMP;
  ADC_Raw = analogRead(DO_PIN);
  ADC_Voltage = uint32_t(VREF) * ADC_Raw / ADC_RES;

  Serial.println("DO: " + String(readDO(ADC_Voltage, Temperaturet)) + " μg/L");
  
  //ORP Sensor
  static unsigned long orpTimer=millis();   //analog sampling interval
  static unsigned long printTime=millis();
  if(millis() >= orpTimer)
  {
    orpTimer=millis()+20;
    orpArray[orpArrayIndex++]=analogRead(orpPin);    //read an analog value every 20ms
    if (orpArrayIndex==ArrayLenth) {
      orpArrayIndex=0;
    }
    orpValue=((30*(double)VOLTAGE*1000)-(75*avergearray(orpArray, ArrayLenth)*VOLTAGE*1000/1024))/75-OFFSET;

    //convert the analog value to orp according the circuit
  }
  if(millis() >= printTime)   //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
  {
    printTime=millis()+800;
    Serial.print("ORP: ");
    Serial.print((int)orpValue);
        Serial.println(" mV");
        digitalWrite(LED,1-digitalRead(LED));
  }
   Serial.println(" ");
   delay(3000); 

}

int i = 0;
bool readSerial(char result[]){
    while(Serial.available() > 0){
        char inChar = Serial.read();
        if(inChar == '\n'){
             result[i] = '\0';
             Serial.flush();
             i=0;
             return true;
        }
        if(inChar != '\r'){
             result[i] = inChar;
             i++;
        }
        delay(1);
    }
    return false;
}

float readTemperature()
{
  sensors.requestTemperatures(); 
}

Code for ESP32C3

Code: Select all

#include "RMaker.h"
#include "WiFi.h"
#include "WiFiProv.h"
#include <wifi_provisioning/manager.h>

//Data Transfer Pins
#define RXp 20
#define TXp 21

// Set Defalt Values
#define DEFAULT_RELAY_MODE true
#define DEFAULT_Temperature 0
#define DEFAULT_WaterLevel 10
#define DEFAULT_pH 7
#define DEFAULT_EC 0
#define DEFAULT_ORP 0
#define DEFAULT_DO 0

// BLE Credentils
const char *service_name = "AB_1";
const char *pop = "1234567";

// GPIO
static uint8_t gpio_reset = 9;
static uint8_t relay = 7;
bool relay_state = true;

bool wifi_connected = 0;

//Timer
volatile int interruptCounter;
int totalInterruptCounter;
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
bool esp_rmaker_time_check(); 


void IRAM_ATTR onTimer() {
 portENTER_CRITICAL_ISR(&timerMux);
 interruptCounter++;
 portEXIT_CRITICAL_ISR(&timerMux);
  }

//------------------------------------------- Declaring Devices -----------------------------------------------------//

//The framework provides some standard device types like switch, lightbulb, fan, temperature sensor.
static TemperatureSensor temperature_sensor("Temperature");
static Device ultrasonic_sensor("Water Level");
static Device pH_sensor("pH");
static Device EC_sensor("Electro-Conductivity");
static Device ORP_sensor("Oxidation-Reduction-Potential");
static Device DO_sensor("Dissolved-Oxygen");
static Switch my_switch("Heater", &relay);

void sysProvEvent(arduino_event_t *sys_event)
{
  switch (sys_event->event_id) {
    case ARDUINO_EVENT_PROV_START:
#if CONFIG_IDF_TARGET_ESP32S2
        Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
        printQR(service_name, pop, "softap");
#else
        Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop);
        printQR(service_name, pop, "ble");
#endif
      break;
    case ARDUINO_EVENT_WIFI_STA_CONNECTED:
      Serial.printf("\nConnected to Wi-Fi!\n");
      wifi_connected = 1;
      delay(500);
      break;
    case ARDUINO_EVENT_PROV_CRED_RECV: {
        Serial.println("\nReceived Wi-Fi credentials");
        Serial.print("\tSSID : ");
        Serial.println((const char *) sys_event->event_info.prov_cred_recv.ssid);
        Serial.print("\tPassword : ");
        Serial.println((char const *) sys_event->event_info.prov_cred_recv.password);
        break;
      }
    case ARDUINO_EVENT_PROV_INIT:
      wifi_prov_mgr_disable_auto_stop(10000);
      break;
    case ARDUINO_EVENT_PROV_CRED_SUCCESS:
      Serial.println("Stopping Provisioning!!!");
      wifi_prov_mgr_stop_provisioning();
      break;
  }
}

void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx)
{
  const char *device_name = device->getDeviceName();
  Serial.println(device_name);
  const char *param_name = param->getParamName();

  if (strcmp(device_name, "Heater") == 0)
  {
    if (strcmp(param_name, "Power") == 0)
    {
      Serial.printf("Received value = %s for %s - %s\n", val.val.b ? "true" : "false", device_name, param_name);
      relay_state = val.val.b;
      (relay_state == false) ? digitalWrite(relay, LOW) : digitalWrite(relay, HIGH);
      param->updateAndReport(val);
    }
  }
}


void setup()
{
  Serial.begin(115200);

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 30000000, true);
  timerAlarmEnable(timer);


  // Configure the input GPIOs
  //pinMode(gpio_reset, INPUT);
  pinMode(relay, OUTPUT);
  digitalWrite(relay, DEFAULT_RELAY_MODE);

  
  //------------------------------------------- Declaring Node -----------------------------------------------------//
  Node my_node;
  my_node = RMaker.initNode("Aquaponics_System");

  //Standard switch device
  my_switch.addCb(write_callback);

  //Water Level Parameter
  Param waterlevelParam("Water Level (cm)", "custom.param.waterlevel", value((float)DEFAULT_WaterLevel), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
  ultrasonic_sensor.addParam(waterlevelParam);
  ultrasonic_sensor.assignPrimaryParam(ultrasonic_sensor.getParamByName("Water Level (cm)"));

  //pH Parameter
  Param pHParam("pH", "custom.param.pH", value((float)DEFAULT_pH), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
  pH_sensor.addParam(pHParam);
  pH_sensor.assignPrimaryParam(pH_sensor.getParamByName("pH"));

  //EC Parameter
  Param ecParam("EC (ms/cm)", "custom.param.EC", value((float)DEFAULT_EC), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
  EC_sensor.addParam(ecParam);
  EC_sensor.assignPrimaryParam(EC_sensor.getParamByName("EC (ms/cm)"));

  //ORP Parameter
  Param orpParam("ORP (mV)", "custom.param.orp", value((float)DEFAULT_ORP), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
  ORP_sensor.addParam(orpParam);
  ORP_sensor.assignPrimaryParam(ORP_sensor.getParamByName("ORP (mV)"));

  //DO Parameter
  Param doParam("DO (μg/L)", "custom.param.do", value((float)DEFAULT_DO), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
  DO_sensor.addParam(doParam);
  DO_sensor.assignPrimaryParam(DO_sensor.getParamByName("DO (μg/L)"));
  

  //------------------------------------------- Adding Devices in Node -----------------------------------------------------//
  my_node.addDevice(temperature_sensor);
  my_node.addDevice(ultrasonic_sensor);
  my_node.addDevice(pH_sensor);
  my_node.addDevice(EC_sensor);
  my_node.addDevice(ORP_sensor);
  my_node.addDevice(DO_sensor);
  my_node.addDevice(my_switch);

  //This is optional
  RMaker.enableOTA(OTA_USING_PARAMS);
  //If you want to enable scheduling, set time zone for your region using setTimeZone().
  //The list of available values are provided here https://rainmaker.espressif.com/docs/time-service.html
  //RMaker.setTimeZone("Asia/Hong_Kong");
  // Alternatively, enable the Timezone service and let the phone apps set the appropriate timezone
  RMaker.enableTZService();
  RMaker.enableSchedule();
  RMaker.enableScenes();
  
  Serial.printf("\nStarting ESP-RainMaker\n");
  RMaker.start();

  WiFi.onEvent(sysProvEvent);

#if CONFIG_IDF_TARGET_ESP32S2
    WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
#else
    WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name);
#endif

}


void loop()
{
  if (interruptCounter > 0 && wifi_connected) {                    
    Serial.println("Sending Sensor's Data");
    portENTER_CRITICAL(&timerMux);
    interruptCounter--;
    portEXIT_CRITICAL(&timerMux);
    totalInterruptCounter++;
    Send_TempSensor();
    Send_UltraSensor();
    Send_pHSensor();
    Send_ECSensor();
    Send_ORPSensor();
    Send_DOSensor();
  }


  //-----------------------------------------------------------  Logic to Reset RainMaker

  // Read GPIO0 (external button to reset device
  if (digitalRead(gpio_reset) == LOW) { //Push button pressed
    Serial.printf("Reset Button Pressed!\n");
    // Key debounce handling
    delay(100);
    int startTime = millis();
    while (digitalRead(gpio_reset) == LOW) delay(50);
    int endTime = millis();

    if ((endTime - startTime) > 10000) {
      // If key pressed for more than 10secs, reset all
      Serial.printf("Reset to factory.\n");
      wifi_connected = 0;
      RMakerFactoryReset(2);
    } else if ((endTime - startTime) > 3000) {
      Serial.printf("Reset Wi-Fi.\n");
      wifi_connected = 0;
      // If key pressed for more than 3secs, but less than 10, reset Wi-Fi
      RMakerWiFiReset(2);
    }
  }
  
  delay(100);
}

void Send_TempSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'Temperature:')  
  {
    String TemperatureS = Serial.readStringUntil('\n');
    float t = TemperatureS.toFloat();
    Serial.println(t);
    temperature_sensor.updateAndReportParam("Temperature", t);
  }
}

void Send_UltraSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'Water Level:')
  {
    String WaterLevelS = Serial.readStringUntil('\n');
    float w = WaterLevelS.toFloat();
    Serial.println(w);
    ultrasonic_sensor.updateAndReportParam("Water Level (cm)", w);    
  }
}

void Send_pHSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'pH:') 
  {
    String pHvalueS = Serial.readStringUntil('\n');
    float c = pHvalueS.toFloat();
    Serial.println(c);
    pH_sensor.updateAndReportParam("pH", c); 
  }
}

void Send_ECSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'EC:')  
  {
    String ECvalueS = Serial.readStringUntil('\n');
    float d = ECvalueS.toFloat();
    Serial.println(d);
    EC_sensor.updateAndReportParam("EC (ms/cm)", d);
  }
}

void Send_ORPSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'ORP:')  
  {
    String ORPvalueS = Serial.readStringUntil('\n');
    float e = ORPvalueS.toFloat();
    Serial.println(e);
    ORP_sensor.updateAndReportParam("ORP (mV)", e);
  }
}

void Send_DOSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
    if (msg = 'DO:') 
  {
    String DOvalueS = Serial.readStringUntil('\n');
    float f = DOvalueS.toFloat();
    Serial.println(f);
    DO_sensor.updateAndReportParam("DO (μg/L)", f);
  }
}


Serial Monitor Output

Code: Select all

15:16:18.440 -> Sending Sensor's Data
15:16:18.440 -> Temperature: 23.87
15:16:18.440 -> Water Level: 24.00
15:16:18.440 -> pH:6.21
15:16:18.482 -> EC:2.40
15:16:18.482 -> DO:1352.00
15:16:18.482 -> ORP: 9.92

15:16:48.467 -> Sending Sensor's Data
15:16:48.467 -> Temperature: 23.87
15:16:48.467 -> Water Level: 24.00
15:16:48.467 -> pH: 4.74
15:16:48.467 -> EC: 3.98
15:16:48.467 -> DO: 2133.00
15:16:48.467 -> ORP: 10.44

15:17:48.442 -> Sending Sensor's Data
15:17:48.442 -> er Level: 25.00
15:17:48.488 -> pH: 6.15
15:17:48.488 -> EC: 2.14
15:17:48.488 -> DO: 1337.00
15:17:48.488 -> ORP: 1587.97
15:17:48.488 -> Temperature: 23.75

15:20:48.516 -> Sending Sensor's Data
15:20:48.516 -> 0.00
15:20:48.516 -> 2.140.00
15:20:48.516 -> 14100.00
15:20:48.516 -> 915.560.00
15:20:48.516 -> 23.810.00
15:20:48.516 -> 250.00

sanketwadekar
Posts: 32
Joined: Tue Jul 26, 2022 10:08 am

Re: Communication between MEGA2560, ESP32C3 and Rainmaker

Postby sanketwadekar » Mon Mar 27, 2023 8:50 am

From your serial logs
15:20:48.516 -> Sending Sensor's Data
15:20:48.516 -> 0.00
15:20:48.516 -> 2.140.00
15:20:48.516 -> 14100.00
15:20:48.516 -> 915.560.00
15:20:48.516 -> 23.810.00
15:20:48.516 -> 250.00
you can see that the strings "er Level", "pH", "EC" etc. are not printed while in the preceding logs, they are printed.
Also, if you look at this line,

Code: Select all

  if (msg = 'Water Level:')
you have used the assignment operator instead of the equality operator(==). Please check in all places where you have made such errors.

CUHenri
Posts: 4
Joined: Sun Mar 19, 2023 5:08 am

Re: Communication between MEGA2560, ESP32C3 and Rainmaker

Postby CUHenri » Mon Mar 27, 2023 3:08 pm

Yup, I have changed all the '=' to '==' for all my sensor's function. This also means that I need to change 'Water Level' to "Water Level", right? However, this creates another issue for me. That is to say, the value cannot be stored in the string: WaterLevelS, which makes that the program cannot execute the code afterwards and not parameters are updating in the RainMaker applications. What are your suggestions?

Code: Select all

void Send_UltraSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg == "Water Level:")  
  {
    String WaterLevelS = Serial.readStringUntil('\n');
    float w = WaterLevelS.toFloat();
    Serial.println(w);
    ultrasonic_sensor.updateAndReportParam("Water Level (cm)", w);    
  }
}

sanketwadekar
Posts: 32
Joined: Tue Jul 26, 2022 10:08 am

Re: Communication between MEGA2560, ESP32C3 and Rainmaker

Postby sanketwadekar » Wed Mar 29, 2023 9:49 am

This seems to be a different issue.
From my understanding, you are not parsing the stream data correctly.

In the first code shown below,

Code: Select all

  Serial.print("Water level: ");
  Serial.print(WaterLevel);
  Serial.println(" cm");
you are sending the stream in the following format "Water level: 23 cm\n"

However, in the c3 code you are expecting a newline character

Code: Select all

void Send_UltraSensor(){
  String msg = Serial.readStringUntil('\n');
  Serial.print(msg);
  if (msg = 'Water Level:')
  {
    String WaterLevelS = Serial.readStringUntil('\n');
    float w = WaterLevelS.toFloat();
    Serial.println(w);
    ultrasonic_sensor.updateAndReportParam("Water Level (cm)", w);    
  }
}
  
Here, on c3 you are expecting a stream like this -> "Water level:\n23\n"
Notice the difference between what is sent and what is expected.
If you are not able to parse it properly, then you will see garbage values on the rainmaker app.
Also, you should be able to synchronize the exchange of data properly. Otherwise, Serial.readStringUntil will timeout.

Who is online

Users browsing this forum: No registered users and 26 guests