Wifi Регистратор данных без Задержки

Я действительно новичок в Arduino, поэтому, пожалуйста, потерпите меня.

Я запускаю Arduino Uno с несколькими датчиками, чтобы захватить некоторые данные, связанные с аквариумом. Вот в основном, что он делает:

  • Захват данных (свет, температура и т. Д.)
  • Отображение данных на ЖК-экране при нажатии кнопки
  • Загружайте данные в thingspeak каждые 5 минут, используя ESP8266
  • Кормить рыбу каждые 4 часа через серво
  • RGB - светодиод мигает различными цветами, чтобы я мог видеть, что происходит (например, красный при подключении к Wi-Fi).

Все работает достаточно хорошо. Тем не менее, мой код очень грязный, и я не уверен, что у меня есть правильный подход. С тем кодом который у меня есть: - Ничего нельзя сделать, пока Arduino загружает Wi-Fi - Ничего нельзя сделать, когда рыба сыта.

Речь идет не столько о сервомоторе, сколько о проблеме Wi-Fi. Как бы вы посоветовали мне переписать свой код так, чтобы (1) я мог запускать Wi-Fi более одного раза в 5 минут и (2) он не блокировал мои датчики и жидкокристаллический дисплей, пока я это делаю?

Извините за плохое качество кода. Новичок здесь.

//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;

    }

, 👍1

Обсуждение

Я бы посоветовал использовать таймер для того, чтобы тоже что-то делать. Так как таймер один можно использовать для того, чтобы делать что-то бок о бок с основной программой. Просто найдите в Google timer one arduino и прочитайте учебник., @Handoko

Ты посылаешь команды АТ, хеа? Обычно они дают ответ, я бы предпочел проверить ответ, а не использовать задержку, а затем еще одну команду AT, чтобы проверить его?, @Paul

Если я правильно вас понимаю, то зачем вам читать показания датчиков при загрузке данных? Даже на полноценном КОМПЬЮТЕРЕ вы сначала собираете данные, а потом действуете и отчитываетесь., @JJ_Jason


3 ответа


0

Вместо использования задержки вы можете использовать некоторую переменную (возможно, структуру или объект) для сохранения статуса вашей передачи или чтения; все, что нуждается в задержке, стало новым состоянием в этой машине состояний, каждый цикл, основанный на вашем статусе, вы проверяете, есть ли информация для выполнения следующего шага.

У вас может быть несколько объектов со статусом, но если они совместно используют ресурс, вам нужно будет добавить уровень абстракции на этот ресурс, чтобы позаботиться об этом. (Например, система событий, маршрутизатор сообщений или буфер вывода)

Также у вас может быть статус, в котором вы ждете статуса в другом конечном автомате.

,

Спасибо за ваше сообщение. Не могли бы вы привести пример?, @user1029296

я думаю, вам нужно изучить какой-нибудь базовый шаблон программирования, в данном случае мы говорим о "конечном автомате", см. http://en.wikipedia.org/wiki/Finite-state_machine, @Lesto


1

Как уже отмечалось,

Вы можете использовать цикл while для своих целей:

while(adc/send/recive_flag == 1){
// делать что-нибудь
}

вместо очень плохой привычки использовать задержку. (Вам не нужно ждать дольше, чем нужно)

Что касается ЖК-и RGB-светодиодов, используйте их внутри прерывания. Сделайте таймер прерывания примерно 50 мс. Таким образом, независимо от того, какой основной код вы запускаете, ISR будет отключен, а ЖК-дисплей и RGB будут обновлены.

Захват данных (освещенность, температура и т.д.)

Используйте выделенные регистры atmega для считывания АЦП. analogRead работает МЕДЛЕННО. Я думаю, что вы можете найти информацию об этом по всему Google.

Загружайте данные в thingspeak каждые 5 минут

Все должно быть в порядке. При необходимости увеличьте СКОРОСТЬ передачи в бодах.

По мне, так это выглядит, вам нужно подождать сбора данных, для этого нет решения, так что сделайте это быстрее.

Что касается Wi-Fi, отключите ISR, отправьте одно сообщение, включите ISR и подождите немного и так далее. Что - то вроде этого может сработать.

Что касается кормления рыбы, все, что вы делаете, это посылаете ШИМ на сервопривод, верно? Используйте выделенные регистры ШИМ на atmega для более быстрого выполнения, но в любом случае это не имеет значения для ЖК-дисплея и RGB-светодиода, потому что они находятся в режиме прерывания, поэтому они в любом случае будут обновлены.

,

1

Вы можете использовать несколько подходов.

Лесто уже указывал на конечный автомат, но при таком подходе код будет выполняться как можно быстрее, и это может быть не то поведение, которое вам нужно. Это хороший подход для управления, например, подключениями Wi-Fi и другими коммуникациями, но он не позволит вам планировать выполнение задачи в фиксированном темпе.

Другие варианты:

1) В примерах arduino по умолчанию есть "Мигание без задержки" (\примеры\цифровые), здесь вы можете прочитать объяснение кода. Это отличается от того, что вы использовали

(пока(сейчас < время окончания))

потому что выполнение вашего кода хранится внутри while.

Если вы в противном случае используете

если (текущий миллиметр - предыдущий миллиметр >= интервал)>

код выполнит проверку, но он продолжит выполнять другие инструкции до тех пор, пока не наступит нужное время.

2) Используйте планировщик! Это удобно, если у вас есть несколько различных задач, которые должны выполняться в постоянном (и относительно медленном для MCU) темпе.

Например:

#include "leOS.h"
leOS myOS

void setup() {
  myOS.begin();
}
void setup() {
  myOS.begin();
  myOS.addTask(updateTS, scheduleTime[30000, SCHEDULED]);
}

С помощью этого кода вы будете запускать функцию обновлений каждые 5 минут, не блокируя выполнение других задач.

,