I am really new to Arduino so please bear with me.
I am running an Arduino Uno with several sensors to capture some data related to a fish tank. Here is basically what it does:
- Capture data (light, temperature, etc.)
- Display data on an LCD screen when a button is pressed
- Upload data to thingspeak every 5 minutes, using a ESP8266
- Feed the fish every 4 hours through a servo
- An RGB led blinks at various colors to let me see what's happening (e.g. red when connecting to wifi).
Everything is working reasonably well. That being said, my code is super messy and I am not sure I have the right approach. With the code I have: - Nothing can be done while the Arduino uploads the wifi - Nothing can be done when the fish are fed.
This is not so much about the servo, it is more about the wifi problem. How would you suggest that I rewrite my code so that (1) I can run the wifi more than once every 5 minutes and (2) it does not block my sensors and LCD display while I am doing it.
Sorry for the poor quality of the code. Newbie here.
//Include LCD Libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <stdlib.h>
//Include Water Probe library
#include <OneWire.h>
//Include DHT11 Libraries
#include <dht11.h>
//Include Servo Libraries
#include <Servo.h>
//Set Peripherals
dht11 DHT11;
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
Servo myservo; // create servo object to control a servo
//Pins for LCD are SDA=A4 and SCL=A5
//Variables for sensors
String sunlight;
String watertemp;
float airtemp;
float airhumid;
OneWire ds(10); //water temperature, pin 10
//Variables for LCD button
#define BUTTON_PIN A3 // Set pin Buttons for Backlight LCD.
unsigned int currentLcdLightOnTime = 0; // For calculating the lcd light on time.
unsigned long lcdLightOn_StartMillis;
boolean isLcdLightOn;
int buttonState = 0;// For checking push-button state.
// Variables for Status LED
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0;
long interval = 1000; // interval at which to blink (milliseconds)
//Variables to display LCD Numbers
char bufferhours[5]; //Output of the itoa function for hours
char bufferminutes[5]; //Output of the itoa function for minutes
// We don't care for seconds. It's irrelevant.
int moisture;
//Variables for Wifi Update
//long DELAYWIFI = 10000; //For test purposes
long DELAYWIFI = 300000; //Every 5 minutes
long endtimewifi;
long nowwifi;
//Variables for Servo
long FEEDINGDELAY = 14400000; // Feed every 4 hours
//long FEEDINGDELAY = 30000; //For test purposes
int pos = 0; // variable to store the servo position
long endtime;
long now;
long timeuntilfeeding;
//Variables for Sunlight
float percent; //transform number above into a percent
// Variables for Wifi Updates
#include <SoftwareSerial.h>
//Hardware Serial
#define _baudrate 9600
//Software Serial
#define _rxpin 0
#define _txpin 1
SoftwareSerial esp8266( _rxpin, _txpin ); // RX, TX
//Thingspeak and Wifi Information
#define SSID "xxx"
#define PASS "yyy"
#define IP "zzz" // ThingSpeak IP Address: 184.106.153.149
// GET /update?key=[THINGSPEAK_KEY]&field1=[data 1]&filed2=[data 2]...;
String GET = "GET /update?key=www";
// Initiates special characters. http://omerk.github.io/lcdchargen/
byte thermometer[8] = //icon for thermometer
{
B00100,
B01010,
B01010,
B01110,
B01110,
B11111,
B11111,
B01110
};
byte droplet[8] = //icon for water droplet
{
B00100,
B00100,
B01010,
B01010,
B10001,
B10001,
B10001,
B01110,
};
byte degree[8] = { // degree centigrade
B01000,
B10100,
B01000,
B00011,
B00100,
B00100,
B00011,
B00000
};
byte sun[8] = {
0b00100,
0b10001,
0b01110,
0b11111,
0b11111,
0b01110,
0b10001,
0b00100
};
void setup()
{
lcd.init();// initialize the lcd
//Initialize DHT/
DHT11.attach(2);
// set the digital pin as output for LED:
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
//Connect to Wifi
Serial.begin(9600);
esp8266.begin(9600);
boolean wifi_connected=false; //not connected yet...
sendesp8266("AT");
delay(2000);
for(int i=0;i<5;i++) //attempt 5 times to connect to wifi - this is a good idea
{
if(connectWiFi()) //are we connected?
{
wifi_connected = true; //yes
break; //get outta here!
}
}
//Initiate characters
lcd.createChar(0, thermometer);
lcd.createChar(1, droplet);
lcd.createChar(2, degree);
lcd.createChar(3, sun);
}
void loop(){
now = millis();
endtime = now + FEEDINGDELAY;
while(now < endtime) { // While loop for the feeding
timeuntilfeeding = endtime - millis();
now = millis();
nowwifi = millis();
endtimewifi = nowwifi + DELAYWIFI;
while(nowwifi < endtimewifi) { // While loop for wifi update
nowwifi = millis();
//We capture sensors
//Sunlight
float value_sunlight = analogRead(A2);
percent = value_sunlight / 1024.0 * 100; //Convert dryness score into moisture percentage
String sunlight =String(percent);// turn integer to string
//Water Temperature
float value_watertemp = getTemp();
String watertemp= String(value_watertemp);// turn integer to string
//Air Temperature and Humidity
int chk = DHT11.read(); //Read DHT11 Sensor
float airtemp=float(DHT11.temperature);// turn integer to string
float airhumid=float(DHT11.humidity);// turn integer to string
// LED Blinking
unsigned long currentMillis = millis();
if(currentLcdLightOnTime == 0){ //We only blink when LCD is not on
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(11, ledState);
}}
buttonState = digitalRead(BUTTON_PIN);// Check the state of the push-button.
if (buttonState == HIGH){// Button pressed.
digitalWrite(11, LOW);
lcdLightOn_StartMillis = millis();
currentLcdLightOnTime = 0;
isLcdLightOn = true;
lcd.setBacklight(1); // switch on the backlight
//Screen 0 Presentation
lcd.setCursor(0, 0);
lcd.print(" Aquaponics Bot ");
lcd.setCursor(0, 1);
lcd.print(" Version 1.0 ");
}
else{
// Button not pressed.
//Serial.println("Button not pressed - LOW");
if(isLcdLightOn){
currentLcdLightOnTime = millis() - lcdLightOn_StartMillis;
//Screen 2
if(currentLcdLightOnTime > 3000 && currentLcdLightOnTime <= 6000){
//Screen 2 - Air Temperature & Humidity
//Line 1
lcd.setCursor(0, 0);
lcd.print(" Air Sensors ");
//Line 2
lcd.setCursor(0, 1);
lcd.write(0);
lcd.print(": ");
lcd.println(airtemp);
lcd.setCursor(5, 1);
lcd.write(2);
lcd.print(" - ");
lcd.write(1);
lcd.print(": ");
lcd.print(airhumid);
lcd.setCursor(14, 1);
lcd.print("% ");
}
//Screen 3
if(currentLcdLightOnTime > 6000 && currentLcdLightOnTime < 9000){
//Screen 3 - Water Data
//Line 1
lcd.setCursor(0, 0);
lcd.print(" Water Sensors ");
//Line 2
lcd.setCursor(0, 1);
lcd.write(0);
lcd.print(": ");
lcd.println(watertemp);
lcd.setCursor(5, 1);
lcd.write(2);
lcd.print(" - soon ");
}
//Screen 4
if(currentLcdLightOnTime > 9000 && currentLcdLightOnTime < 12000){
//Screen 4 - Light Sensors
//Line 1
lcd.setCursor(0, 0);
lcd.print(" Light Sensors ");
//Line 2
lcd.setCursor(0, 1);
lcd.write(3);
lcd.print(": ");
lcd.println(sunlight);
lcd.setCursor(5, 1);
lcd.print("% - soon ");
}
if(currentLcdLightOnTime > 12000 && currentLcdLightOnTime < 15000){
//Screen 5 - Next Feeding
long hours=0;
long mins=0;
long secs=0;
secs = timeuntilfeeding/1000; //convect milliseconds to seconds
mins=secs/60; //convert seconds to minutes
hours=mins/60; //convert minutes to hours
secs=secs-(mins*60); //subtract the coverted seconds to minutes in order to display 59 secs max
mins=mins-(hours*60); //subtract the coverted minutes to hours in order to display 59 minutes max
//Display results
itoa(hours, bufferhours, 10);//Convert the number of hours into readable by LCD
itoa(mins, bufferminutes, 10);//Convert the number of minutes into readable by LCD
lcd.setCursor(0, 0);
lcd.print("Fish Feeding: ");
lcd.setCursor(0, 1);
lcd.print(bufferhours);
lcd.print(" hours ");
lcd.print(bufferminutes);
lcd.print(" min. ");
}
// Turn Off the Screen
if(currentLcdLightOnTime > 15000){
lcd.clear();
isLcdLightOn = false;
lcd.setBacklight(0); // switch off the backlight
currentLcdLightOnTime = 0; //So that the blinking can start again.
ledState = LOW;
}
}
}
} // End of the while wifi loop
// Make sure we are still connected to the internet
esp8266.begin(9600);
boolean wifi_connected=false; //not connected yet...
sendesp8266("AT");
delay(2000);
for(int i=0;i<5;i++) //attempt 5 times to connect to wifi - this is a good idea
{
if(connectWiFi()) //are we connected?
{
wifi_connected = true; //yes
break; //get outta here!
}
}
digitalWrite(13, HIGH);
//We capture sensors again to send the value
//Sunlight
float value_sunlight = analogRead(A2);
percent = value_sunlight / 1024.0 * 100; //Convert dryness score into moisture percentage
String sunlight =String(percent);// turn integer to string
//Water Temperature
float value_watertemp = getTemp();
String watertemp= String(value_watertemp);// turn integer to string
//Air Temperature and Humidity
int chk = DHT11.read(); //Read DHT11 Sensor
String airtemp=String(DHT11.temperature);// turn integer to string
String airhumid=String(DHT11.humidity);// turn integer to string
updateTS(sunlight,watertemp, airtemp, airhumid);
delay(3000); //
digitalWrite(13, LOW);
} // End of the while feeding loop
// Double Swipe to Feed the Fish
//Intialize Servo
myservo.attach(3); // attaches the servo on pin 3 to the servo object
//Display that the fish is being fed.
digitalWrite(13, HIGH);
for(pos = 0; pos < 180; pos += 5) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=0; pos-=5) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
myservo.detach(); // detattaches the servo from pin 3
//Clears display
digitalWrite(13, LOW);
// SEND AN EMAIL OR UPDATE A DATABASE..
}
//----- update the Thingspeak string with 4 values
void updateTS( String L, String W , String T, String H)
{
// ESP8266 Client
String cmd = "AT+CIPSTART=\"TCP\",\"";// Setup TCP connection
cmd += IP;
cmd += "\",80";
sendesp8266(cmd);
delay(2000);
if( Serial.find( "Error" ) )
{
esp8266.print( "RECEIVED: Error\nExit1" );
return;
}
cmd = GET + "&field1=" + L +"&field2="+ W + "&field3=" + T + "&field4=" + H +"\r\n";
Serial.print( "AT+CIPSEND=" );
Serial.println( cmd.length() );
if(Serial.find( ">" ) )
{
esp8266.print(">");
esp8266.print(cmd);
Serial.print(cmd);
}
else
{
sendesp8266( "AT+CIPCLOSE" );//close TCP connection
}
if( Serial.find("OK") )
{
esp8266.println( "RECEIVED: OK" );
}
else
{
esp8266.println( "RECEIVED: Error\nExit2" );
}
}
void sendesp8266(String cmd) //Send info to esp8266
{
esp8266.print("SEND: ");
esp8266.println(cmd);
Serial.println(cmd);
}
boolean connectWiFi()
{
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
Serial.println("AT+CWMODE=1");//WiFi STA mode - if '3' it is both client and AP
delay(5000);
//Connect to Router with AT+CWJAP="SSID","Password";
// Check if connected with AT+CWJAP?
String cmd="AT+CWJAP=\""; // Join accespoint
cmd+=SSID;
cmd+="\",\"";
cmd+=PASS;
cmd+="\"";
sendesp8266(cmd);
delay(15000);
digitalWrite(12, LOW);
if(Serial.find("OK"))
{
esp8266.println("RECEIVED: OK");
return true;
}
else
{
esp8266.println("RECEIVED: Error");
return false;
}
cmd = "AT+CIPMUX=0";// Set Single connection
sendesp8266( cmd );
if( Serial.find( "Error") )
{
esp8266.print( "RECEIVED: Error" );
return false;
}
}
float getTemp(){ //Get the water temperature
//returns the temperature from one DS18S20 in DEG Celsius
byte data[12];
byte addr[8];
if ( !ds.search(addr)) {
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.println("CRC is not valid!");
return -1000;
}
if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Device is not recognized");
return -1000;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
ds.reset_search();
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum;
}