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;
}
@user1029296, 👍1
Обсуждение3 ответа
Вместо использования задержки вы можете использовать некоторую переменную (возможно, структуру или объект) для сохранения статуса вашей передачи или чтения; все, что нуждается в задержке, стало новым состоянием в этой машине состояний, каждый цикл, основанный на вашем статусе, вы проверяете, есть ли информация для выполнения следующего шага.
У вас может быть несколько объектов со статусом, но если они совместно используют ресурс, вам нужно будет добавить уровень абстракции на этот ресурс, чтобы позаботиться об этом. (Например, система событий, маршрутизатор сообщений или буфер вывода)
Также у вас может быть статус, в котором вы ждете статуса в другом конечном автомате.
Спасибо за ваше сообщение. Не могли бы вы привести пример?, @user1029296
я думаю, вам нужно изучить какой-нибудь базовый шаблон программирования, в данном случае мы говорим о "конечном автомате", см. http://en.wikipedia.org/wiki/Finite-state_machine, @Lesto
Как уже отмечалось,
Вы можете использовать цикл while для своих целей:
while(adc/send/recive_flag == 1){
// делать что-нибудь
}
вместо очень плохой привычки использовать задержку. (Вам не нужно ждать дольше, чем нужно)
Что касается ЖК-и RGB-светодиодов, используйте их внутри прерывания. Сделайте таймер прерывания примерно 50 мс. Таким образом, независимо от того, какой основной код вы запускаете, ISR будет отключен, а ЖК-дисплей и RGB будут обновлены.
Захват данных (освещенность, температура и т.д.)
Используйте выделенные регистры atmega для считывания АЦП. analogRead работает МЕДЛЕННО. Я думаю, что вы можете найти информацию об этом по всему Google.
Загружайте данные в thingspeak каждые 5 минут
Все должно быть в порядке. При необходимости увеличьте СКОРОСТЬ передачи в бодах.
По мне, так это выглядит, вам нужно подождать сбора данных, для этого нет решения, так что сделайте это быстрее.
Что касается Wi-Fi, отключите ISR, отправьте одно сообщение, включите ISR и подождите немного и так далее. Что - то вроде этого может сработать.
Что касается кормления рыбы, все, что вы делаете, это посылаете ШИМ на сервопривод, верно? Используйте выделенные регистры ШИМ на atmega для более быстрого выполнения, но в любом случае это не имеет значения для ЖК-дисплея и RGB-светодиода, потому что они находятся в режиме прерывания, поэтому они в любом случае будут обновлены.
Вы можете использовать несколько подходов.
Лесто уже указывал на конечный автомат, но при таком подходе код будет выполняться как можно быстрее, и это может быть не то поведение, которое вам нужно. Это хороший подход для управления, например, подключениями 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 минут, не блокируя выполнение других задач.
- Отправка данных из ESP8266 в PHP
- NODEMCU 1.0 HX711 Тензодатчик читает странный текст
- При компиляции я получаю #endif без ошибки #if. Вот код. Пожалуйста помоги!
- Почему вывод отправки влияет на вывод приема с помощью библиотеки емкостных датчиков Arduino
- У меня есть код, печатающий нужные мне данные, но я не знаю, как подключить данные к IP-веб-серверу ESP8266.
- AT-команда не отвечает на последовательный монитор
- Отправка данных Arduino в MySQL с помощью phpMyAdmin и XAMPP на Windows10
- Arduino выводит значения мусора на serial monitor с ESP8266
Я бы посоветовал использовать таймер для того, чтобы тоже что-то делать. Так как таймер один можно использовать для того, чтобы делать что-то бок о бок с основной программой. Просто найдите в Google timer one arduino и прочитайте учебник., @Handoko
Ты посылаешь команды АТ, хеа? Обычно они дают ответ, я бы предпочел проверить ответ, а не использовать задержку, а затем еще одну команду AT, чтобы проверить его?, @Paul
Если я правильно вас понимаю, то зачем вам читать показания датчиков при загрузке данных? Даже на полноценном КОМПЬЮТЕРЕ вы сначала собираете данные, а потом действуете и отчитываетесь., @JJ_Jason