/*
PROGRAMA DE CONTROL NANO Y ESP8266
This sketch reads several inputs and connects to thingspeak channel to store data and read at the same time talkback commands if available
If any talkback command is received it executes if there is provision for.
The available commands are described in the COMMANDS subroutine
The configurations have to be changed to account for the channelID and TalkbackID
Also take into account the speed of ESP Serial.
If ESP Serial has to be changed, "Serial1.begin" there is a separated sketch to program it with AT commands TestComunicacionESP8266_Arduino.ino
FUNCTIONS
leer_entradas reading NANO inputs
httpRequest
printWifiStatus
leerSerial
extraerComando extrae el comando y lo ejecuta
isNumeric comprueba si una cadena es numero
*/
#include "WiFiEsp.h"
#include <avr/wdt.h>
// Emulate Serial1 on pins 6/7 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial Serial1(8, 7); // RX, TX
#endif
// ############## CONFIG START ##################################
#define NombrePrograma "FarmWaterControl_NANO_V14_BOX_1"
//Nombre identificador del punto de acceso para conectarse
//char ssid[] = "WLAN_0F"; // your network SSID (name)
//char pass[] = "Z0002CFBA620F"; // your network password
//char ssid[] = "MOVISTAR_C490"; // your network SSID (name)
//char pass[] = "bTj44KamhyhcPap3KCTa"; // your network password
//char ssid[] = "GUIMAR"; // your network SSID (name)
//char pass[] = "GUIMAR552"; // your network password
char ssid[] = "WLAN_8171"; // your network SSID (name)
char pass[] = "2424528LJGEGHHEH7161"; // your network password
//char ssid[] = "WLAN_8172_PEPE"; // your network SSID (name)
//char pass[] = "2424528LJGEGHHEH7161"; // your network password
//char ssid[] = "WLAN_6F88"; // your network SSID (name)
//char pwd[] = "a3b9db88d86e02d4db7e"; // your network password
//char ssid[] = "LasTresPalmeras"; // las tres palmeras por alguna razon quizas de configuracion no es capaz de leer el comando recibido (quizas puerto cerrado?)
//char pass[] = "20200916";
//char ssid[] = "TP-LINK_7F12"; // your network SSID (name)
//char pass[] = "95569321"; // your network password
byte ChId[8] = {2,3,4,5,6,9,10,11}; //Asignacion del pin de arduino a cada canal
char api_key[] = "U1F3WFKW1I1IZG38";
char talkback_key[] = "GCOX4IAD8OQNEZT6";
const unsigned long tiempo_datos_fast=30000;
const unsigned long tiempo_datos_slow=60000;
unsigned long postingInterval = 60000L; // delay between updates, in milliseconds
// ############# Config ENDS ###################################################
byte status = WL_IDLE_STATUS; // the Wifi radio's status
unsigned long lastConnectionTime = millis()-postingInterval; // last time you connected to the server, in milliseconds
// Soporte para los tiempos de riego dado en los comandos
byte TiempoRiego = 0;
unsigned long MillisInicio = 0;
bool CanalActivado = false; // Si hay cualquier canal activado, el valor pasa a 1 y se activa la actualizacion rapida
byte tiempos[8] = {0,0,0,0,0,0,0,0}; // tiempo 0 es que esa posicion esta vacía
byte c_atrib2[8] = {99,99,99,99,99,99,99,99}; // canal 99 significa vacio
byte c_atrib3[8] = {99,99,99,99,99,99,99,99};
byte c_atrib4[8] = {99,99,99,99,99,99,99,99};
byte c_atrib5[8] = {99,99,99,99,99,99,99,99};
// Initialize the Ethernet client object
WiFiEspClient client;
//-----------------------------
void setup()
{
// Añadir AL PRINCIPIO de la función setup()
wdt_disable();
// initialize digital pin as an output.
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
// initialize serial for debugging
Serial.begin(115200);
//Serial.println(NombrePrograma);
// initialize serial for ESP module
Serial1.begin(9600);
// initialize ESP module
WiFi.init(&Serial1);
// check for the presence of the shield
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue
while (true);
}
// attempt to connect to WiFi network
checkwifi();
postingInterval=tiempo_datos_slow;
// Añadir AL FINAL de la función setup()
// codigo que corrige el bucle infinito cuando entra en reset segun
// https://forum.arduino.cc/t/arduino-nano-wdt-problem/492906
// aunque el autor indica que son 4 segundos, este sketch se reinicia cada
// 10 segundos
wdt_reset();
//set up WDT interrupt
WDTCSR = (1 << WDCE) | (1 << WDE);
//Start watchdog timer with 4s prescaller
WDTCSR = (1 << WDIE) | (1 << WDE) | (1 << WDP3) | (1 << WDP0);
// wdt_enable(WDTO_8S);
}
//---------------------
void loop()
{
wdt_reset();
// Read the Serial port of the ESP is data is available
if (client.available()) {
int i = leerSerial();
}
// Si hay algún canal activado, la frecuencia de actualizacion es mas rapida
if (CanalActivado){
postingInterval = tiempo_datos_fast;
}
else {
postingInterval = tiempo_datos_slow;
}
// Control para ver si se ha llegado al final del tiempo de riego
if ((((millis()-MillisInicio))>TiempoRiego*60000)&(TiempoRiego>0)) {
// Serial.print("----> El tiempo de riego ha pasado, se para todo ");
// Serial.println(millis()-MillisInicio);
// ha terminado un temporizador, vemos si la pila esta vacía y si no activamos otro
TiempoRiego=0;
MillisInicio=0;
comandos("OFF","ALL","","","");
}
//
// si no hay ningun canal activado, comprobamos si hay pendiente alguna secuencia de riego
if (!CanalActivado) {
for (int i=0; i < 8; i++) {
if (tiempos[i] != 0) {
comandos(String(tiempos[i]),String(c_atrib2[i]),String(c_atrib3[i]),String(c_atrib4[i]),String(c_atrib5[i]));
TiempoRiego = tiempos[i];
MillisInicio = millis();
tiempos[i] = 0; //eliminamos esa posicion de pila
c_atrib2[i] = 99;
c_atrib3[i] = 99;
c_atrib4[i] = 99;
c_atrib5[i] = 99;
break;
}
}
}
// if N seconds have passed since your last connection
// then connect again and send data
if (millis() - lastConnectionTime > postingInterval) {
// Reading input data
String stringdata = leer_entradas();
if (stringdata != "") {
Serial.print("Data:");
Serial.println(stringdata);
// httpRequest("POST /update?" + stringdata + "&api_key=" + String(api_key) + "&talkback_key=" + String(talkback_key) + " HTTP/1.1");
httpRequest("GET /update?" + stringdata + "&api_key=" + String(api_key) + "&talkback_key=" + String(talkback_key) + " HTTP/1.1");
}
else {
Serial.println("Error on data");
}
}
}
//-------------------------
//////////// CHECK WIFI
/*
* Version 05/2021
*
*
*/
void checkwifi() {
int debug=3;
int chk;
int loops=1;
bucle:
wdt_reset();
chk=1;
while ((WiFi.status() == WL_DISCONNECTED)&(chk!=0)) {
Serial.print("Intento WiFi.begin ");
Serial.println(chk);
WiFi.begin(ssid, pass);
delay(2000);
chk++;
wdt_reset();
if (chk>=20) {
// initialize ESP module
Serial.print("Reiniciando ESP ");
WiFi.init(&Serial1);
WiFi.begin(ssid, pass);
}
}
IPAddress ip = WiFi.localIP();
if (debug>2) {
Serial.print("IP Address: ");
Serial.println(ip);
}
int i=0;
while (!client.connected()) {
client.connect("api.thingspeak.com", 80);
wdt_reset();
delay(1000);
Serial.print("Intento Conexion ");
Serial.println(i);
long rssi = WiFi.RSSI();
// print the received signal strength
if (debug>2) {
Serial.print("RSSI:");
Serial.print(rssi);
Serial.println(" dBm");
}
i++;
if (i>5) {
Serial.println("Reset ESP ");
WiFi.reset();
loops++;
if (loops>5) {
break;
}
Serial.print("CheckWifi loops ");
Serial.println(loops);
goto bucle;
}
}
}
//////////// FINAL CHECK WIFI
String leer_entradas(){
String fielddata = "";
int dat;
char cstr[2];
CanalActivado = false;
for (int i=0;i<=7;i++){
char field[7] = "field";
if (digitalRead(ChId[i]) == 1){
CanalActivado = true;
}
dat = char(digitalRead(ChId[i]));
itoa(i+1, cstr, 10);
strcat(field,cstr);
//Serial.println(field);
if (i==0){
fielddata = field;
fielddata += "=";
itoa(dat, cstr, 10);
fielddata += cstr;
//Serial.println(fielddata);
}
else {
fielddata += "&";
fielddata += field;
fielddata += "=";
itoa(dat, cstr, 10);
fielddata += cstr;
//Serial.println(fielddata);
}
}
Serial.println(fielddata);
return fielddata;
}
//-------------------------------
// this method makes a HTTP connection to the server
void httpRequest(String Resp)
{
Serial.println();
checkwifi();
// close any connection before send a new request
// this will free the socket on the WiFi shield
client.stop();
// if there's a successful connection
if (client.connect("api.thingspeak.com", 80)) {
Serial.println("Con...");
// send the HTTP PUT request
client.println(Resp);
// client.println(F("GET /update?key=U1F3WFKW1I1IZG38&field1=0&field2=0&field3=0&field4=0&field5=0&field6=0&field7=0&field8=0 HTTP/1.1"));
// client.println(F("POST https://api.thingspeak.com/update?field1=0&field2=0&field3=0&field4=0&field5=0&field6=0&field7=0&field8=0&api_key=M2A8396KY39PQVGT&talkback_key=GCOX4IAD8OQNEZT6 HTTP/1.1"));
client.println(F("Host: api.thingspeak.com"));
client.println("Connection: close");
client.println();
// Read the Serial port of the ESP is data is available
if (client.available()) {
int i = leerSerial();
}
// note the time that the connection was made
lastConnectionTime = millis();
}
else {
// if you couldn't make a connection
Serial.println("Con failed");
}
}
//------------------------
void printWifiStatus()
{
// print the SSID of the network you're attached to
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address
IPAddress ip = WiFi.localIP();
Serial.print("IP: ");
Serial.println(ip);
// print the received signal strength
long rssi = WiFi.RSSI();
Serial.print("RSSI: ");
Serial.print(rssi);
Serial.println(" dBm");
}
int leerSerial(){
//devuelve el numero de caracteres leido en la puerta Serial1 del cliente definido
String linea = "";
String comando = "";
int c;
int i=0;
while ((c = client.read()) > 0) {
// get the new byte:
char inChar = (char)c;
//Serial.write(inChar);
i += 1;
linea += inChar;
if (inChar == '\n') {
//linea terminada
Serial.print(linea);
//identificamos el mensaje en la linea, buscando HTTP/1.1 200 OK
if (linea.indexOf("200 OK") != -1) {Serial.println("Llamada correcta");}
//buscando si se ha recibido una comando "TURN_" en esa linea para procesarla
int k = linea.indexOf("TURN_");
if (k != -1) {
Serial.print("comando :");
comando = linea;
Serial.print(comando);
}
linea = "";
}
}
//Serial.println(linea);
if (comando != "") {
extraerComando(comando);
}
Serial.print("caracteres leidos; ");
Serial.println(i);
return i;
}
void extraerComando(String resp) {
String atrib_1 = "";
String atrib_2 = "";
String atrib_3 = "";
String atrib_4 = "";
String atrib_5 = "";
int s_atrib_1 = resp.indexOf('_');
int s_atrib_2 = resp.indexOf('_',s_atrib_1 + 1);
int s_atrib_3 = resp.indexOf('_',s_atrib_2 + 1);
int s_atrib_4 = resp.indexOf('_',s_atrib_3 + 1);
int s_atrib_5 = resp.indexOf('_',s_atrib_4 + 1);
//lectura del primer atributo
if ((s_atrib_1 >= 0) & (s_atrib_2 >=0)) {
atrib_1 = resp.substring(s_atrib_1+1,s_atrib_2);
}
else {
atrib_1 = resp.substring(s_atrib_1+1,resp.length());
}
//lectura del segundo atributo
if ((s_atrib_2 >= 0) & (s_atrib_3 >=0)) {
atrib_2 = resp.substring(s_atrib_2+1,s_atrib_3);
}
else {
atrib_2 = resp.substring(s_atrib_2+1,resp.length());
}
//lectura del tercer atributo
if ((s_atrib_3 >= 0) & (s_atrib_4 >=0)) {
atrib_3 = resp.substring(s_atrib_3+1,s_atrib_4);
}
else {
atrib_3 = resp.substring(s_atrib_3+1,resp.length());
}
//lectura del cuarto atributo
if ((s_atrib_4 >= 0) & (s_atrib_5 >=0)) {
atrib_4 = resp.substring(s_atrib_4+1,s_atrib_5);
}
else {
atrib_4 = resp.substring(s_atrib_4+1,resp.length());
}
//lectura del quinto y ultimo atributo
if (s_atrib_5 >= 0) {
atrib_5 = resp.substring(s_atrib_5+1,resp.length());
}
// antes de nada comprobamos si en el comando viene una programación de riego y si ya se está regando con algun canal activado
if ((CanalActivado)&(isNumeric(atrib_1))) {
//ponemos el tiempo y los canales correspondientes en la pila, en la posicion que este vacía
for (int i=0; i < 8; i++){
if (tiempos[i] == 0) {
tiempos[i] = atrib_1.toInt();
if (isNumeric(atrib_2)) {c_atrib2[i] = atrib_2.toInt();}
if (isNumeric(atrib_3)) {c_atrib3[i] = atrib_3.toInt();}
if (isNumeric(atrib_4)) {c_atrib4[i] = atrib_4.toInt();}
if (isNumeric(atrib_5)) {c_atrib5[i] = atrib_5.toInt();}
break;
}
}
}
else {
comandos(atrib_1,atrib_2,atrib_3,atrib_4,atrib_5);
}
}
void comandos(String atrib_1,String atrib_2,String atrib_3,String atrib_4,String atrib_5){
// Serial.println("-" + atrib_1 + "-" + atrib_2 + "-" + atrib_3 + "-" + atrib_4 + "-" + atrib_5);
//TABLA DE EJECUCION
// primer atributo
if (atrib_1 == "ON")
{
//atrib_1 es "ON"
//------
// ------- segundo atributo
if (isNumeric(atrib_2)) {
if (atrib_2.toInt()<8) {digitalWrite(ChId[atrib_2.toInt()], HIGH);}
CanalActivado = true;
}
else if (atrib_2 == "ALL")
{
for (int i=0;i<=7;i++)
{
digitalWrite(ChId[i], HIGH);
CanalActivado = true;
}
}
else
{
Serial.println("Atributo 2 " + atrib_2 + " desconocido");
}
//------
}
else if (atrib_1 == "OFF")
{
//atrib_1 es "OFF"
//------
// ----- segundo atributo
if (isNumeric(atrib_2)) {
if (atrib_2.toInt()<8) {digitalWrite(ChId[atrib_2.toInt()], LOW);}
}
else if (atrib_2 == "ALL")
{
for (int i=0;i<=7;i++)
{
digitalWrite(ChId[i], LOW);
}
}
else
{
Serial.println("Atributo 2 " + atrib_2 + " desconocido");
}
//------
}
else if (atrib_1 == "RESET")
{
//atrib_1 es "RESET" por lo tanto hay que parar inmediatamente todos los canales y poner las pilas a cero
//------
comandos("OFF","ALL","","","");
for (int i=0; i < 8; i++){
tiempos[i] = 0;
c_atrib2[i] = 99;
c_atrib3[i] = 99;
c_atrib4[i] = 99;
c_atrib5[i] = 99;
}
//------
}
else if ((isNumeric(atrib_1)&(!(CanalActivado))))
{
//atrib_1 es un numero entero y por lo tanto ha de arrancarse con los minutos indicados en ese valor los canales que se encuentren despues
// en los atributos 2 a 5 de tal manera que en una sola orden se puede arrancar por los minutos especificados varios canales simultaneamente
// se inicia tambien el contador de minutos de ejecucion de esta orden.
// si se vuelve a recibir un comando de este mismo tipo se resetea la situacion anterior parando todos los canales y fijando la nueva situación
//------
// comandos("OFF","ALL","","","");
TiempoRiego = atrib_1.toInt();
MillisInicio = millis(); //millis inicio del contador de riego, si MillisInicio <>0 entonces tenemos arrancada la secuencia
Serial.print("TiempoRiego: ");
Serial.println(TiempoRiego);
Serial.print("MillisInicio: ");
Serial.println(TiempoRiego);
// ------- segundo atributo
if (isNumeric(atrib_2)) {
//arrancamos el canal encontrado en el atributo
if (atrib_2.toInt()<8) {digitalWrite(ChId[atrib_2.toInt()], HIGH);}
CanalActivado = true;
}
// ------- tercer atributo
if (isNumeric(atrib_3)) {
//arrancamos el canal encontrado en el atributo
if (atrib_3.toInt()<8) {digitalWrite(ChId[atrib_3.toInt()], HIGH);}
CanalActivado = true;
}
// ------- cuarto atributo
if (isNumeric(atrib_4)) {
//arrancamos el canal encontrado en el atributo
if (atrib_4.toInt()<8) {digitalWrite(ChId[atrib_4.toInt()], HIGH);}
CanalActivado = true;
}
// ------- quinto atributo
if (isNumeric(atrib_5)) {
//arrancamos el canal encontrado en el atributo
if (atrib_5.toInt()<8) {digitalWrite(ChId[atrib_5.toInt()], HIGH);}
CanalActivado = true;
}
}
else
{
// do Thing C
Serial.println("Atributo 1 " + atrib_1 + " desconocido");
}
// final tabla de ejecucion
}
/////////////
boolean isNumeric(String str) {
unsigned int stringLength = str.length();
if (stringLength == 0) {
return false;
}
boolean seenDecimal = false;
for(unsigned int i = 0; i < stringLength; ++i) {
if (isDigit(str.charAt(i))) {
continue;
}
if (str.charAt(i) == '.') {
if (seenDecimal) {
return false;
}
seenDecimal = true;
continue;
}
return false;
}
return true;
}
/////////////
Loading
esp-01
esp-01