Sensor capacitivo de humedad del suelo.

Responder
elmaya
Mensajes: 1107
Registrado: Mié Jun 27, 2018 5:48 pm
Ubicación: Sevilla

Sab Nov 14, 2020 9:12 am

Para quien esté interesado en probar el sensor capacitivo de humedad del suelo
Utilizamos un canal de temperatura y humedad.
La temperatura muestra la lectura del sensor analógico "Esto no es necesario pero no hay canal independiente para humedad así que lo usamos" muestra un valor alrededor de 700 en seco (0%) y de alrededor de 250 estando dentro de un vaso de agua (100%).
La humedad muestra la humedad del suelo entre 0% y 100%.
¡Necesita calibración para una lectura correcta!

Csm_bb.png
Csm_bb.png (107.38 KiB) Visto 331 veces

Led = D4 "Led incorporado en wemos / nodemcu"
Parpadeo rápido "0.5s" = no conectado / conectando.
Parpadeo lento "1.5s" = WiFiConfig.
Apagado = conectado Ok.

Pulsador = D3:

Presione durante al menos 5 segundos para WiFiConfig. (Nombre de WiFiAP "CSM_Probe")

3 clics para calibrar y almacenar el valor del sensor a humedad mínima 0% "expuesto al aire"
El led parpadea 3 veces.

2 clics para calibrar y almacenar el valor del sensor para la humedad máxima del 100% "sumergido en agua"
El led parpadea 2 veces.

CSM_DOUT_16Mbit.rar
(669.27 KiB) Descargado 9 veces
elmaya
Mensajes: 1107
Registrado: Mié Jun 27, 2018 5:48 pm
Ubicación: Sevilla

Mar Nov 17, 2020 2:55 pm

En vista de la gran aceptación del proyecto añado código y librerías. :lol: :lol: :lol: :lol:

Código Arduino:

Código: Seleccionar todo

#include "LittleFS.h" // LittleFS is declared 
#include <WiFiManager.h>
#include <ArduinoJson.h> //--------- V6 ------ 
#include <EEPROM.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <ESP8266TrueRandom.h>
#include <Button3.h>
#include <Ticker.h> 
#include <SuplaDevice.h>  
#include <supla/sensor/CSM.h>
#include <supla/network/esp_wifi.h>
Supla::ESPWifi wifi("", "");  //------ Do not change----wifimanager takes care------

extern "C"
{
#include "user_interface.h"
}

//#define D0 16  //no internal pullup resistor
//#define D1  5
//#define D2  4
//#define D3  0  //must not be pulled low during power on/reset, toggles value during boot
//#define D4  2  //must not be pulled low during power on/reset, toggles value during boot
//#define D5 14
//#define D6 12
//#define D7 13
//#define D8 15  //must not be pulled high during power on/reset

#define status_led 2   // D4
#define wificonfig_pin 0  // D3  WiFiconfig 

char Supla_server[81]=("Unknown server address");
char Email[81]=("Unknown email address");                        
char Supla_name[51];
char Supla_status[51];
int C_W_state = HIGH; 
int last_C_W_state = HIGH;
unsigned long time_last_C_W_change = 0; 
long C_W_delay = 5000;                      // config delay 5 seconds        
bool shouldSaveConfig = false;
bool initialConfig = false; 
char GUID[SUPLA_GUID_SIZE];
char AUTHKEY[SUPLA_AUTHKEY_SIZE];
byte uuidNumber[16];
int s;
bool starting = true; 
bool Tik = true;
Button3 buttonA = Button3(wificonfig_pin);
ESP8266WebServer httpServer(81);
ESP8266HTTPUpdateServer httpUpdater;
WiFiManager wifiManager;
Ticker ticker;

Supla::Sensor::CSM *CSM1 = nullptr;

void tick() {
  int state = digitalRead(status_led);  
  digitalWrite(status_led, !state);     
}
void saveConfigCallback () {          
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
void ondemandwifiCallback () {
 ticker.attach(1.5, tick);
   WiFiManagerParameter custom_Supla_server("server", "supla server", Supla_server, 81,"required");
   WiFiManagerParameter custom_Email("email", "Email", Email, 81,"required");
   WiFiManagerParameter custom_Supla_name("name", "Supla Device Name", Supla_name, 51,"required");
   WiFiManagerParameter custom_html_id21("<div><h4> - Supla State -   ");
   WiFiManagerParameter custom_html_id22( Supla_status);
   WiFiManagerParameter custom_html_id23( "</h4></div>");

   wifiManager.setBreakAfterConfig(true);
   wifiManager.setSaveConfigCallback(saveConfigCallback);
  
   wifiManager.addParameter(&custom_Supla_server);
   wifiManager.addParameter(&custom_Email);
   wifiManager.addParameter(&custom_Supla_name);
   wifiManager.addParameter(&custom_html_id21);
   wifiManager.addParameter(&custom_html_id22);
   wifiManager.addParameter(&custom_html_id23);

   wifiManager.setCustomHeadElement("<style>html{ background-color: #01DF3A;}</style><div class='s'><svg version='1.1' id='l' x='0' y='0' viewBox='0 0 200 200' xml:space='preserve'><path d='M59.3,2.5c18.1,0.6,31.8,8,40.2,23.5c3.1,5.7,4.3,11.9,4.1,18.3c-0.1,3.6-0.7,7.1-1.9,10.6c-0.2,0.7-0.1,1.1,0.6,1.5c12.8,7.7,25.5,15.4,38.3,23c2.9,1.7,5.8,3.4,8.7,5.3c1,0.6,1.6,0.6,2.5-0.1c4.5-3.6,9.8-5.3,15.7-5.4c12.5-0.1,22.9,7.9,25.2,19c1.9,9.2-2.9,19.2-11.8,23.9c-8.4,4.5-16.9,4.5-25.5,0.2c-0.7-0.3-1-0.2-1.5,0.3c-4.8,4.9-9.7,9.8-14.5,14.6c-5.3,5.3-10.6,10.7-15.9,16c-1.8,1.8-3.6,3.7-5.4,5.4c-0.7,0.6-0.6,1,0,1.6c3.6,3.4,5.8,7.5,6.2,12.2c0.7,7.7-2.2,14-8.8,18.5c-12.3,8.6-30.3,3.5-35-10.4c-2.8-8.4,0.6-17.7,8.6-22.8c0.9-0.6,1.1-1,0.8-2c-2-6.2-4.4-12.4-6.6-18.6c-6.3-17.6-12.7-35.1-19-52.7c-0.2-0.7-0.5-1-1.4-0.9c-12.5,0.7-23.6-2.6-33-10.4c-8-6.6-12.9-15-14.2-25c-1.5-11.5,1.7-21.9,9.6-30.7C32.5,8.9,42.2,4.2,53.7,2.7c0.7-0.1,1.5-0.2,2.2-0.2C57,2.4,58.2,2.5,59.3,2.5z M76.5,81c0,0.1,0.1,0.3,0.1,0.6c1.6,6.3,3.2,12.6,4.7,18.9c4.5,17.7,8.9,35.5,13.3,53.2c0.2,0.9,0.6,1.1,1.6,0.9c5.4-1.2,10.7-0.8,15.7,1.6c0.8,0.4,1.2,0.3,1.7-0.4c11.2-12.9,22.5-25.7,33.4-38.7c0.5-0.6,0.4-1,0-1.6c-5.6-7.9-6.1-16.1-1.3-24.5c0.5-0.8,0.3-1.1-0.5-1.6c-9.1-4.7-18.1-9.3-27.2-14c-6.8-3.5-13.5-7-20.3-10.5c-0.7-0.4-1.1-0.3-1.6,0.4c-1.3,1.8-2.7,3.5-4.3,5.1c-4.2,4.2-9.1,7.4-14.7,9.7C76.9,80.3,76.4,80.3,76.5,81z M89,42.6c0.1-2.5-0.4-5.4-1.5-8.1C83,23.1,74.2,16.9,61.7,15.8c-10-0.9-18.6,2.4-25.3,9.7c-8.4,9-9.3,22.4-2.2,32.4c6.8,9.6,19.1,14.2,31.4,11.9C79.2,67.1,89,55.9,89,42.6z M102.1,188.6c0.6,0.1,1.5-0.1,2.4-0.2c9.5-1.4,15.3-10.9,11.6-19.2c-2.6-5.9-9.4-9.6-16.8-8.6c-8.3,1.2-14.1,8.9-12.4,16.6C88.2,183.9,94.4,188.6,102.1,188.6z M167.7,88.5c-1,0-2.1,0.1-3.1,0.3c-9,1.7-14.2,10.6-10.8,18.6c2.9,6.8,11.4,10.3,19,7.8c7.1-2.3,11.1-9.1,9.6-15.9C180.9,93,174.8,88.5,167.7,88.5z'/></svg>");
   wifiManager.setMinimumSignalQuality(8);
   //wifiManager.setShowStaticFields(true); // force show static ip fields
   //wifiManager.setShowDnsFields(true);    // force show dns field always
   wifiManager.setConfigPortalTimeout(180);

   if (!wifiManager.startConfigPortal("CSM_Probe")) { Serial.println("Not connected to WiFi but continuing anyway.");} else { Serial.println("connected...yeey :)");}                
    strcpy(Supla_server, custom_Supla_server.getValue());
    strcpy(Email, custom_Email.getValue());
    strcpy(Supla_name, custom_Supla_name.getValue()); 
    if(strcmp(Supla_server, "get_new_guid_and_authkey") == 0){
      Serial.println("new guid & authkey.");
      EEPROM.write(300, 0);
      EEPROM.commit();
      ESP.reset(); 
    }
    WiFi.softAPdisconnect(true);   //  close AP
    ticker.detach();
    digitalWrite(status_led, HIGH);    
}
void status_func(int status, const char *msg) {    //    ------------------------ Status --------------------------
  if (s != status){
    s = status; 
      if (s != 10){
        strcpy(Supla_status, msg); 
  }  }            
}
void guid_authkey(void) {
  if (EEPROM.read(300) != 60){
    int eep_gui = 301;

    ESP8266TrueRandom.uuid(uuidNumber);

    String uuidString = "";
    for (int i = 0; i < 16; i++) {
      int topDigit = uuidNumber[i] >> 4;
      int bottomDigit = uuidNumber[i] & 0x0f;
      uuidString += "0123456789abcdef"[topDigit];
      uuidString += "0123456789abcdef"[bottomDigit];
    }
    int length_uuid = uuidString.length();
    for (int i = 0; i < length_uuid; ++i) {
      EEPROM.put(eep_gui + i, uuidString[i]);
    }

    int eep_aut = 341;

    ESP8266TrueRandom.uuid(uuidNumber);

    String uuidString2 = "";
    for (int i = 0; i < 16; i++) {
      int topDigit = uuidNumber[i] >> 4;
      int bottomDigit = uuidNumber[i] & 0x0f;
      uuidString2 += "0123456789abcdef"[topDigit];
      uuidString2 += "0123456789abcdef"[bottomDigit];
    }
    int length_uuid2 = uuidString2.length();
    for (int i = 0; i < length_uuid2; ++i) {
      EEPROM.put(eep_aut + i, uuidString2[i]);
    }
    EEPROM.write(300, 60);
    EEPROM.commit();
  }
  read_guid();
  read_authkey();
  Serial.print("GUID : ");Serial.println(read_guid()); 
  Serial.print("AUTHKEY : ");Serial.println(read_authkey()); 
}

String read_guid(void) {
  String read_eeprom = "";
  int i, ii = 0;
  int eep_star = 301;
  int end_guid = eep_star + SUPLA_GUID_SIZE;
  String temp_read = "0x";
  for (i = eep_star; i < end_guid + 16;  i = i + 1) {
    temp_read += char(EEPROM.read(i));
    read_eeprom += char(EEPROM.read(i));
    if ( (i % 2) == 0) {
      char *_guid = strcpy((char*)malloc(temp_read.length() + 1), temp_read.c_str());
      GUID[ii] = strtoul( _guid, NULL, 16);
      temp_read = "0x";
      ii++;
    }
  }
  return read_eeprom;
}
String read_authkey(void) {
  String read_eeprom = "";
  int i, ii = 0;
  int eep_star = 341;
  int end_authkey = eep_star + SUPLA_AUTHKEY_SIZE;
  String temp_read = "0x";
  for (i = eep_star; i < end_authkey + 16;  i = i + 1) {
    temp_read += char(EEPROM.read(i));
    read_eeprom += char(EEPROM.read(i));
    if ( (i % 2) == 0) {
      char *_authkey = strcpy((char*)malloc(temp_read.length() + 1), temp_read.c_str());
      AUTHKEY[ii] = strtoul( _authkey, NULL, 16);
      temp_read = "0x";
      ii++;
    }
  }
  return read_eeprom;
}

void setup() {

   wifi_set_sleep_type(NONE_SLEEP_T); 
    
  Serial.begin(115200);  
  pinMode(wificonfig_pin, INPUT_PULLUP);
  pinMode(status_led,OUTPUT); 
  ticker.attach(0.5, tick);
  
  EEPROM.begin(1024);
  if (EEPROM.read(300) != 60){initialConfig = true;} 
  guid_authkey();

  if (WiFi.SSID()==""){ initialConfig = true;} 

  buttonA.setClickHandler(click);
  buttonA.setDoubleClickHandler(doubleClick);
  buttonA.setTripleClickHandler(tripleClick);

  if (LittleFS.begin()) {  // ------------------------- wificonfig read -----------------
    Serial.println("mounted file system");
    if (LittleFS.exists("/config.json")) {
      Serial.println("reading config file");
       File configFile = LittleFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
         size_t size = configFile.size();
         std::unique_ptr<char[]> buf(new char[size]);
         configFile.readBytes(buf.get(), size);
        DynamicJsonDocument json(1024);
        DeserializationError deserializeError = deserializeJson(json, buf.get());
        serializeJsonPretty(json, Serial);
        if (!deserializeError) {Serial.println("\nparsed json");         
          if (json.containsKey("Supla_server")) strcpy(Supla_server, json["Supla_server"]);
          if (json.containsKey("Email")) strcpy(Email, json["Email"]);
          if (json.containsKey("Supla_name")) strcpy(Supla_name, json["Supla_name"]);       
        } else {
          Serial.println("failed to load json config");
           initialConfig = true;
        }
        configFile.close(); 
      }
    }
   } else {
    Serial.println("failed to mount FS");
  }
   wifi_station_set_hostname(Supla_name);
   
  WiFi.mode(WIFI_STA); // Force to station mode because if device was switched off while in access point mode it will start up next time in access point mode.

  SuplaDevice.setStatusFuncImpl(&status_func);
  SuplaDevice.setName(Supla_name);
  wifi.enableSSL(false); 
   
  CSM1 = new Supla::Sensor::CSM(A0,100); // pin, EEPROM offset

     SuplaDevice.begin(GUID,Supla_server,Email,AUTHKEY);
        
}

void loop() {
   if (initialConfig == true){ondemandwifiCallback();}

   int C_W_read = digitalRead(wificonfig_pin);{  
   if (C_W_read != last_C_W_state) {            
     time_last_C_W_change = millis();
   }
   if ((millis() - time_last_C_W_change) > C_W_delay) {     
     if (C_W_read != C_W_state) {     
       C_W_state = C_W_read;       
       if (C_W_state == LOW) {
        ondemandwifiCallback () ;
       }
     }
    }
   last_C_W_state = C_W_read;            
 }
  
  if (shouldSaveConfig == true) { // ------------------------ wificonfig save --------------
    Serial.println(" config...");  
    DynamicJsonDocument json(1024);
    json["Supla_server"] = Supla_server;
    json["Email"] = Email;
    json["Supla_name"] = Supla_name;
    File configFile = LittleFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }
    serializeJsonPretty(json, Serial);
    serializeJson(json, configFile);
    configFile.close();
    Serial.println("Config written successfully");
    shouldSaveConfig = false;
    initialConfig = false;
    ticker.detach();
    digitalWrite(status_led, HIGH);  
    WiFi.mode(WIFI_STA);   
    delay(5000);
    ESP.restart();     
  }
  
   buttonA.loop();     
   SuplaDevice.iterate();
   delay(25);

   if (WiFi.status() == WL_CONNECTED){
    if (starting){
      httpUpdater.setup(&httpServer, "/update", "admin", "pass");
      httpServer.begin(); 
      starting = false;         
     }
    httpServer.handleClient();
   }else if (Tik == false){
    ticker.attach(0.5, tick);
    Tik = true;
   }

  if (s == 17){
    if ((Tik == true) && (WiFi.status() == WL_CONNECTED)){
      ticker.detach();
      digitalWrite(status_led, HIGH); 
      Tik = false;
      }     
  }
}

void click(Button3& btn) {

}
void doubleClick(Button3& btn) {
  CSM1->storeMax();
  ticker.detach();
  Tik = false;
  digitalWrite(status_led, HIGH); 
  delay(200);
  digitalWrite(status_led, LOW); 
  delay(200);
  digitalWrite(status_led, HIGH); 
  delay(200);
  digitalWrite(status_led, LOW); 
  delay(200);
  digitalWrite(status_led, HIGH); 
}
void tripleClick(Button3& btn) {
  CSM1->storeMin();
  ticker.detach();
  Tik = false;
  digitalWrite(status_led, HIGH); 
  delay(200);
  digitalWrite(status_led, LOW); 
  delay(200);
  digitalWrite(status_led, HIGH); 
  delay(200);
  digitalWrite(status_led, LOW); 
  delay(200);
  digitalWrite(status_led, HIGH); 
  delay(200);
  digitalWrite(status_led, LOW); 
  delay(200);
  digitalWrite(status_led, HIGH);  
}
void longClick(Button3& btn) {

}

Librería sensor capacitivo humedad suelo: CSM.h
Copiar a: “Arduino\libraries\SuplaDevice\src\supla\sensor”

Código: Seleccionar todo

#ifndef _csm_h
#define _csm_h

#include "therm_hygro_meter.h"

namespace Supla {
namespace Sensor {
class CSM: public ThermHygroMeter {
  public:
    CSM(int pin, int eepAdresse) {
      _pin = pin;
      eep_ofset = eepAdresse;
      lastValidTemp = TEMPERATURE_NOT_AVAILABLE;	
    }

    double getTemp() {

      int value = analogRead(_pin);
	  if ((value < lastValidTemp - 5 ) || (value > lastValidTemp + 5 )){
        lastValidTemp = value;	  	
	  }
      return lastValidTemp;
    }

    double getHumi() {

      int _value = analogRead(_pin);
      int value = map(_value, max_value, min_value, 100,0);

      return value;
    }


    void onInit() {
      EEPROM.get(eep_ofset,min_value);
      if ((min_value < 100) || (min_value >1023)) min_value = 1023;
      supla_log(LOG_DEBUG, "load min CSM value %d", min_value);
      EEPROM.get(eep_ofset + 5,max_value);
      if ((max_value < 0) || (max_value >1023)) max_value = 0;
	  if (max_value >= min_value) max_value = 0;
      supla_log(LOG_DEBUG, "load max CSM value %d", max_value);
      channel.setNewValue(getTemp(), getHumi());
    }

  void storeMin() {
      min_value = analogRead(_pin);
      supla_log(LOG_DEBUG, "save min CSM value %d", min_value);
      EEPROM.put(eep_ofset, min_value);
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
      EEPROM.commit();
#endif
  }
  void storeMax() {
      max_value = analogRead(_pin);
      supla_log(LOG_DEBUG, "save max CSM value %d", max_value);
      EEPROM.put(eep_ofset + 5, max_value);
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
      EEPROM.commit();
#endif
  }  

    protected:
      int8_t _pin;
      int eep_ofset;
      int min_value;
      int max_value;
      int lastValidTemp;
};
};  // namespace Sensor
};  // namespace Supla

#endif

Ejemplo básico:

Código: Seleccionar todo

#include <EEPROM.h>
#include <SuplaDevice.h>
#include <supla/sensor/CSM.h>


// ESP8266 based board:
 #include <supla/network/esp_wifi.h>
 Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password");

Supla::Sensor::CSM *CSM1 = nullptr;

 
void setup() {

  Serial.begin(115200);
  EEPROM.begin(1024);
  
  // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid
  char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

  // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey
  char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

  /*
    this uses a temperature and humidity channel
    temperature shows analog sensor reading
    humidity shows humidity between 0% and 100%
    needs calibration for a correct reading! "storeMin()" + "storeMax()"
   */
    
  CSM1 = new Supla::Sensor::CSM(A0,100); // A0 = analog pin where the sensor is connected -- 100 = eeprom offset where to save "offset + 10"

  /*
   * SuplaDevice Initialization.
   * Server address is available at https://cloud.supla.org 
   * If you do not have an account, you can create it at https://cloud.supla.org/account/create
   * SUPLA and SUPLA CLOUD are free of charge
   * 
   */

  SuplaDevice.begin(GUID,              // Global Unique Identifier 
                    "svr1.supla.org",  // SUPLA server address
                    "email@address",   // Email address used to login to Supla Cloud
                    AUTHKEY);          // Authorization key
    
}

void loop() {
  SuplaDevice.iterate();

  delay(25); // For ESP8266 to have time for WiFi, improve stability.

// call to calibrate and store the sensor value for minimum humidity 0% "exposed to air"
  //CSM1->storeMin();

// call to calibrate and store the sensor value for maximum humidity 100% "in a glass of water"    
  //CSM1->storeMax(); 
 

  
}
edm
Mensajes: 166
Registrado: Lun Ene 15, 2018 6:18 pm

Mar Nov 17, 2020 8:18 pm

Puedo solicitar un enlace, por ejemplo, aliexpress al sensor de humedad del suelo apropiado, que coincida con este proyecto.
elmaya
Mensajes: 1107
Registrado: Mié Jun 27, 2018 5:48 pm
Ubicación: Sevilla

Mar Nov 17, 2020 8:25 pm

elmaya
Mensajes: 1107
Registrado: Mié Jun 27, 2018 5:48 pm
Ubicación: Sevilla

Dom Nov 22, 2020 10:40 pm

Después de detectar un error que podía llevar a un bucle de reinicio si por accidente se memorizaban los mismos valores para mínimo y máximo he corregido CSM.h y el firmware compilado. :oops:
elmaya
Mensajes: 1107
Registrado: Mié Jun 27, 2018 5:48 pm
Ubicación: Sevilla

Lun Nov 23, 2020 10:42 am

Solución fácil para hacer el sensor impermeable:
Sumergir en esmalte sintético. :lol: :lol: :lol:
frontal.jpg
frontal.jpg (40.02 KiB) Visto 226 veces
trasera.jpg
trasera.jpg (49.25 KiB) Visto 226 veces
edm
Mensajes: 166
Registrado: Lun Ene 15, 2018 6:18 pm

Lun Nov 23, 2020 5:52 pm

¡Bravo tú!
Responder

Volver a “Ideas y conceptos”