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;
}
-
I'd suggest to use the timer one to do stuff as well. Since the timer one can be used to do things along side with the main program. Just search on Google for timer one arduino and read the tutorial.Handoko– Handoko2015年02月17日 06:14:30 +00:00Commented Feb 17, 2015 at 6:14
-
You're sending AT commands hea? Usually they'll give an response, I'd rather check the response instead of using a delay and then another AT command to verify it?aaa– aaa2015年04月19日 09:50:24 +00:00Commented Apr 19, 2015 at 9:50
-
If i follow you correctly, why would you read sensors while uploading data? Even on a full pc you would first collect data then act and report.JJ_Jason– JJ_Jason2016年02月05日 22:21:43 +00:00Commented Feb 5, 2016 at 22:21
3 Answers 3
As already pointed out,
You can use a while loop for your purposes:
while(adc/send/recive_flag == 1){
// do stuff
}
instead of the very bad habit of using delay. (You don't need to wait any longer than you have to)
As for LCD and RGB leds, use them inside an interrupt. Make interrupt timer something like 50 ms. This way, whatever main code you are running, ISR will timout and LCD and RGB will be updated.
Capture data (light, temperature, etc.)
Use dedicated atmega registers to read ADC. AnalogRead is SLOW. I think you can find information about this all over google.
Upload data to thingspeak every 5 minutes
This should be fine. Increase transmission BAUD RATE if necessary.
To me the way it looks, you have to wait for data capture, theres no solution for that so make it faster.
As for Wifi, disable ISR, send a single message, enable ISR and wait for a bit and so on. Something like this could work.
As for feeding the fish, all you do is send PWM to servo, right? Use dedicated PWM registers on atmega for faster execution but it doesn't matter anyway for the LCD and RGB LED because they are in interrupt routine so they will be updated anyways.
You can use several approaches.
Lesto already pointed out the finite state machine, but this approach will run the code as fast as possible and this might not be the behaviour you want. It's a good approach to manage, for instance, wifi connections and other communications but it won't let you schedule a task in a fixed pace.
Other options:
1) In the arduino default examples there's "Blink without delay" (\examples\digital), here you can read the code explanation. It's different from what you used
(while(now < endtime)
)
because your code execution is kept inside the while.
If you otherwise use an
if (currentMillis - previousMillis >= interval)
the code will do the check but it continues to run other instructions until the right time has come.
2) Use a scheduler! This is handy if you have several different tasks that should run at a constant (and relatively slow for the MCU) pace.
For instance:
#include "leOS.h"
leOS myOS
void setup() {
myOS.begin();
}
void setup() {
myOS.begin();
myOS.addTask(updateTS, scheduleTime[30000, SCHEDULED]);
}
With this code you'll run the updateTS function every 5 min, without blocking the execution of other tasks.
Instead of using a delay, you may use some variable (maybe a struct or an object) to keep the status of your transmission or reading; every things that need a delay became a new state in this state-machine, every loop, based on witch status you have, you check if there are the information to execute the next step.
You may have multiple thing with status, but if they share a resource you'll need to add an abstraction layer on that resource to take care of that. (Like an event system or a message router or an output buffer)
Also you may have a status in witch you wait for a status in another state machine.
-
Thanks for your message. Would you have an example?user1029296– user10292962015年02月18日 17:56:58 +00:00Commented Feb 18, 2015 at 17:56
-
i think you need to learn some basic programming pattern, in this case we are talking about a "finite state machine", see en.wikipedia.org/wiki/Finite-state_machineLesto– Lesto2015年02月28日 23:11:59 +00:00Commented Feb 28, 2015 at 23:11