Problema con relés en datalogger autónomo

Muy buenas. Es la primera vez que escribo en el foro, aunque os he leído muchísimo. Estoy haciendo un proyecto con varios componentes. Uno de ellos, es un datalogger que toma los datos de 3 sensores de suelo y un dth11 de humedad y temperatura. El microcontrolador que uso es un arduino Nano, y los datos los envío mediante un ESP8266 que tengo conectado. Para que sea autónomo, le he conectado un cargador de baterías alimentado por una placa solar. Funciona con 2 baterías 18650. El sistema está alimentado únicamente con esas baterías. Os adjunto un pequeño esquema del circuito (lo siento, pero de momento no lo he pasado a Fritzing). Mido el voltaje de la batería para añadirlo a la información que mando y muestro el "estado de la carga" en un led. Hasta ahí todo bien.

Como el sistema debe de ser autónomo, y teniendo en cuenta que alguno de los sensores o el ESP8266 consumen bastante, los desconecto mediante los relés. El primero de los relés conecta el circuito de medición de la batería, y el segundo, mete corriente al resto de los componentes para hacer las mediciones y el envío de los datos por wifi.

La librería que he utilizado para el ESP8266 es esta:

EL problema que tengo es que, aleatoriamente, se queda frito cuando activa el segundo relé. Puede hacer 10 ciclos bien, que en algún momento del procesado del segundo relé, se queda todo encendido y no continúa el arduino. Pensé que podrían ser problemas de RAM, pero he comprobado y no se pierde nada. También había probado a hacer un reset por software, pero sigue teniendo el mismo problema. Así que todo pinta a que es un problema de hardware, y como no soy un experto ni mucho menos, pues me he decidido a preguntar, a ver si me podéis echar una mano y decirme dónde he metido la pata.

Nota: Alimento al arduino mediante el pin Vin. El resto de componentes, son alimentados desde el arduino, desde el pin de 5 voltios, que pasa previamente por uno de los relés.

Pego alguno de los componentes reales:

Agradezco cualquier ayuda de antemano.

Saludos

Adjunto el código:

#include "ESP8266.h"
#include <SoftwareSerial.h>
#include "DHT.h"


#define DHTPIN 5     // what pin we're connected to
#define DHTTYPE DHT11   // DHT 22  (AM2302)
#define sensor1 A1  //sensor humedad 1
#define sensor2 A2  //sensor humedad 2
#define sensor3 A3  //sensor humedad 3
#define releA 4     // quita la energía de los periféricos.
#define releB 9     // quita la energía a la pila
#define LEDVERDE 7
#define LEDAZUL 6
#define LEDROJO 8
#define ANALOGPILA 0  //A0

// Variables
int analogValor = 0;
float voltaje = 0;
int ledDelay = 800;

// Umbrales
float maximo = 4.0;
float medio = 3.7;
float minimo = 3.2;

const char* SSID = "WifiCasa";
const char* PASSWORD = "Periquito#21";
const char* HOST_NAME = "www.ingenieros.es";


const int HOST_PORT = 80;

const long interval = 3600000;
unsigned long previousMillis = 0;

DHT dht(DHTPIN, DHTTYPE);

SoftwareSerial softSerial(3, 2); // RX, TX
ESP8266 wifi(softSerial);
 
void setup(void) {
  
   Serial.begin(9600);
   Serial.print(F("Start\r\n"));
   Serial.print(wifi.getVersion());
   pinMode(releA, OUTPUT);
   pinMode(releB, OUTPUT);
   pinMode(LEDVERDE, OUTPUT);
   pinMode(LEDAZUL, OUTPUT);
   pinMode(LEDROJO, OUTPUT);
   dht.begin();
}
 
void loop(void)
{
  Serial.println(freeRam());
  digitalWrite(releB, LOW); //apago el relé de la batería
  digitalWrite(releA, LOW); //apago el relé de la alimebtación
  delay(2000); 
  digitalWrite(LEDVERDE, LOW);
  

  //Comprobamos el envío de datos, programado en milis
  unsigned long currentMillis = millis();

  // if ((currentMillis - previousMillis >= interval) || (previousMillis == 0)) {

  if (currentMillis - previousMillis >= interval) {

   digitalWrite(releB, HIGH); //encendemos en relé de la pila, para medir la batería.
   delay(2000);
   analogValor = analogRead(ANALOGPILA);   // Obtenemos el voltaje 
   voltaje = 0.889 * 0.0048 * analogValor;   // convertimos 

   int v1 = 0.889 * 0.0048 * analogValor *100;
   int porc_bat = map(v1, 320, 426, 0, 100);

   
   digitalWrite(releB, LOW); //apagamos el relé de la batería para que no gaste
   //Serial.print("Voltaje: ");
   //Serial.println(voltaje);
   //Serial.print("Porcentaje batería: ");
   //Serial.println(porc_bat);
   delay(500);

      
   digitalWrite(releA, HIGH); //Encendemos el relé del resto de los sensores
   delay(2500); //esperamos un poco para que se estabilice el circuito

    
   if (wifi.setOprToStationSoftAP()) {   //comprobamos los valores
      Serial.print(F("to station + softap ok\r\n"));
   }
   else {
      Serial.print(F("to station + softap err\r\n"));
      return;
   }
 
   if (wifi.joinAP(SSID, PASSWORD)) { //conectamos con el router
      Serial.print(F("Join AP success\r\n"));
      Serial.print(F("IP:"));
      Serial.println(wifi.getLocalIP().c_str());
      previousMillis = currentMillis;
   }
   else {
      Serial.print(F("Join AP failure\r\n"));
      return;
   }
 
   if (wifi.disableMUX()) { //configuramos los parámetros de envío
      Serial.print(F("single ok\r\n"));
   }
   else {
      Serial.print(F("single err\r\n"));
   }
 
   Serial.print(F("setup end\r\n"));
   
    Serial.print(F("Entramos aqui\r\n"));  
    
    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity();
    // Read temperature as Celsius
    float t = dht.readTemperature();
    // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t)) {
      Serial.println(F("Failed to read from DHT sensor!"));
      return;
    }
    
        
    int suelo1 = map(analogRead(sensor1), 0, 1023, 100, 0);
    int suelo2 = map(analogRead(sensor2), 0, 1023, 100, 0);
    int suelo3 = map(analogRead(sensor3), 0, 1023, 100, 0);

       
     if (wifi.createTCP(HOST_NAME, HOST_PORT)) {
        Serial.print(F("create tcp ok\r\n"));
     }
     else {
        Serial.print(F("create tcp err\r\n"));
        wifi.releaseTCP();
        return;
     }
    
     String data = "s1=" + String(suelo1) + "&s2=" + String(suelo2) + "&s3=" + String(suelo3) + "&b1=" + String(porc_bat)+ "&h1=" + String(h) + "&t1=" + String(t);
     String tamanio = String(data.length());
     String headers= "POST /pr1/post.php HTTP/1.1\r\nHost: www.ingenieros.es\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded;charset=UTF-8\r\nContent-Length:" + String(tamanio) + "\r\n\r\n";
   

     Serial.println(F("----paquete------"));
     headers+=data;
     Serial.println(headers);
     Serial.println(F("----------------"));
     Serial.println( headers.length() );
     
     char *request = headers.c_str();
     
     if (wifi.send((const uint8_t*)request, strlen(request))) {
        Serial.print(F("message sent ok\r\n"));
  
     }
     else {
        Serial.print(F("delivery err\r\n"));
        wifi.releaseTCP();
        return;
     }
     
     delay(5000);
     wifi.releaseTCP(); 

     digitalWrite(releA, LOW);    
     Serial.println(F("Apago relé para ahorrar batería"));
     Serial.println(freeRam());
     delay(500);
    }


  Serial.print(F("Voltaje: "));
  Serial.println(voltaje);

  // Dependiendo del voltaje mostramos un LED u otro
  if (voltaje >= maximo)
  {
    digitalWrite(LEDVERDE, HIGH);
    delay(ledDelay);
    digitalWrite(LEDVERDE, LOW);
  }
  else if (voltaje < maximo && voltaje > medio)
  {
    digitalWrite(LEDAZUL, HIGH);
    delay(ledDelay);
    digitalWrite(LEDAZUL, LOW);
  }
  else if (voltaje < medio && voltaje > minimo)
  {
    digitalWrite(LEDROJO, HIGH);
    delay(ledDelay);
    digitalWrite(LEDROJO, LOW);
  }

   // Apagamos todos los LEDs
   digitalWrite(LEDVERDE, LOW);
   digitalWrite(LEDAZUL, LOW);
   digitalWrite(LEDROJO, LOW);
   delay(15000);

}

int freeRam () 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Moderador
Por favor edita tus etiquetas y tienes muchísimas.
También debes hacer visibles las imagenes adjuntas.

Normas del foro

Cuando vea las imágenes y los enlaces te responderé.

Espero que así esté mejor. Muchas gracias por responder.

Saludos

**Moderador:**Mucho mejor, ahora se ve de que trata todo y podemos ir a cada link sin problemas.
Tu mismo notarás la diferencia.

Okay, son muchas cosas para responder.
Te aconsejo que uses un transistor MOSFET Canal P (luego te diré cual) para activar/desactivar el ESP8266.
Tienes muchos LEDs activos los que consumen y no brindan ayuda. Te aconsejo que los vayas eleminando de a uno, incluido el LED de power del NANO y los de los RELES tambien.

cada rele activado consume 65mA lo mismo que el NANO.
El ESP01 consume 150mA en TX/RX lo que es importante tambien.

No creo que tu sistema necesite estar activo todo el tiempo asi que habría que ponerlo a dormir por lapsos importantes y despertarlo cada X minutos, tomar sus datos y hacer lo que deba y a dormir de nuevo.

Eliminando leds, sacando los RELEs que no son indispensables y poniendo a dormir todo tendras gran mejora de consumo y por lo tanto autonomía.

El NANO es mi caballito de batalla pero ultimamente he descubierto el STM32 Blue Pill y te aseguro que en modo dormir o Sleep consume mucho menos que el NANO y tiene muchas mas ventajas que éste.

Muy buenas. Gracias por la respuesta. En realidad, el problema principal es el cuelgue del nano. Ya no estoy seguro de que sea a causa suya. Cuando activo el segundo relé (el que da tensión a los sensores), se queda colgado aleatoriamente, dejando ese relé encendido e impidiendo que continúe el programa. Además, gasta mucha batería hasta que lo reinicio de forma manual, pero no he encontrado cómo arreglarlo, así que agradezco cualquier consejo.

En cuanto a los leds, voy a echar un vistazo a ver cómo puedo apagarlos (no me aportan nada salvo para depuración, sinceramente). Los relés permanecen apagados todo el tiempo, tan sólo hago una medición cada hora que dura menos de 30 segundos. Por lo tanto, cuando funciona, no hay demasiado gasto. Con el panel solar recupera al 100% (aunque ahora estamos en verano, en invierno vete a saber).

Me interesa saber cómo poner el MOSFET Canal P.

Voy a intentar cambiar el módulo de relés (por si estuviese mal). ¿Crees que la alimentación es correcta?

Saludos

Hi,
Aqui yo veo que estas alimentando los relays con 5 voltios del NANO y esos relays el coil es de una resistencia baja, Otra cosa es que estas que alimentando el NANO por el VIN. Si lo alimentas por el VIN entonces necesitas un voltaje de mas de 6 voltios de entrada para compensar la perdida de voltaje del regulador de interno del NANO.Tambien estas alimentando los modulos de sensores usando los 5 voltios de salida del NANO. Lee el voltaje de 5 voltios de la salida del NANO con un voltimetro para ver que voltajes lees.

Mirando tu código encuentro varias fallas en general relacionadas con lo mismo : delay().

Es un contrasentido usar millis() y dentro usar delay()

delay() no se lleva bien con nada porque detiene la operación del microcontrolador de modo que te aconsejo que pienses en como eliminarlo de tu programación.

Veo que lo usas bien, de modo que tu error es incluir delay() en procesos cortos de no hacer nada.
Tal vez deberías reenfocar tu código usando máquina de estados Te dejo donde leerlo:

Ve a Documentación => Indice de temas tutoriales => millis() y también lee máquina de estados.

Algo que no veo en toda la operación es que no pones a dormir al NANO y al ESP01.
Eso tambien ayudaría a ahorrar batería.

El programa se complica pero ganará en fluidez y justamente menor consumo.

Si te parece te iré dando pautas al respecto, pero no me adelanto porque lo he hecho antes y luego el interesado toma otro rumbo y es lógico. Sigue siendo TU proyecto.

tauro0221:
Hi,
Aqui yo veo que estas alimentando los relays con 5 voltios del NANO y esos relays el coil es de una resistencia baja, Otra cosa es que estas que alimentando el NANO por el VIN. Si lo alimentas por el VIN entonces necesitas un voltaje de mas de 6 voltios de entrada para compensar la perdida de voltaje del regulador de interno del NANO.Tambien estas alimentando los modulos de sensores usando los 5 voltios de salida del NANO. Lee el voltaje de 5 voltios de la salida del NANO con un voltimetro para ver que voltajes lees.

Muchas gracias por la respuesta. He probado a alimentarlo también por el puerto usb (el cargador tiene las dos opciones) con el mismo resultado. El voltaje que hay entre VIN y GND a la entrada del Nano es de 5.28 voltios. ¿necesitaría algún tipo de boostup para ello?

surbyte:
Mirando tu código encuentro varias fallas en general relacionadas con lo mismo : delay().

Es un contrasentido usar millis() y dentro usar delay() ...

Gracias por la respuesta. Le echo un vistazo a la documentación que me has pasado y vuelvo, pero así por encima, te comento que probé a echar a dormir el nano y no ganaba mucho (igual lo hacía mal???). Con el ESP no tengo ni idea de hacerlo, pero viendo el consumo que tenía, me asusté, jeje. Como estoy parado con el tema hasta solucionar lo de los cuelgues, reescribiré el código para sustituir el delay por una función que haga lo mismo con milis (vamos, que entre en un bucle "tonto" sin hacer nada). Tampoco me puedo flipar porque me quedan 198 bytes de memoria al final del programa. Os aviso cuando me haya leído los manuales que me comentas.

Para determinar los cuelgues trabaja paso a paso descartando responsables.
Empieza eliminando el ESP de la ecuación que es quien usa punteros y cuestiones que son posibles de tener problemas.
Si luego de eso identificas que es el ESP entonces el siguiente paso es ir agregando funciones de a poco comunicandote con el NANO de manera MAS SEGURA.

Recuerda que el ESP trabaja a 3.3V y requiere 150mA. Si no estoy viendo mal, no encuentro fuente para el ESP y si lo alimentas con los 3.3V del NANO he ahi el problema mas importante.
En algún momento puede responderte pero cuando intentar comunicarse o recibir y exige 150mA o mas no puede recibirlos del NANO.

Confirma porque no puedo darme cuenta si estoy o no en lo correcto.

He leído los manuales que me has dicho. Todo correcto, la máquina de estados es bastante simplen en realidad. En espera (lo único que hace es esperar y encender un led), leyendo voltaje y por último leyendo sensores y enviando datos. Por ahí no veo ningún problema, parece coherente y si no me decís nada de la lógica, no creo que esté mal.

Sobre el ESP, tengo un adaptador para él. Es el esp01, os pongo por aquí cuál es. Lo que hace es convertir la corriente de 5v a 3.3v para el dispositivo:

Si el nano no puede suministrar esos 150 mA, ¿Cómo puedo alimentarlo? ¿conecto directamente el pin de 5v a la batería? En ese caso creo que sí que tendré que echarlo a dormir si consume demasiado.

Saludos y gracias

Muy buenas. Al alimentar el relé directamente de la pila se termina el problema de los cuelgues. Por lo que lo más probable es que haya sido por alimentar al ESP desde el nano.

Estoy intentando cambiar la función delay por esperar:

int esperar(int segundos) {
  for (int i = 0 ;  i  <  segundos; i++)
             LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
}

Pero el programa se cuelga si la uso (aunque sea en uno sólo de los delays, el de 15 segundos principal del programa).

Otra cosa, en otro hilo he visto que se puede referenciar para que la lectura del voltaje de la pila sea precisa. Yo no lo he conseguido. ¿Me podríais dar un ejemplo para un arduino nano?

Gracias

Hi,
Para hacer un delay puedes hacerlo como el ejemplo en el sketch adjunto.

unsigned long time;
unsigned long  tiempo_delay = 5000; //delay for 5000 milliseconds
//************************************************
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}
//************************************************
void loop() {
  // put your main code here, to run repeatedly:
//  LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
time = millis();
  do {
       // delay for 5 millisegundos
   } while (millis() < (time + tiempo_delay ));
Serial.print("Delay = "); Serial.println( millis() - time );
  delay (1000);
}
//***********************************************

Gracias, pero lo que quería era “echar” a dormir al arduino para ahorrar batería, ya que funciona con pilas 18650 y un panel solar. Supongo que esto no está bien:

int esperar(int segundos) {
  for (int i = 0 ;  i  <  segundos; i++)
             LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
}

Hi,
Si yo entiendo estas ejecutando esa instruccion de poner a domir el micro cada vez que pasa por el looop. Creo que solamente con una vez que la ejecuta es suficiente. Una vez que el micro esta dormido se queda en ese estado hasta que recibe la senal que lo despierta.

Pero lo de dormir por 8 segundos, ¿No eran sólo los 8 segundos? al menos eso he leído en un par de manuales...

Hi,
Cuando pones el micro a dormir el se queda durmiento hasta que mandes la senal para despertarlo. Ahora si quires dormirlo solamente por 8 segundos entonces cuando sales del delay entonces manda la senal para despertarlo. Seria mejor que explicaras que es lo que pretendes hacer pues yo creia que lo que quires hacer poner a domir el micro por un tiempo determinado.

Estas equivocado tauro0221, los 8 segundos son reales. El NANO en este caso se duerme solo por 8 segundos por lo tanto si quieres mas debes usar múltiplos de 8 seg para dormirlo por mas tiempo.
Durante ese estado se lo puede despertar con interrupción externa.
Pero no es que lo duermes y se queda asi indefinidamente.

Al final no conseguí dormir el Nano, pero genero con el panel solar batería suficiente. Muchas gracias por la ayuda, se puede marcar el hilo como resuelto.