I am making a control system using BLYNK as my IoT platform to view data over the mobile.I have to monitor and control a fish pond’s environment. I take sensor readings such as Temperature, Dissolved Oxygen , pH , and a servo motor to control the dissolved oxygen of the system as required by the user.
Problem - I am using an LCD display (I2C 16*2) to view data at the remote location itself. But i noticed that when the esp_01 module is not connected to the internet (when the system is not connected to BLYNK) the LCD display is not updating for some reason and i’m having a hard time figuring out how to do this. This LCD display should work independently even if there are issues in connecting to the BLYNK network. Please be kind enough to help me how to do this. Thanks in advance.
Best,
Moh
#include <LiquidCrystal_I2C.h> //LCD Library headers
LiquidCrystal_I2C lcd(0x27,20,4);
//pH
#define SensorPin A0 //pH meter Analog output to Arduino Analog Input 0
#define Offset 0.00 //deviation compensator
#define LED 13
#define samplingInterval 20
#define printInterval 800
#define ArrayLenth 40 //times of collection
int pHArray[ArrayLenth]; //Store the average value of the sensor feedback
int pHArrayIndex=0;
static unsigned long samplingTime = millis();
static unsigned long printTime = millis();
static float pHValue,voltage;
//temperature libraries
#include <OneWire.h>
#include <DallasTemperature.h>
// Signal plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
#include <avr/pgmspace.h>
#include <EEPROM.h>
#define DoSensorPin A1 //dissolved oxygen sensor analog output pin to arduino mainboard
#define VREF 5000
float doValue; //current dissolved oxygen value, unit; mg/L
float temperature = 25;
#define EEPROM_write(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp[i]);}
#define EEPROM_read(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) pp[i]=EEPROM.read(address+i);}
#define ReceivedBufferLength 20
char receivedBuffer[ReceivedBufferLength+1]; // store the serial command
byte receivedBufferIndex = 0;
#define SCOUNT 30 // sum of sample point
int analogBuffer[SCOUNT]; //store the analog value in the array, readed from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0,copyIndex = 0;
#define SaturationDoVoltageAddress 12 //the address of the Saturation Oxygen voltage stored in the EEPROM
#define SaturationDoTemperatureAddress 16 //the address of the Saturation Oxygen temperature stored in the EEPROM
float SaturationDoVoltage,SaturationDoTemperature;
float averageVoltage;
const float SaturationValueTab[41] PROGMEM = { //saturation dissolved oxygen concentrations at various temperatures
14.46, 14.22, 13.82, 13.44, 13.09,
12.74, 12.42, 12.11, 11.81, 11.53,
11.26, 11.01, 10.77, 10.53, 10.30,
10.08, 9.86, 9.66, 9.46, 9.27,
9.08, 8.90, 8.73, 8.57, 8.41,
8.25, 8.11, 7.96, 7.82, 7.69,
7.56, 7.43, 7.30, 7.18, 7.07,
6.95, 6.84, 6.73, 6.63, 6.53,
6.41,
};
//BLYNK
/*************************************************************
Download latest Blynk library here:
https://github.com/blynkkk/blynk-library/releases/latest
Blynk is a platform with iOS and Android apps to control
Arduino, Raspberry Pi and the likes over the Internet.
You can easily build graphic interfaces for all your
projects by simply dragging and dropping widgets.
Downloads, docs, tutorials: http://www.blynk.cc
Sketch generator: http://examples.blynk.cc
Blynk community: http://community.blynk.cc
Follow us: http://www.fb.com/blynkapp
http://twitter.com/blynk_app
Blynk library is licensed under MIT license
This example code is in public domain.
*************************************************************
WARNING!
It's very tricky to get it working. Please read this article:
http://help.blynk.cc/hardware-and-libraries/arduino/esp8266-with-at-firmware
This example shows how value can be pushed from Arduino to
the Blynk App.
NOTE:
BlynkTimer provides SimpleTimer functionality:
http://playground.arduino.cc/Code/SimpleTimer
App project setup:
Value Display widget attached to Virtual Pin V5
*************************************************************/
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <Servo.h>
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "6eecbbbeff964c47adfac95f9a3b2d7e";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "OnePlus6"; //AIS 4G Pocket Wifi_320611 //devilman1069
char pass[] = "devilman1069"; //36320611 //OnePlus6
// Hardware Serial on Mega, Leonardo, Micro...
#define EspSerial Serial1
// or Software Serial on Uno, Nano...
//#include <SoftwareSerial.h>
//SoftwareSerial EspSerial(2, 3); // RX, TX
// Your ESP8266 baud rate:
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);
int a=0;
int y=0;
int x=0;
int b=0;
BLYNK_WRITE(V1)
{
int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable
a=pinValue;
//required dissolved oxygen value from the user
// process received value
}
Servo servo;
BLYNK_WRITE(V3)
{
int servo = param.asInt(); //manual control for the servo motor to control the level of
b=servo; //oxygen in the pond
}
BlynkTimer timer;
//MILLIS
// This function sends Arduino's up time every second to Virtual Pin (5).
// In the app, Widget's reading frequency should be set to PUSH. This means
// that you define how often to send data to Blynk App.
//APP DISPLAY FUNCTIONS
//void myTimerEvent()
//{
// You can send any value at any time.
// Please don't send more that 10 values per second.
//Blynk.virtualWrite(V5, millis() / 1000);
//}
//TEMPERATURE
void mytemp()
{
// You can send any value at any time.
// Please don't send more that 10 values per second.
float temp=sensors.getTempCByIndex(0);
Blynk.virtualWrite(V6,temp);
}
//DO
void mydo()
{
// You can send any value at any time.
// Please don't send more that 10 values per second.
float do1=doValue;
static unsigned long analogSampleTimepoint = millis();
if(millis()-analogSampleTimepoint > 30U) //every 30 milliseconds,read the analog value from the ADC
{
analogSampleTimepoint = millis();
analogBuffer[analogBufferIndex] = analogRead(DoSensorPin); //read the analog value and store into the buffer
analogBufferIndex++;
if(analogBufferIndex == SCOUNT)
analogBufferIndex = 0;
}
static unsigned long tempSampleTimepoint = millis();
if(millis()-tempSampleTimepoint > 500U) // every 500 milliseconds, read the temperature
{
tempSampleTimepoint = millis();
//temperature = readTemperature(); // add your temperature codes here to read the temperature, unit:^C
sensors.requestTemperatures();
temperature =sensors.getTempCByIndex(0);
}
static unsigned long printTimepoint = millis();
if(millis()-printTimepoint > 1000U)
{
printTimepoint = millis();
for(copyIndex=0;copyIndex<SCOUNT;copyIndex++)
{
analogBufferTemp[copyIndex]= analogBuffer[copyIndex];
}
averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 1024.0; // read the value more stable by the median filtering algorithm
//Serial.print(F("Temperature:"));
//Serial.print(temperature,1);
//Serial.print(F("^C"));
doValue = pgm_read_float_near( &SaturationValueTab[0] + (int)(SaturationDoTemperature+0.5) ) * averageVoltage / SaturationDoVoltage; //calculate the do value, doValue = Voltage / SaturationDoVoltage * SaturationDoValue(with temperature compensation)
//Serial.print(F(", DO Value:"));
//Serial.print(doValue,2);
//Serial.println(F("mg/L"));
if(serialDataAvailable() > 0)
{
byte modeIndex = uartParse(); //parse the uart command received
doCalibration(modeIndex); // If the correct calibration command is received, the calibration function should be called.
}
Blynk.virtualWrite(V7,do1);
}
}
//pH_value
void mypH()
{
// You can send any value at any time.
// Please don't send more that 10 values per second.
float pH=pHValue;
// static unsigned long samplingTime = millis();
// static unsigned long printTime = millis();
// static float pHValue,voltage;
if(millis()-samplingTime > samplingInterval)
{
pHArray[pHArrayIndex++]=analogRead(SensorPin);
if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
pHValue = 3.5*voltage+Offset;
samplingTime=millis();
}
if(millis() - printTime > printInterval) //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
{
//Serial.print("Voltage:");
// Serial.print(voltage,2);
//Serial.print(" pH value: ");
// Serial.println(pHValue,2);
digitalWrite(LED,digitalRead(LED)^1);
printTime=millis();
}
Blynk.virtualWrite(V8,pH);
}
void myLCD ()
{
lcd.setCursor(0,0);
lcd.print("tempt:");
//
lcd.setCursor(0,1);
lcd.print("DO:");
lcd.setCursor(8,1);
lcd.print("pH:");
lcd.setCursor(7,0);
lcd.print(sensors.getTempCByIndex(0));
//
lcd.setCursor(3,1);
lcd.print(doValue,2);
lcd.setCursor(11,1);
lcd.print(pHValue);
}
int led_pin=12;
int reset_pin=29; //pin to reset the ESP module
void servoauto_manual ()
{
if (digitalRead(led_pin) == LOW)
{
if (doValue<a-1.5 )
{
if(x!=0)
{
x=x-5;
servo.write(x);
//delay (200);
}
}
if (doValue>a+1.5)
{
if( x!=90 )
{
x=x+5;
servo.write(x);
// delay (200);
}
}
if (doValue<a+0.5 && doValue>a-0.5)
{
servo.write(x);
}
}
if (digitalRead(led_pin) == HIGH)
{
servo.write(b);
}
}
void espreset() //TIMELY ESP RESET
{
digitalWrite (reset_pin,LOW) ;
delay(1000);
digitalWrite (reset_pin,HIGH) ;
}
void setup()
{
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
sensors.begin();
pinMode(LED,OUTPUT);
pinMode(led_pin,INPUT);
pinMode(DoSensorPin,INPUT);
pinMode(reset_pin,OUTPUT);
digitalWrite (reset_pin,HIGH) ;
readDoCharacteristicValues();
timer.setInterval(1500L, myLCD);
EspSerial.begin(ESP8266_BAUD); // Set ESP8266 baud rate
delay(10);
Blynk.begin(auth, wifi, ssid, pass);
// You can also specify server:
//Blynk.begin(auth, wifi, ssid, pass, "blynk-cloud.com", 80);
//Blynk.begin(auth, wifi, ssid, pass, IPAddress(192,168,1,100), 8080);
// Setup a function to be called every second
// timer.setInterval(1000L, myTimerEvent);
timer.setInterval(2630L, mytemp);
timer.setInterval(3110L, mydo);
timer.setInterval(5210L, mypH);
servo.attach(9);
servo.write(x);
timer.setInterval(300000L, espreset);
// timer.setInterval(1000L, servoauto_manual );
//lcd.print("millis:");
// lcd.setCursor(1,7);
//lcd.print(millis());
//
//lcd.setCursor(0,1);
//lcd.print("D:");
}
void loop()
{
Blynk.run();
timer.run();
//Serial.print(a);
//Serial.print(" ");
//Serial.print(x);
//Serial.print(" ");
//Serial.println(doValue);
//Serial.print(void mydo());
}
boolean serialDataAvailable(void)
{
char receivedChar;
static unsigned long receivedTimeOut = millis();
while ( Serial.available() > 0 )
{
if (millis() - receivedTimeOut > 500U)
{
receivedBufferIndex = 0;
memset(receivedBuffer,0,(ReceivedBufferLength+1));
}
receivedTimeOut = millis();
receivedChar = Serial.read();
if (receivedChar == '\n' || receivedBufferIndex == ReceivedBufferLength)
{
receivedBufferIndex = 0;
strupr(receivedBuffer);
return true;
}else{
receivedBuffer[receivedBufferIndex] = receivedChar;
receivedBufferIndex++;
}
}
return false;
}
byte uartParse()
{
byte modeIndex = 0;
if(strstr(receivedBuffer, "CALIBRATION") != NULL)
modeIndex = 1;
else if(strstr(receivedBuffer, "EXIT") != NULL)
modeIndex = 3;
else if(strstr(receivedBuffer, "SATCAL") != NULL)
modeIndex = 2;
return modeIndex;
}
void doCalibration(byte mode)
{
char *receivedBufferPtr;
static boolean doCalibrationFinishFlag = 0,enterCalibrationFlag = 0;
float voltageValueStore;
switch(mode)
{
case 0:
if(enterCalibrationFlag)
Serial.println(F("Command Error"));
break;
case 1:
enterCalibrationFlag = 1;
doCalibrationFinishFlag = 0;
Serial.println();
Serial.println(F(">>>Enter Calibration Mode<<<"));
Serial.println(F(">>>Please put the probe into the saturation oxygen water! <<<"));
Serial.println();
break;
case 2:
if(enterCalibrationFlag)
{
Serial.println();
Serial.println(F(">>>Saturation Calibration Finish!<<<"));
Serial.println();
EEPROM_write(SaturationDoVoltageAddress, averageVoltage);
EEPROM_write(SaturationDoTemperatureAddress, temperature);
SaturationDoVoltage = averageVoltage;
SaturationDoTemperature = temperature;
doCalibrationFinishFlag = 1;
}
break;
case 3:
if(enterCalibrationFlag)
{
Serial.println();
if(doCalibrationFinishFlag)
Serial.print(F(">>>Calibration Successful"));
else
Serial.print(F(">>>Calibration Failed"));
Serial.println(F(",Exit Calibration Mode<<<"));
Serial.println();
doCalibrationFinishFlag = 0;
enterCalibrationFlag = 0;
}
break;
}
}
int getMedianNum(int bArray[], int iFilterLen)
{
int bTab[iFilterLen];
for (byte i = 0; i<iFilterLen; i++)
{
bTab[i] = bArray[i];
}
int i, j, bTemp;
for (j = 0; j < iFilterLen - 1; j++)
{
for (i = 0; i < iFilterLen - j - 1; i++)
{
if (bTab[i] > bTab[i + 1])
{
bTemp = bTab[i];
bTab[i] = bTab[i + 1];
bTab[i + 1] = bTemp;
}
}
}
if ((iFilterLen & 1) > 0)
bTemp = bTab[(iFilterLen - 1) / 2];
else
bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
return bTemp;
}
void readDoCharacteristicValues(void)
{
EEPROM_read(SaturationDoVoltageAddress, SaturationDoVoltage);
EEPROM_read(SaturationDoTemperatureAddress, SaturationDoTemperature);
if(EEPROM.read(SaturationDoVoltageAddress)==0xFF && EEPROM.read(SaturationDoVoltageAddress+1)==0xFF && EEPROM.read(SaturationDoVoltageAddress+2)==0xFF && EEPROM.read(SaturationDoVoltageAddress+3)==0xFF)
{
SaturationDoVoltage = 1127.6; //default voltage:1127.6mv
EEPROM_write(SaturationDoVoltageAddress, SaturationDoVoltage);
}
if(EEPROM.read(SaturationDoTemperatureAddress)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+1)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+2)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+3)==0xFF)
{
SaturationDoTemperature = 25.0; //default temperature is 25^C
EEPROM_write(SaturationDoTemperatureAddress, SaturationDoTemperature);
}
}
//pH FUNCTION
double avergearray(int* arr, int number){
int i;
int max,min;
double avg;
long amount=0;
if(number<=0){
//Serial.println("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;
}