Inbetriebnahme und Weiterentwicklung der Firmware für Arduino (Bienenwaage 2.0 und 5.0)

Hallo @Stefan,

ich habe den ESP32-Code mit ESP8266 verheiratet. Und dabei meine kleinen Änderungen mit eingebracht. Bei jeder Änderung ist ein // Oliver: als Kommentar. So kann man leichter suchen.

  • GSM kann ich nicht testen. Funktioniert dementsprechend nur mit “false”.
  • DS18B20 teste ich, sobald ich die Komponenten in der Hand halte.
  • Wifi Enterprise noch testen
  • Battery Level noch testen
  • Vielleicht noch die Pinbelegung zentralisieren und auf Boardauswahl anpassen.

Was ich mich beim ESP32-Code am meisten frage: Es es sinnvoll das EEPROM so stark zu belasten? Sind doch jede Menge Schreibzyklen?

Und könnte man “WUNDERGROUND” nicht weg lassen? Geht das im Dashboard mit Grafana nicht genauso gut?

So sieht der Code aktuell aus. Geht mit Sicherheit eleganter.

    #include <Arduino.h>

    // Sketch ist für TTGO-T-CALL mit dem SIM800 Modul ausgelegt

    // Farbecode Anschlußkabel BOSCHE H40A
    /*
    +E Rot
    +A Grün
    -E Schwarz
    -A Weiß
    Shield Lila
    */  

    //Hinweis: Die WPA2 Verbindung wurde noch nicht mit dem ESP32 verifziert.
    //Hinweis: Der Sensor Count für den Temperatur Sensor gibt 0 aus - Messung selbst 

    //Wichtig: #define MAXBUFFERSIZE (2048) in Adafruit_MQTT.h anpassen, da ansonsten nicht alle Daten übermittelt werden können 


    #define ESP8266                 true    // Oliver Boardauswahl ergänzt
    #define GSM_ENABLED             false    //Bei FALSE wird automatisch WIFI aktiviert
    #define WEIGHT                  true 
    #define SENSOR_DS18B20          true  
    #define SENSOR_BATTERY_LEVEL    true  // falls Spannungsmessung über Spannungsteiler erfolgen soll, wenn kein SIM800 Modul verwendet wird.
    #define DEEPSLEEP_ENABLED       true  // Code ist aktuell nur auf TRUE ausgelegt, falls False, muß noch im main() ein Delay eingebaut werden.
    #define SLEEP_TIMER             false  // SleepDauer abhängig vom Ladezustand, Sleep_Timer noch nicht mit Wifi verifziert.
    #define WUNDERGROUND            false  // funktionert aktuell nur mit GSM_ENABLED false
    #define WIFI_ENTERPRISE         false  // Unterstützung von WPA2 Enterprise Verschlüsselung, falls FALSE wird der WifiManager verwendet, 
    #define SLEEP_SHORT             true   // Steuerung der Sleepdauer - 5 min/15 min oder 15 min/60 min 


    #if GSM_ENABLED

      #define WIFI_ACTIVE false
      
      #define TINY_GSM_MODEM_SIM800
      #define TINY_GSM_RX_BUFFER   1024 

      #include <Wire.h>
      #include <TinyGsmClient.h>
      #define SerialAT Serial1
      

      // TTGO T-Call pins
        #define MODEM_RST            5
        #define MODEM_PWKEY          4
        #define MODEM_POWER_ON       23
        #define MODEM_TX             27
        #define MODEM_RX             26
        #define I2C_SDA              21
        #define I2C_SCL              22
      
        /* Daten für Netzclub SIM Karte
        const char apn[]  = "pinternet.interkom.de";  //Abhängig vom Netzprovider - ggfs.auf eigene Werte anpassen.
        const char user[] = "";                       //Abhängig vom Netzprovider
        const char pass[] = "";                       //Abhängig vom Netzprovider
        */

        /*Daten für Telekom SIM
        const char apn[]  = "internet.telekom"; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org
        const char user[] = "t-mobile"; // GPRS User
        const char pass[] = "tm"; // GPRS Password
        */

        // Daten für Discotel SIM
        const char apn[]  = "internet"; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org
        const char user[] = ""; // GPRS User
        const char pass[] = ""; // GPRS Password


        // SIM card PIN (leave empty, if not defined)
      
        const char simPIN[]   = "1234"; //PIN gegen eignen Wert austauschen


      int gsm_csq; //Variable für GSM Signal Stärke
      
    #else

      #define WIFI_ACTIVE true
      long wifi_rssi ; // Wifi Signalstärke
      
    #endif

    #if SENSOR_BATTERY_LEVEL
      int adc_level;  
    #endif

    // Variablen für Spanungsmessung, werden unabhängig der Messmethode (Spannungsteiler o. SIM800 Modul benötigt)
    int volt_level;
    float voltage;

    // Oliver: Library je nach Board
    #if ESP8266
        #include <ESP8266WiFi.h>
      #else
        #include <WiFi.h>
    #endif

    //#include <WebServer.h>    // Oliver: wozu?
    //#include <WiFiUdp.h>      // Oliver: wozu?

    #if WIFI_ACTIVE
      #include <WiFiManager.h>         //https://github.com/tzapu/WiFiManager
    #endif

    #if WIFI_ENTERPRISE

      //#include "user_interface.h"      // Bibliotheken zur Unterstützung der WPA2 Verschlüsselung
      //#include "wpa2_enterprise.h"
      #include "esp_wpa2.h"

      // SSID to connect to
      static const char* ssid = "Your_WPA2SSID";
      // Username for authentification
      static const char* username = "Your_User";
      // Password for authentication
      static const char* password = "Your_Password";

    #endif

    #if SLEEP_TIMER

      #include <EEPROM.h>
      int power_save_mode = 0;
      int address = 1;     // Willkürlich von mir festgelegt das im EEPROM an Adresse 1 der Wert für den Ladezustand abgelegt wird.
      int voltbe4;         // Wert der vorherigen Messung 
      int voltbelow = 75;  // Schwellwert für die Aktivierung des PowerSave Modus (Verlängerung des SleepTimers auf 60 min)
      
    #endif


    #if SLEEP_SHORT
      int sleepTimerShort = 300; //5-Minuten SleepTimer
      int sleepTimerLong  = 900; //15-Minuten SleepTimer
    #else
      int sleepTimerShort = 900; //15-Minuten SleepTimer
      int sleepTimerLong  = 3600; //60-Minuten SleepTimer
    #endif

    //int sleepTimeS = sleepTimerShort;    // Oliver: Variable vorbelegt, falls Sleeptimer false.
    int sleepTimeS = 20;    // Oliver: Zum Testen mal verkürzt.
    #define uS_TO_S_FACTOR 1000000LL 

    // Boot Counter - Wert wird mit jedem WakeUp des ESP hochgezählt, 0 bei Kaltstart
    // Oliver: Wozu die Funktion eigentlich?
    #if ESP8266
        #define RTCMEMORYSTART 65
        extern "C" {
          #include "user_interface.h"
        }
        typedef struct {
          int count;
        } rtcStore;    
        rtcStore rtcMem;
    #else
        RTC_DATA_ATTR int bootCount = 0;
    #endif

    // Adafruit MQTT
    // https://github.com/adafruit/Adafruit_MQTT_Library
    #include <Adafruit_MQTT.h>
    #include <Adafruit_MQTT_Client.h>

    // JSON serializer
    // https://github.com/bblanchon/ArduinoJson
    #include <ArduinoJson.h>

    // ----
    // MQTT
    // ----

    // How often to retry connecting to the MQTT broker
    #define MQTT_RETRY_COUNT    5

    // How long to delay between MQTT (re)connection attempts (seconds)
    #define MQTT_RETRY_DELAY    1.5f

    // The address of the MQTT broker to connect to
    #define MQTT_BROKER         "swarm.hiveeyes.org"
    #define MQTT_PORT           1883

    // A MQTT client ID, which should be unique across multiple devices for a user.
    // Maybe use your MQTT_USERNAME and the date and time the sketch was compiled
    // or just use an UUID (https://www.uuidgenerator.net/) or other random value.

    //#define MQTT_CLIENT_ID      "xxxxxxxx-yyyy-xxxx-yyyy-zzzzzzzzzzzz"  // Hier eigenen Werte eintragen
    #define MQTT_CLIENT_ID        "testdrive"     // Oliver: meine Testumgebung

    // The credentials to authenticate with the MQTT broker
    // Eigenen Login Daten bei den netten Team von Hiveeyes via hello@hiveeyes.org anfragen.
    #define MQTT_USERNAME       ""        // Benutzername, Testdrive: leer lassen
    #define MQTT_PASSWORD       ""        // Kennwort, Testdrive: leer lassen

    // The MQTT topic to transmit sensor readings to.
    // Note that the "testdrive" channel is not authenticated and can be used anonymously.
    // To publish to a protected data channel owned by you, please ask for appropriate
    // credentials at https://community.hiveeyes.org/ or hello@hiveeyes.org.

    //#define MQTT_TOPIC        "hiveeyes/-------MQTT-CLIENT-ID-von-oben------/spielwiese/node-1/data.json"     //ggfs. beim Pfad beim Team von hiveeyes noch nachfragen.
    #define MQTT_TOPIC          "hiveeyes/testdrive/oliver/node-2/data.json"  // Oliver: meine Testumgebung

    #if GSM_ENABLED


    // I2C for SIM800 (to keep it running when powered from battery)
    TwoWire I2CPower = TwoWire(0);

      // Client für GSM hier aufrufen
      TinyGsm modem(SerialAT);
      TinyGsmClient client(modem);
      TinyGsmClient clientMQTT(modem);

      
        #define IP5306_ADDR          0x75
        #define IP5306_REG_SYS_CTL0  0x00

        bool setPowerBoostKeepOn(int en){
        I2CPower.beginTransmission(IP5306_ADDR);
        I2CPower.write(IP5306_REG_SYS_CTL0);
        if (en) {
            I2CPower.write(0x37); // Set bit1: 1 enable 0 disable boost keep on
        } else {
            I2CPower.write(0x35); // 0x37 is default reg value
        }
        return I2CPower.endTransmission() == 0;
        }

    #endif

    #if WIFI_ACTIVE

      WiFiClient client;
      WiFiClient clientMQTT;

    #endif


    // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details
    Adafruit_MQTT_Client mqtt(&clientMQTT, MQTT_BROKER, MQTT_PORT, MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD);

    // Setup MQTT publishing handler
    Adafruit_MQTT_Publish mqtt_publisher = Adafruit_MQTT_Publish(&mqtt, MQTT_TOPIC);


    #if WEIGHT

      #include <HX711.h>
      #include <RunningMedian.h>  // http://playground.arduino.cc/Main/RunningMedian
      
      // Aktuelle HX711 Library verwendet deshalb anstatt #define -> const int und ; am Ende ;-)
      //#define SCALE_DOUT_PIN_A D5 // DT
      //#define SCALE_SCK_PIN_A D3 // SCK

      const int SCALE_DOUT_PIN_A = 33;// DT
      const int SCALE_SCK_PIN_A = 32;// SCK 

      //#define SCALE_DOUT_PIN_B D2 // DT
      //#define SCALE_SCK_PIN_B D1 // SCK

      const int SCALE_DOUT_PIN_B = 14;// DT
      const int SCALE_SCK_PIN_B = 25;// SCK

      HX711 scale_A;
      HX711 scale_B;

      long AktuellesGewicht_A = 0;
      long AktuellesGewicht_B = 0;

      float Taragewicht_A = 226743;  // Hier ist der Wert aus der Kalibrierung einzutragen
      float Skalierung_A = 41.647;  // Hier ist der Wert aus der Kalibrierung einzutragen
      
      float Taragewicht_B = -238537;  // Hier ist der Wert aus der Kalibrierung einzutragen
      float Skalierung_B = -42.614;  // Hier ist der Wert aus der Kalibrierung einzutragen

      long Gewicht_A = 999999;
      float GewichtEinzelmessung_A;

      long Gewicht_B = 999999;
      float GewichtEinzelmessung_B;

      // create RunningMedian object with 10 readings
      RunningMedian GewichtSamples_A = RunningMedian(10);
      RunningMedian GewichtSamples_B = RunningMedian(10);

    #endif

    #if SENSOR_DS18B20

      // Temperatur Sensoren DS18B20 connectet to Pin 4
      #define DS18B20_PIN 4

      // Oliver: wieviele Sensoren werden max angeschlossen? Arraygröße muss vorbelegt werden...
      // Bei Speicherproblemen reduzieren.
      #define MaxDS18B20 20
      
      // 1-Wire library
      #include <OneWire.h>
      
      // DS18B20/DallasTemperature library
      #include <DallasTemperature.h>
      
      // For communicating with any 1-Wire device (not just DS18B20)
      OneWire oneWire(DS18B20_PIN);
      
      // Initialize DallasTemperature library with reference to 1-Wire object
      DallasTemperature sensors(&oneWire);
      
      // Device Adresses - dedected by oneWireSearch.ino or Multiple similar
      // Update device adress for your Sensor

      /*
      uint8_t Sensor1 [8] = { 0x28, 0xFF, 0xCF, 0xBD, 0xA4, 0x16, 0x04, 0x46 };  // Nr. 3 grüne Schrift
      uint8_t Sensor2 [8] = { 0x28, 0xFF, 0x67, 0x65, 0x51, 0x16, 0x04, 0xEE }; // langes Kabel
      
      // Define Variable to hold temperature value for Sensor1
      float temps1;
      
      // Define Variable to hold temperature value for Sensor2
      float temps2;
      */
      // Oliver: wird ersetzt durch Array
      float temps[MaxDS18B20];    // Oliver: todo, geht das variabel? Wahrscheinlich nicht.
      //int AnzahlDS18B20 = sensors.getDeviceCount();
      int AnzahlDS18B20 = 3;  // Oliver: mal zum testen geändert

    #endif

    #if WUNDERGROUND

      // Weatherundeground
      const char* weatherhost = "api.weather.com";
      
      // TextFinder Lib used for text extractation of XML Data
      #include <TextFinder.h>
      
      // Define variable as "char" as they will be extracted via finder.string of XML Data
      char currentTemp[8];
      char currentHumidity[8];
      
      // Weatherunderground Station - Alternative Station IMNCHEN1945
      // Weatherunderground Station - Alternative Station IBAYERNM74
      const char*  WUG_Station = "IMUNICH323";

    #endif

    void gsm_setup();
    void gsm_connect();
    void gsm_disconnect();
    void gsm_info();
    void gsm_poweron();
    void gsm_SleepMode2On();
    void gsm_SleepModeOff();
    bool mqtt_connect();
    void transmit_readings();
    void setup_weight();
    void read_weight();
    void setup_tempsensor();
    void read_tempsensor();
    void read_battery_level();
    void power4sleep();
    void read_weatherunderground();



    void setup() {
      
    Serial.begin(115200);
    // Oliver: Neustart darstellen.
    Serial.println();
    Serial.println();
    Serial.println("#### Restart ####");
    Serial.println();

    //Increment boot number and print it every reboot
    // Oliver: Aus Boardauswahl angepasst. Wozu eigentlich?
    #if ESP8266
      Serial.println("Boot number: " + String(readFromRTCMemory()));
      writeToRTCMemory();
    #else
      ++bootCount;
      Serial.println("Boot number: " + String(bootCount));
    #endif

    #if WIFI_ACTIVE

      #if WIFI_ENTERPRISE
        
        Serial.println("Booting WPA2 Enterprise Wifi Mode ");

        // WPA2 Connection starts here
        // Setting ESP into STATION mode only (no AP mode or dual mode)

        /* Code für WPA2 für esp8266 - achtung auch wpa2_enterprise.h bei WIFI_ENTERPRISE notwendig
        wifi_set_opmode(STATION_MODE);
        struct station_config wifi_config;
        memset(&wifi_config, 0, sizeof(wifi_config));
        strcpy((char*)wifi_config.ssid, ssid);
        wifi_station_set_config(&wifi_config);
        wifi_station_clear_cert_key();
        wifi_station_clear_enterprise_ca_cert();
        wifi_station_set_wpa2_enterprise_auth(1);
        wifi_station_set_enterprise_identity((uint8*)username, strlen(username));
        wifi_station_set_enterprise_username((uint8*)username, strlen(username));
        wifi_station_set_enterprise_password((uint8*)password, strlen(password));
        wifi_station_connect();

        */
      WiFi.disconnect(true);  //disconnect form wifi to set new wifi connection
      WiFi.mode(WIFI_STA); //init wifi mode
      esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)username, strlen(username)); //provide identity
      esp_wifi_sta_wpa2_ent_set_username((uint8_t *)username, strlen(username)); //provide username --> identity and username is same
      esp_wifi_sta_wpa2_ent_set_password((uint8_t *)password, strlen(password)); //provide password
      esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
      esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
      WiFi.begin(ssid); 
        
        // WPA2 Connection ends here
        /*
        // Wait for connection AND IP address from DHCP
        Serial.println();
        Serial.println("Waiting for connection and IP Address from DHCP");


        while (WiFi.status() != WL_CONNECTED) {
          delay(2000);
          Serial.print(".");
        }*/
        // Wait for connection AND IP address from DHCP
        Serial.println();
        Serial.println("Waiting for connection and IP Address from DHCP");
        while (WiFi.status() != WL_CONNECTED) {
          delay(2000);
          Serial.print(".");
        }

      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());

      /* 
        while (WiFi.waitForConnectResult() != WL_CONNECTED) {
          Serial.println("Connection Failed! Rebooting...");

          Serial.println("ESP8266 in sleep for 1 min mode");
          esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
        esp_deep_sleep_start();
          delay(100);      
        }
        */

      #else

        Serial.println("Booting Wifi Mode ");

        // Start Wifi Manager to connect
        WiFiManager wifiManager;

        wifiManager.setConfigPortalTimeout(60);
        wifiManager.autoConnect("AutoConnectAP");

        while (WiFi.waitForConnectResult() != WL_CONNECTED) {
          Serial.println("Connection Failed! Rebooting...");

          Serial.println("ESP8266 in sleep for 1 min mode");
          //Go to sleep now
          // Oliver: auf Boardauswahl angepasst
          #if ESP8266
            ESP.deepSleep(60 * uS_TO_S_FACTOR, WAKE_RF_DEFAULT);
            delay(100);
          #else
            esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
            esp_deep_sleep_start();
            delay(100);  
          #endif 
        }

      #endif

      Serial.println(WiFi.localIP().toString());
      Serial.println(WiFi.SSID());

      Serial.println("Ready");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());

      wifi_rssi = WiFi.RSSI();
      Serial.print("Wifi Signalstärke: ");
      Serial.println(wifi_rssi);

    #endif

    #if GSM_ENABLED

      Serial.println(" ->");
      Serial.println("Booting GSM Mode");

      gsm_setup();

      gsm_SleepModeOff();

      gsm_connect();

      gsm_info();

    #endif

    Serial.println(" ->");
      Serial.println("Setup Weight");

    setup_weight();


    #ifdef SENSOR_DS18B20 
      Serial.println(" ->");
      Serial.println("Setup Temp Sensor");

      setup_tempsensor();

    #endif

    }

    void loop() {
      

      Serial.println("MQTT Connect");

      if (!mqtt_connect()) {
        return;
      }
      
      Serial.println("Read Weight");
      read_weight();
      
      Serial.println("Read Temperatur Sensor");
      read_tempsensor();

      Serial.println("Read Battery Level");
      read_battery_level();

      Serial.println("Read Weatherunderground");
      read_weatherunderground();

      Serial.println("Set Sleep Timer");
      power4sleep();
      
      Serial.println("Transmit Reading");
      transmit_readings();

      delay(1000);

      #if GSM_ENABLED
        gsm_disconnect();
      #endif

      #if DEEPSLEEP_ENABLED      // Oliver: falls kein deepsleep, Delay für die gleiche Zeit
          Serial.print("Going to sleep for ");
          Serial.print(sleepTimeS);
          Serial.println(" s");
          #if ESP8266     // Oliver: auf Boardauswahl angepasst, Dauer für sleep wird ausgegeben.
            ESP.deepSleep(sleepTimeS * uS_TO_S_FACTOR, WAKE_RF_DEFAULT);
            delay(100);
          #else
            esp_sleep_enable_timer_wakeup(sleepTimeS * uS_TO_S_FACTOR);
            esp_deep_sleep_start();
            delay(100);  
          #endif 
      #else
          Serial.println();
          Serial.println();
          for (int i = (sleepTimeS); i >0 ; i--) {
              Serial.print("Delay for: ");
              Serial.print(i);
              Serial.println(" s");
              delay(1000);
          }
          Serial.println();
      #endif

    }

    void setup_weight() {

      #if WEIGHT

      scale_A.begin(SCALE_DOUT_PIN_A, SCALE_SCK_PIN_A);
      scale_B.begin(SCALE_DOUT_PIN_B, SCALE_SCK_PIN_B);

        // Waage A Setup
        scale_A.set_offset(Taragewicht_A);
        scale_A.set_scale(Skalierung_A);
      
        // Waage B Setup
        scale_B.set_offset(Taragewicht_B);
        scale_B.set_scale(Skalierung_B);
      #endif

    }


    void read_weight() {

      #if WEIGHT
      
        //clearRunningMedian Sample
        GewichtSamples_A.clear();
        GewichtSamples_B.clear();

        // read x times weight and take median
        // do this till running median sample is full
        while (GewichtSamples_A.getCount() < GewichtSamples_A.getSize()) {

          // wait between samples
          //Serial.print(".");  // Oliver: Auskommentiert. Mich stört der Punkt in der Ausgabe. Wozu dieser?
          delay(180);

          // power HX711 / load cell
         scale_A.power_up();
         scale_B.power_up();

          delay(2);  // wait for stabilizing

          // read raw data input of HX711
          // Oliver: wenn eine Waage nicht ready ist, geht es so trotzdem weiter. Waagenwert wird dann Null gesetzt
          if (scale_A.wait_ready_timeout(1000)) {
            GewichtEinzelmessung_A = scale_A.read();
          } else {
            GewichtEinzelmessung_A = 0;
          }
          if (scale_B.wait_ready_timeout(1000)) {
            GewichtEinzelmessung_B = scale_B.read();
          } else {
            GewichtEinzelmessung_B = 0;
          }

          // switch off HX711 / load cell
          scale_A.power_down();
          scale_B.power_down();

          // calculate weight in kg
          Gewicht_A = (GewichtEinzelmessung_A - Taragewicht_A) / Skalierung_A;
          Gewicht_B = (GewichtEinzelmessung_B - Taragewicht_B) / Skalierung_B;

          // use calculated kg values for median statistic
          GewichtSamples_A.add(Gewicht_A);
          GewichtSamples_B.add(Gewicht_B);

          AktuellesGewicht_A = GewichtSamples_A.getMedian();
          AktuellesGewicht_B = GewichtSamples_B.getMedian();

          Serial.print("Gewicht A: ");
          Serial.print(AktuellesGewicht_A);
          Serial.println(" g");
          
          Serial.print("Gewicht B: ");
          Serial.print(AktuellesGewicht_B);
          Serial.println(" g");
          
        } 
      #endif
    }


    void gsm_setup() {

    #if GSM_ENABLED

      
      I2CPower.begin(I2C_SDA, I2C_SCL, 400000);

      // Keep power when running from battery
      bool isOk = setPowerBoostKeepOn(1);
      Serial.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL"));

      // Set modem reset, enable, power pins
      pinMode(MODEM_PWKEY, OUTPUT);
      pinMode(MODEM_RST, OUTPUT);
      pinMode(MODEM_POWER_ON, OUTPUT);
      digitalWrite(MODEM_PWKEY, LOW);
      digitalWrite(MODEM_RST, HIGH);
      digitalWrite(MODEM_POWER_ON, HIGH);

      // Set GSM module baud rate and UART pins
      SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);

      // Restart takes quite some time
      // To skip it, call init() instead of restart()
      Serial.println("Initializing modem...");
      modem.restart();

    // Unlock your SIM card with a PIN if needed
     if ( simPIN && modem.getSimStatus() != 3 ) {
        modem.simUnlock(simPIN);
    Serial.print("GetSimStatus:");
    Serial.print(simPIN);
      }

    #endif

    }

    void gsm_connect() {

    #if GSM_ENABLED

      Serial.print("Waiting for network...");
      if (!modem.waitForNetwork()) {
        Serial.println(" fail");
        delay(10000);
        return;
      }
      Serial.println("Connected to mobil network");

      gsm_info();

      Serial.print("GPRS Connecting to ");
      Serial.print(apn);
      if (!modem.gprsConnect(apn, user, pass)) {
        Serial.println(" fail");
        delay(10000);

      // Falls die Verbindung mit dem GSM & GPRS Netz nicht klappt, legt sich der der ESP für 60 sec schlafen und wir starten im Anschluß von vorn.
      // GSM Modem stoppen

         gsm_disconnect();

        // Sleep für 60 Sekunden
         esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
         esp_deep_sleep_start();
         delay(100);   
        
        return;
      }
      Serial.println("GPRS Connection established");

    #endif

    }


    void gsm_info() {

    #if GSM_ENABLED

      gsm_csq = modem.getSignalQuality();
      volt_level = modem.getBattPercent();
      voltage = modem.getBattVoltage() / 1000.0F;

      Serial.println();
      Serial.println("Signal Qualität");
      Serial.println(gsm_csq);

      Serial.println("Akku Level ");
      Serial.println(volt_level);

      Serial.println("Akku Wert Spannung ");
      Serial.println(voltage);

    #endif

    }



    void gsm_disconnect() {

    #if GSM_ENABLED

      // For dem Sleep  - Verbindung kappen
      Serial.println("GPRS Disconnect");
      modem.gprsDisconnect();

      // For dem Sleep  - Modem RadioOFF schalten 
      Serial.println("GSM Radio Off ");
      modem.radioOff();

      delay(1000);

      Serial.println("GSM Sleep Mode 2 Enablen ");
      gsm_SleepMode2On();

    #endif

    }

    void gsm_SleepMode2On() {

    #if GSM_ENABLED

      modem.sendAT(GF("+CSCLK=2"));
      delay(1000);

    #endif

    }

    void gsm_SleepModeOff() {

    #if GSM_ENABLED

      modem.sendAT(GF("+CSCLK=0"));
      delay(1000);

    #endif

    }

    bool mqtt_connect() {

      // If already connected, don't do anything and signal success
      if (mqtt.connected()) {
        return true;
      }

      Serial.println("Connecting to MQTT broker");

      // Reconnect loop
      uint8_t retries = MQTT_RETRY_COUNT;
      int8_t ret;
      while ((ret = mqtt.connect()) != 0) {

        Serial.println("Anzahl MQTT Connection Retries:");
        Serial.println(ret);
        Serial.println(String(mqtt.connectErrorString(ret)).c_str());

        retries--;
        if (retries == 0) {
          Serial.println("Giving up connecting to MQTT broker");
          
          // Falls keine MQTT Verbindung aufgebaut werden kann, hängen wir hier ewig in der Schleife
          // da solange die Funktion mqtt.connect aufgerufen wird
          // deshalb fangen wir von vorne an in dem wir den ESP 1min schlafen geht und die Schleife von vorne beginnt

          // GSM Modem stoppen

              gsm_disconnect();

          // Sleep für 60 Sekunden.   // Oliver: auf Boardauswahl angepasst
              #if ESP8266
                ESP.deepSleep(60 * uS_TO_S_FACTOR, WAKE_RF_DEFAULT);
                delay(100);
              #else
                esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
                esp_deep_sleep_start();
                delay(100);  
              #endif 

          return false;
        }

        Serial.println("Retry MQTT Connection in x Seconds:");
        Serial.println(MQTT_RETRY_DELAY);

        // Wait some time before retrying
        delay(MQTT_RETRY_DELAY * 1000);
      }

      if (mqtt.connected()) {

        Serial.println("Successfully connected to MQTT broker");
        Serial.println(MQTT_BROKER);

        return true;
      }

      // Giving up on further connection attempts
      return false;
    }

    void setup_tempsensor() {

    #if SENSOR_DS18B20

      Serial.println("Temperaturmessung mit dem DS18B20 ");

      // Start Dallas Tempeartur Library / Instanz
      sensors.begin();  // DS18B20 starten

      // Präzision auf 12 Bit   // Oliver: verringern zur zeitersparnis bei vielen Sensoren?
      /*
       9 bit  0.5 degrees C      94 mSec
      10 bit  0.25 degrees C    188 mSec
      11 bit  0.125 degrees C   375 mSec
      12 bit  0.0625 degrees C  750 mSec
      */
      //sensors.setResolution(TEMP_12_BIT);
      sensors.setResolution(12);

      //Anzahl Sensoren ausgeben
      Serial.print("Sensoren: ");
      Serial.println(sensors.getDeviceCount());

    #endif

    }

    void read_tempsensor() {

    #if SENSOR_DS18B20

      // Temperatursensor(en) auslesen
      sensors.requestTemperatures();

      // Read Temperature form each Sensor
      //temps1 = sensors.getTempC(Sensor1);  // Oliver: Ersetzt durch nachfolgende Schleife
      //temps2 = sensors.getTempC(Sensor2);  // Oliver: Ersetzt durch nachfolgende Schleife

      for (int i=0; i<AnzahlDS18B20; i++){
        temps[i] = sensors.getTempCByIndex(i);
      }

    #endif

    }

    void read_battery_level() {

    #if SENSOR_BATTERY_LEVEL

      // Read the battery level from the ESP8266 analog input pin.
      // Analog read level is 10 bit 0-1023 (0V-1V).
      // Our 1M & 220K voltage divider takes the max
      // MY 1M & 3,3 K
      // LiPo value of 4.2V and drops it to 0.758V max.
      // This means our minimum analog read value should be 580 (3.14V)
      // and the maximum analog read value should be 774 (4.2V).

       // Read Value from A0 - max 3.3V, daher etwas andere Teiler
      // 1024 / 3,3  * 1,73 = 537 1,73V laut spannungsteiler bei 4,2 V mit 4,7k & 3,3k
      // 1024 / 3,3  * 1,3 = 400 1,3V lt. spannungsteiler bei 3,14 V mit 4,7k & 3,3 k

      
      
      adc_level = analogRead(A0);
      Serial.print("ADC_level: ");
      Serial.println(adc_level);

      // Map Value to % Level
      volt_level = map(adc_level, 400, 537, 0, 100);
      Serial.println(volt_level);

      // Calculate Voltage Value
      Serial.print("Voltage: ");
      voltage = adc_level * 4.2 / 537 ;
      Serial.println( voltage );
      
      // Give operating system / watchdog timer some breath
      yield();
    #endif
    }

    void power4sleep() {

    #if SLEEP_TIMER   
    // Oliver: Ist es wirklich sinnvoll hier das EPROM zu belasten? Es kommen ja schon eine ganze Menge Schreibzyklen zusammen.
    //         Wäre das nicht auch was für den RTC-Speicher?

      EEPROM.begin(512);

      //letzten Ladungsstand Wert aus EEprom auslesen
      voltbe4 = EEPROM.read(address);
      Serial.print("Read Value:");
      Serial.println(voltbe4);

      //aktuellen Ladungsstand Wert in EEprom ablegen
      EEPROM.write(address, volt_level );
      EEPROM.commit();

      if (volt_level < voltbelow && voltbe4 < voltbelow )
      {
        // Wenn Ladungswert unter 75% wird die Sleep Dauer auf 60 min (=3600 Sekunden) verlängert
        //sleepTimeS = 3600;

        // Wert wird Abhängig vom #define SLEEP_SHORT gesetzt

        sleepTimeS = sleepTimerLong; 
        power_save_mode = 1;

      }
      else
      {
        // Wenn Ladungswert über  75% ist die Sleep Dauer auf 15 min (=900 Sekunden) gesetzt
        // sleepTimeS = 900;

        // Wert wird Abhängig vom #define SLEEP_SHORT gesetzt

        sleepTimeS = sleepTimerShort;
        power_save_mode = 0;

      }

        Serial.print("Set Sleep Timer to:");
        Serial.println(sleepTimeS);
        Serial.print("Set Power Save to:");
        Serial.println(power_save_mode);
        
    #endif

    }

    void read_weatherunderground() {

    #if WUNDERGROUND

      if (client.connect(weatherhost, 80))
      {
        String wurl = "/v2/pws/observations/current?apiKey=6532d6454b8aa370768e63d6ba5a832e&stationId=IMUNICH323&format=xml&units=m" ;
        client.print(String("GET ") + wurl + " HTTP/1.1\r\n" + "Host: " + weatherhost + "\r\n" +  "Connection: close\r\n\r\n");

        while (!client.available()) {
          //  delay(200);
          delay(10000);
          Serial.print("client not availabel connect: ");
        }

        // Read all the lines of the reply from server and print them to Serial
        TextFinder finder(client);  // Keine Ahnung wozu - aber ohne intanzierung läuft der Textfinder nicht

        //   while (client.available()) {
        String line = client.readStringUntil('\r');

        finder.getString("<humidity>", "</humidity>", currentHumidity, 8) ;

        finder.getString("<temp_c>", "</temp_c>", currentTemp, 8) ;

        Serial.println("closing connection");
      }

    #endif

    }

    int readFromRTCMemory() {
      system_rtc_mem_read(RTCMEMORYSTART, &rtcMem, sizeof(rtcMem));
      yield();
      return rtcMem.count;
    }

    void writeToRTCMemory() {
      rtcMem.count++;
      system_rtc_mem_write(RTCMEMORYSTART, &rtcMem, 4);
      yield();
    }

    void transmit_readings() {

      // Build JSON object containing sensor readings
      // TODO: How many data points actually fit into this?

      // json5: StaticJsonBuffer<1024> jsonBuffer;
      // neu für json6

      DynamicJsonDocument doc(512);

    // Die folgende  Zeile war im Beispiel - aber das obj wird nirgends verwendet
    //  JsonObject obj = doc.as<JsonObject>();

      // Create telemetry payload by manually mapping sensor readings to telemetry field names.
      // Note: For more advanced use cases, please have a look at the TerkinData C++ library
      //       https://hiveeyes.org/docs/arduino/TerkinData/README.html

      // json5: JsonObject& json_data = jsonBuffer.createObject();

      #if WUNDERGROUND
        doc["WXD_Temp"]        = currentTemp;
        doc["WXD_Humi"]        = currentHumidity;
      #endif

      #if SENSOR_DS18B20
        for (int i=0; i<AnzahlDS18B20; i++){
        doc[("temps" + String(i+1))]    = temps[i];
        }
      #endif

      #if WEIGHT
        doc["Waage1"]          = AktuellesGewicht_A;
        doc["Waage2"]          = AktuellesGewicht_B;
      #endif

      #if WIFI_ACTIVE
        doc["Signal"]          = wifi_rssi;
      #endif

      #if GSM_ENABLED
        doc["Signal"]          = gsm_csq;
      #endif

      #if SLEEP_TIMER
         doc["power"]          = power_save_mode;
      #endif

      doc["volt_level"]        = volt_level;
      doc["voltage"]           = voltage;

      // Debugging
      // json5: json_data.printTo(Serial);
      // für json6:

      
      serializeJsonPretty(doc, Serial);
      Serial.println();

      // Serialize data
      // json5:int json_length = json_data.measureLength();
      // json5:char payload[json_length + 1];
      // json5: json_data.printTo(payload, sizeof(payload));
      // für json6.
      String payload;
      serializeJson(doc, payload);


      // Publish data
      // TODO: Refactor to TerkinTelemetry
      // json5: if (mqtt_publisher.publish(payload)) {
      // für json6:

      if (mqtt_publisher.publish(payload.c_str())) {
        Serial.println("MQTT publish succeeded");
      } else {
        Serial.println("MQTT publish failed");
      }

    }

Hallo @Oliver,

ich hab mal angefangen Deine Anregungen und Vorarbeiten auf zunehmen.

Als erstes würde ich vorschlagen den Code direkt abhängig von der gewählten Board Architektur zu machen, so muß man gar keine Variable setzen.
Da wir die Sleep Funktion öfter brauchen und diese auch Board abhängig ist, habe ich diese in eine Funktion ausgelagert.

void esp2sleep(int dauer) {

#if defined(ARDUINO_ARCH_ESP8266)

    ESP.deepSleep(dauer * uS_TO_S_FACTOR, WAKE_RF_DEFAULT);
    delay(100);
    
 #elif defined(ARDUINO_ARCH_ESP32)

    esp_sleep_enable_timer_wakeup(dauer * uS_TO_S_FACTOR);
	  esp_deep_sleep_start();
    delay(100);   

#endif

}

Wenn es ne elegante Möglichkeit gibt nen Wert über den DeepSleep zu “retten” wäre das top. Als ich vor nem Jahr mit dem SIM 800 Modul und dem ESP8266 angefangen habe, ist mir nichts bessere eingefallen.

Aktuell ist der Boot Counter unnötig - ich habe in einer anderen Version eine Telegram Schnittstelle mit Wartungsfunktion- siehe auch Schwarmalarm & Wartungsmodus via Telegram

Bei einem Restart wurde da auch eine Nachricht gesendet die über den Coldboot informiert.
Die Funktionalitäten hätte ich eigentlich gerne wieder implementiert- aber leider bekomme ich Telegram nicht mit dem SIM800 Modul zum laufen.

Hallo @Stefan und @Oliver,

Ich habe gerade etwas an ringlabs/bienenwaage-5.0 geschafft und die Umgebung ein wenig weitergebracht. Es gibt nun erste Dokumentationsschnipsel und weiterhin wurde die von Stefan begonnene platformio.ini nun um ein Makefile erweitert, so dass man die komplette Firmware direkt aus dem Verzeichnis heraus per make bauen kann, ohne die Umgebung inkl. Bibliotheken mühselig mit der klassischen Arduino IDE einrichten zu müssen.

Gerade eben habe ich noch den Bezug der Wetterdaten von Weather Underground auf JSON umgestellt, was ich aber noch nicht getestet habe. Ich hoffe es klappt, ggf. findet Ihr Zeit, das einmal unter die Lupe zu nehmen?

Viele Grüße,
Andreas.

1 Like

Hallo @Andreas,

ganz herzlichen Dank für die wirklich tolle Dokumentation- sieht richtig Klasse aus- bin tief beeindruckt.

Die Umstellung der WeatherUnderground Daten auf JSON finde ich genial und hab mich schon an die ersten Tests gemacht - nach einer kleinen Erweiterung hat es dann auch prompt geklappt.

Dabei hab ich denn auch noch ein wenig experimentiert und hab es zum ersten Mal geschafft Daten auch per GSM von Weather Underground abzuziehen.

Hoffentlich kann ich bald mehr berichten!

Nochmal ein ganz großes Dankeschön an Dich!

2 Likes

Hallo @Stefan,

schön, dass Dir die Erweiterungen gefallen.

Exzellent! Ich hatte schon befürchtet, dass ich die Sache durch die Umstellung verschlimmbessert habe. Ich freue mich darauf, wenn Deine Fixes wieder Downstream im Repository landen.

Wunderbar!

Gern geschehen!

Sag, funktioniert denn auch das Build-Environment aka. »Just type "make"« für Dich? Wie Du vielleicht gesehen hast, habe ich bei GitHub - daq-tools/Adafruit_MQTT_Library at maxbuffersize-2048 einen Fork der Adafruit_MQTT_Library hinterlegt, der – Nomen est omen – MAXBUFFERSIZE auf 2048 bytes erhöht, damit alles passt. Diese wird wiederum in der platformio.ini angezogen:

https://github.com/hiveeyes/arduino/blob/master/node-esp32-generic/platformio.ini#L29-L29

Viele Grüße,
Andreas.

Ui, hier ist ja einiges passiert. Ich hatte das Thema stumm geschaltet? :thinking:
Ich bin gerade viel am Testen. Ich denke demnächst zeige ich mal was ich weiter gebastelt habe.
Aktuell habe ich ein Problem. Ich habe jetzt einen Temperaturrechen installiert. Folglich gibt es deutlich mehr Werte zu übertragen. Hierbei stürzt der ESP32 und auch ESP8266 dann ab. Ich denke es hat mit der Zeichenzahl zu tun. Aber ich weiß nicht, wo ich ansetzen kann? Oder ist das serverseitig limitiert?

Serielle Ausgabe:

Transmit Reading
{
  "Temp1": 23.125,
  "Temp2": 21.75,
  "Temp3": 23.125,
  "Temp4": 21.75,
  "Temp5": 23.125,
  "Temp6": 21.75,
  "Temp7": 21.75,
  "Temp8": 21.75,
  "Temp9": 21.75,
  "Signal": -53,
  "volt_level": 0,
  "voltage": 0
}
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x40151ab0  PS      : 0x00060630  A0      : 0x800e25ae  A1      : 0x3ffb1e60  
A2      : 0x3ffc10c8  A3      : 0x3ffc10ea  A4      : 0x000000e0  A5      : 0x00000000  
A6      : 0x000000fa  A7      : 0x00000000  A8      : 0x800e257c  A9      : 0x3ffb1e40  
A10     : 0x61746c6f  A11     : 0x3ffcc00c  A12     : 0x000000aa  A13     : 0x0000ff00  
A14     : 0x00ff0000  A15     : 0xff000000  SAR     : 0x00000008  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x0000003c  LBEG    : 0x40001699  LEND    : 0x400016aa  LCOUNT  : 0xfffffffc  

Backtrace: 0x40151ab0:0x3ffb1e60 0x400e25ab:0x3ffb1e80 0x400e2611:0x3ffb1ea0 0x400e271f:0x3ffb1ec0 0x400d2365:0x3ffb1ee0 0x400d240d:0x3ffb1f90 0x400e53c1:0x3ffb1fb0 0x40089051:0x3ffb1fd0

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac

Also bei etwas über 150 zu übertragenden Zeichen funktioniert die Übertragung nur noch sporadisch. Von daher vermute ich mal, dass hier der Watchdog Probleme macht.
Am Programmcode kann ich da ja nichts machen.

if (mqtt_publisher.publish(payload.c_str())) {     // <- da drin hängter sich auf.
  Serial.println("MQTT publish succeeded");
} else {
  Serial.println("MQTT publish failed");
}

Müsste ja dann in der Library verändert werden? Oder muss man sich mit dem Limit abfinden? Vielleicht 2 getrennte Übertragungen durchführen? Gefällt mir aber nicht so richtig…

Gerade was in den Programmkommentaren gelesen:

  • #define MAXBUFFERSIZE (2048) in Adafruit_MQTT.h anpassen,
    da ansonsten nicht alle Daten übermittelt werden können.

Mal testen :sweat_smile:

Ok. Funktioniert. Den Ärger hätte ich mir sparen können. Peinlich. :stuck_out_tongue_closed_eyes:

4 Likes

Sooo. Ich wollte euch mal zeigen, was ich aus Stefan seinem Programm für mich gebastelt habe. @Stefan, @Andreas die zwischenzeitlichen Änderungen von euch habe ich jetzt noch nicht nachvollzogen um ehrlich zu sein. :sweat_smile:
00_hiveeyesStefan32RTCmodOliver.zip (16,2 KB)

  • Ich habe das Programm umstrukturiert. Ich finde so die ganzen Einstellungen leichter.
  • Bootcounter über RTC-Speicher. Allerdings muss noch initial mal auf Null gesetzt werden. Umsetzung fehlt, da ich das für mich erst mal nicht gebraucht habe. Hochzählen ab dem Zufallswert an sich funktioniert.
  • Den Vorschlag für die automatische Boarderkennung EP32 / ESP8266 habe ich übernommen. Nicht aber die ausgelagerte Funktion. Stupides Ersetzen war erst mal einfacher.
  • Es können jetzt BMx280 Sensoren angeschlossen werden.
  • Als Alternative zum deepsleep wartet der Programmablauf jetzt genauso lange im delay.
  • Das Programm läuft weiter, auch wenn keine Waage erkannt wird.
  • Batterylevel wird über #defines des Spannungsteiler etc. automatisch berechnet.
  • Es können beliebig viele DS18B20 Sensoren angeschlossen werden. (Ist nur durch Speichervorbelegung reduziert, derzeit auf 20). Kann in den Settings einfach eingestellt werden.
  • Adressen der DS18B20 müssen nicht mehr rausgesucht werden. Man kann jedem Sensor eine ID übergeben. Diese wird im Sketch abgerufen und der Bezeichner danach erstellt. Es kann also auch ein defekter Sensor einmal ausgetauscht werden, ohne die Adresse neu vergeben zu müssen. Setzen der IDs über die Beispieldatei DallasTemperature - UserDataWriteBatch.ino. Ich habe mir das ein bisschen zurecht gebogen, um mehrere Sensoren individuell mit einer ID zu versehen. Kann ich hier auch mal noch gerne posten.

Was haltet ihr von der Umsetzung der DS18B20 und den IDs?

So wie nachfolgend dargestellt könnte man die Indizierung der DS18B20 vornehmen. Ginge natürlich noch anwenderfreundlicher, aber man braucht es auch nicht übertreiben. Ist ja eher Beiwerk.
Es werden alle gefundenen Sensoren nacheinander durchgegangen und können mit einem Zahlenwert belegt werden. Die Temperaturen werden zwischendrin immer aktualisiert. So kann man z.B. durch Erwärmen dann den angewählten Sensor suchen und dann nummerieren.

    #include <OneWire.h>
    #include <DallasTemperature.h>

    #define ONE_WIRE_BUS      21

    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensors(&oneWire);

    byte deviceCount = 0;

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


    void setup() {
      sensors.begin();
      sensors.requestTemperatures();
      Serial.begin(115200);
      
      Serial.println();
      Serial.println("########### Restart ############");
      Serial.println();
      
      deviceCount = sensors.getDeviceCount();
      Serial.print("#devices: ");
      Serial.println(deviceCount);
      Serial.println();
      
      

      for (byte i = 0; i < deviceCount; i++)
      {
      Serial.println("------------------------------------------");
      Serial.println("Enter ID for marked sensor");
      Serial.println("------------------------------------------");
      deviceInfos(i);
      
      int c = 0;
      int id = 0;
      while (c != '\n' && c != '\r')
      {
        unsigned long currentMillis = millis();
        if (currentMillis - previousMillis >= interval) {
          previousMillis = currentMillis;
          Serial.println("------------------------------------------");
          deviceInfos(i);
        }
        c = Serial.read();
        switch(c)
        {
        case '0'...'9':
          id *= 10;
          id += (c - '0');
          break;
        default:
          break;
        }
      }
      DeviceAddress t;
      sensors.getAddress(t, i);
      sensors.setUserData(t, id);

      }
      
      Serial.println();
      Serial.println("### END ###");
      deviceInfos(-1);

    }


    void loop() {

    }


    void deviceInfos(int index){
      for (byte i = 0; i < deviceCount; i++)
      {
        sensors.requestTemperatures();
        DeviceAddress t;
        sensors.getAddress(t, i);
        printAddress(t);
        Serial.print("  Temp: ");
        Serial.print(sensors.getTempC(t));
        Serial.print("  ID: ");
        int id = sensors.getUserData(t);
        Serial.print(id);
        if (index == i){
          Serial.print("  <---");
        }
        Serial.println();
      }
    }



    void printAddress(DeviceAddress deviceAddress)
    {
      for (byte i = 0; i < 8; i++)
      {
        // zero pad the address if necessary
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
      }
    }


    int serial_read_number() {
      int c = 0;
      int id = 0;
      while (c != '\n' && c != '\r')
      {
        //Serial.println();
        c = Serial.read();
        switch(c)
        {
        case '0'...'9':
          id *= 10;
          id += (c - '0');
          break;
        default:
          break;
        }
      }
      return id;
    }
2 Likes

Damit kann man ins EEPROM jedes der DS18X20-Sensoren eigene Werte zur individuellen Wiedererkennung schreiben? Saustark – das war mir nicht bewusst!

2 Likes

Ja, ich habe das auch erst in den neuen Beispielen der Library entdeckt.

Mit dem Sketch aus der Library bekommen alle Sensoren am Bus die gleiche Id. Man kann also alle Sensoren anschließen. Vergibt allen die letzte Id. Zieht den letzten ab, vergibt n-1 usw… Am Ende alle wieder anschließen und fertig. Ist vermutlich das schnellste.
Oder man schließt immer nur einen Sensor an. Oder oder oder.
Ich habe sie verlötet. Von daher muss ich den Sketch von mir nutzen.

Was wäre denn, wenn man den Wifimanager noch für weitere Settings nutzt? Hier kann man ja auch eigene Felder anlegen.
Zum Beispiel mqtt Server/Benutzer-Daten.
Oder die Kalibrierdaten? Alternativ könnte der Kalibriersketch die Daten auch im EPROM ablegen und der Sketch greift darauf zu?

1 Like

Das wäre prima! Die Leute von https://luftdaten.info machen genau das für ihren Feinstaubsensor.

Wir haben hier schon mal ein paar libs diskutiert:

Wenn nichts dagegen spricht sollten wir die GitHub - prampec/IotWebConf: ESP8266/ESP32 non-blocking WiFi/AP web configuration Arduino library verwenden, sie könnte sogar OTA!
GitHub - prampec/IotWebConf: ESP8266/ESP32 non-blocking WiFi/AP web configuration Arduino library

Die alte lib – ohne den EEPROM-Teil – kann auch schon IDs intern zuweisen, d.h. auch nach einem restart des Geräts bleibt die Reihenfolge des Datenabrufs gleich. Das habe ich mir zunutze gemacht und alle Sensoren genau in der Reihenfolge angeordnet, wie das Skript die Daten ausliest, natürlich vor einem Verlöten oder befestigen mit Kabelbindern o.ä.! ;-)

Ich habe ein Problem mit der mqtt-Übertragung.
Laut serieller Ausgabe wird alles normal übertragen. Am Dashboard kommen aber die angeblich übertragenen Daten nicht immer an. Kann man das irgendwie abfangen? Und dann eine neue Übertragung initiieren?
Der wlan-Empfang an der Beute ist nicht besonders gut. Vielleicht spielt das dabei eine Rolle.

Schickst du an swarm.hiveeyes.org? Dann könntest du hier schauen, ob was reinkommt: Wtee

1 Like

Nach dem Vorschlag von @clemens

Hab ich mir IoTWebConf ein wenig angeschaut. Ging mir nicht so geschmeidig von der Hand wie mit dem WifiManager- hat mich aber auf die Idee gebracht, das der Vorteil des non-Blockingg evlt. genau ein Nachteil bei meiner Implementierung ist, da sobald die Daten verarbeitet sind, der ESP in den Tiefschlaf geht und dann das Portal evlt. gerade zur Konfiguration benutzt wird.

Die Idee der Konfiguration via Web-Interface finde ich ja allerdings schon sehr “charmant”. Ich hab mich daran erinnert das der WifiManager ebenfalls in der Lage ist CustomParameter zu verarbeiten.

Genau der Nachteil des Blocking, versuche ich aktuell zu nutzen.

Kurz der Start skizziert:
Der ESP startet und öffnet (unabhängig ob WiFi oder GSM Konfiguration) einen AccessPoint mit Portal für 60 Sekunden.
Verbindet man sich mit dem AccessPoint kann man verschiedenen Werte über das Portal anpassen.
Nach dem Beendigen des AccessPoints werden die geladenen bzw. geänderten Werte zurückgespeichert und der ESP startet weiter (entweder im Wifi Mode oder GSM Mode) wie gewohnt und legt sich dann für die SleepDauer wieder schlafen.
Die SleepDauer hätte ich dann einfach um die 60 Sekunden des AccessPoint Portals gekürzt.

Eine andere Variante wäre das der WifiManager auf Tastendruck ein Config Portal Startet, hat für mich den Nachteil das man einen Taster am Gehäuse nach aussen legen muß und über jedes zusätzliches Loch im Gehäuse Feuchtigkeit eindringen kann.

Wie sich das der Startup und die 60 Sekunden des ConfigPortals auswirken habe ich noch nicht näher untersucht. Allerdings kann ich von meinen Test mit Solar Lader und Wifi Betrieb erinnern, das der ESP im Wifi Mode eher sparsamer ist.
Soweit die Theorie:
In der Praxis klappt der Start des WifiManagers mit AccessPoint und Start des Config Portals mit Custom Werten, die auch nach der Beendigung des Portals für den weiteren Programmablauf zur Verfügung stehen.
Die ersten konfigurierbaren Werte können geändert und abgefragt werden.

Und so sieht das dann aus

!
!
!

Im Moment experimentiere ich gerade noch u.a. Verwendung von Check Boxen und überlege welche Parameter sinnvoll zur Konfiguration “on-the-fly” sind.

1 Like

Sehr cool @Stefan, dass du schon was umgesetzt hast!

Ich denke die Konfiguration braucht man nicht allzuhäufig und einen eigenen Taster nach draußen zu führen muss auch nicht sein. Den AP jedesmal beim Neustart aufzumachen finde ich auch übertrieben, der wird ein mal genutzt, aber 4000x geöffnet wenn ihn niemand braucht, das verbraucht unnötig Energie.

Meine Idee wäre, dass ein hard reset den AP-Modus aktiviert. Vielleicht kann man auch checken ob noch jemand verbunden ist und so lange noch nicht in den deep sleep gehen.

1 Like

Hi Stefan,

beide Deiner Varianten haben nun jeweils eine Dokumentationsseite spendiert bekommen.

Viele Grüße,
Andreas.

1 Like

Hallo Stefan,

Dank GitHub - pradyunsg/furo: A clean customizable documentation theme for Sphinx konnten wir noch einmal eine Schippe drauflegen und haben die Dokumentation damit gerade ein wenig modernisiert.

Mit Maintenance: Adjust directory layout. Naming things. Modernize documentation. by amotl · Pull Request #61 · hiveeyes/arduino · GitHub ff. hat sich das Verzeichnislayout ein wenig geändert. Deine Firmwares liegen jetzt im ringlabs Unterverzeichnis – ich hoffe das passt [1].

Ich hatte festgestellt, dass beide Firmwares leider noch nicht auf der Übersichtsseite integriert waren. Das habe ich jetzt nachgeholt. Sag bitte gerne Bescheid, falls sich irgendwo Fehler eingeschlichen haben, oder Du Raum für Verbesserungen siehst.

Viele Grüße,
Andreas.


  1. Irgendeinen Namen musste ich halt finden. Ich hatte schon trudering verwendet, und dachte auch noch an stulabs, aber jetzt ist es eben ringlabs geworden. Wenn Du einen anderen Vorschlag hast, sag gerne Bescheid. ↩︎

Hallo nochmal,

bei Abhängigkeiten der Bienenwaage 2.0 und 5.0 aktualisieren · Issue #65 · hiveeyes/arduino · GitHub hab ich uns ein Einmerkerl reingetan, um bei Gelegenheit die Abhängigkeiten Deiner Firmwares zu erneuern.

Viele Grüße,
Andreas.