1

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 Feb 17 '15 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? – Paul Apr 19 '15 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 Feb 5 '16 at 22:21
0

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.

0

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.

0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.