shurillu / CTBot

A simple (and easy to use) Arduino Telegram BOT Library for ESP8266/ESP32
MIT License
147 stars 34 forks source link

kernel panic with getNewMessage() #106

Open SimonESchiller opened 1 year ago

SimonESchiller commented 1 year ago

Hello Guys I want to recive messages from CTBot and use the input to control my water boiler. I use a ESP8266 The program runs as expected, but unfrequently the kernel panics when the getNewMessage() is called. Do you know how fix it? Thanks in advance! Simon

#include <ArduinoJson.h>        // Arduino JSON (6.19.4)
#include <ESP8266WiFi.h>        // ESP8266 WiFi
#include <WiFiClient.h>         // ESP8266 Wifi-Client
#include <ESP8266HTTPClient.h>  // ESP8266 HTTP-Client
#include <NTPClient.h>          // TIme
#include <WiFiUdp.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "CTBot.h"
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>

//Pin configuration for power controll (now GPIO15 / D8)
#define GPIO_PIN_PWM_CONTROL 15

CTBot myBot;
//SD card
const int chipSelect = 5;

// GPIO where the DS18B20(themperature probe) is connected to
const int oneWireBus = 4;

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);

// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);

//Telegram token
String token = "xxxxxx:xxxxxxxx_xxxxxxxxxxxxxxxx";

// WiFi SSID und PSK
const char *ssid = "xxxxxxxx";
const char *password = "xxxxx";
const char *host_name = "xxxxxxx";

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

WiFiClient wifiClient;

//Inverter IP adress
const String inverterHostname = "xxxxxxxxxx";

// Fronius Solar API URL "Flow Data"
String urlFlow = "http://" + inverterHostname + "/solar_api/v1/GetPowerFlowRealtimeData.fcgi";

//global variables:
int value = 0;                 //1024= 100% power ; 0= 0% power
bool heating = false;          //if boiler heats at 100%
float temperatureC;            //actual temperature
float targetTemperature = 50;  //target temperatur for the nightheating
bool temperatureReachedOnThisDay = false;
bool hollidayModus = false;
bool sdAvailable = false;

//Size calculated by https://arduinojson.org/v6/assistant/
size_t capacityFlow = 768;

void setup() {
  //1s wait
  delay(1000);
  // serial begin, 115200/8/N/1
  Serial.begin(115200);
  // Start the DS18B20(temperature) sensor
  sensors.begin();

  // WiFi aus
  WiFi.mode(WIFI_OFF);
  // 1s warten
  delay(1000);
  // WiFi Client-Modus, AP-Modus aus
  WiFi.mode(WIFI_STA);
  WiFi.hostname(host_name);
  // WiFi verbinden
  WiFi.begin(ssid, password);

  Serial.println();
  Serial.println("ESP8266 WiFi-Client für Fronius Solar Api V1");
  Serial.println("https://www.onderka.com/hausautomation-technik-und-elektronik/nodemcu-json-daten-von-fronius-wechselrichter-einlesen/");
  Serial.println();
  Serial.print("Verbinde ");

  // Auf Verbindung warten
  while (WiFi.status() != WL_CONNECTED) {
    // Alle 0.5s einen Punkt
    delay(500);
    Serial.print(".");
  }
  Serial.println(" Verbunden.");

  // set the telegram bot token
  myBot.setTelegramToken(token);
  // check if all things are ok
  if (myBot.testConnection())
    Serial.println("\ntestConnection OK");
  else
    Serial.println("\ntestConnection NOK");

  // Verbindungs-Informationen
  Serial.println();
  Serial.print("SSID:              ");
  Serial.println(ssid);
  Serial.print("Kanal:             ");
  Serial.println(WiFi.channel());
  Serial.print("Signal (RX):       ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.print("IP-Adresse:        ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC-Adresse:       ");
  Serial.println(WiFi.macAddress());
  Serial.println();

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    sdAvailable = false;
  } else {
    sdAvailable = true;
    Serial.println("card initialized.");
  }
  // Initialize a NTPClient to get time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // GMT 0 = 0
  timeClient.setTimeOffset(7200);
  delay(2000);
}
//end setup

//loop()
void loop() {
  timeClient.update();
  sensors.requestTemperatures();
  temperatureC = sensors.getTempCByIndex(0);

  // HTTP-Clients initialisieren
  HTTPClient http;
  //Jason buffer
  DynamicJsonDocument jsonBufferFlow(capacityFlow);

  // URL - Flow Data
  http.begin(wifiClient, urlFlow);  // get the result (**the error code**)
  //http.begin(urlFlow);
  int httpCodeFlow = http.GET();
  // JSON-Antwort
  String httpResponseFlow = http.getString();
  // HTTP-Client beenden
  http.end();

  auto jsonFlowError = deserializeJson(jsonBufferFlow, httpResponseFlow);
  if (jsonFlowError) {
    // JSON nicht erfolgreich geparst
    Serial.print("JSON-Parser:       ");
    Serial.println(jsonFlowError.c_str());

  } else {
    //Serial.println("JSON-Parser:       OK");
  }
  Serial.println();

  // Timestamp Daten Power Flow
  String ts_flow = (jsonBufferFlow["Head"]["Timestamp"] | "Leer");
  // Einspeisung / Bezug: Negativ Einspeisung, positiv Bezug
  auto in_out = jsonBufferFlow["Body"]["Data"]["Site"]["P_Grid"].as<float>();

  // Ausgabe seriell
  Serial.print("Timestamp Flow:   ");
  Serial.println(ts_flow);
  Serial.println();

  // Negativ Einspeisung, positiv Bezug
  if (in_out >= 0) {
    // Bezug
    Serial.print("Strom Bezug:       ");
    Serial.print(in_out);
    Serial.println(" W");
    Serial.println("Strom Einspeisung: 0.00 W");
  } else {
    // Einspeisung
    Serial.println("Strom Bezug:       0.00 W");
    Serial.print("Strom Einspeisung: ");
    Serial.print(in_out * -1);
    Serial.println(" W");
  }
  Serial.print("Temperatur Boiler: ");
  Serial.print(temperatureC);
  Serial.println(" C");

  //check if heating to target temperature is necessary
  int currentHour = timeClient.getHours();
  if (currentHour == 0 && temperatureReachedOnThisDay) {  //reset the heating to target temperature
    temperatureReachedOnThisDay = false;
  }
  if (currentHour >= 5 && temperatureReachedOnThisDay == false) {
    Serial.println("Debug 1");
    Serial.print(currentHour);
    HeatingToTargetTemperature(temperatureC, targetTemperature);
    Serial.println("Debug 8");
  }
  Serial.println("Debug 9");

  //control loop!
  if (!heating) {
    if (in_out <= -10 && value < 1024) {
      value = value + 4;
    }
    if (in_out >= 0 && value > 0) {
      value = value - 4;
    }
  }

  Serial.println("Debug 10");
  //write the value to the output
  analogWrite(GPIO_PIN_PWM_CONTROL, value);
  Serial.print("Power value that the heater is heating (1024=100% (1600W) ) power): ");
  Serial.println(value);

  TBMessage msg;
  Serial.println("Debug 11");
  //TelegramBot

  if (CTBotMessageText == myBot.getNewMessage(msg)){
      // ...forward it to the sender  
    Serial.println("Case Telegramm bot");
    Serial.println("Debug 11 receive nöööö");

    String responseTextBot;

    if (msg.text == "/help") {

      String tempOut = " /temp -- gibt die aktuelle Temperatur aus \n ";
      String settempOut = "/settemp --setzt die Temperatur, die auf die er in der Nacht heizt zwischen 40 und 65 \n";
      String urlaubOnOut = "/urlaubOn --  Urlaubsmodus gesetzt. Es wird nur noch mit Überschussstrom geheizt \n";
      String urlaubOffOut = "/urlaubOff -- Urlaubsmodus aus. Es wird in der Nacht auf " + String(targetTemperature) + " Grad C geheitzt. \n";
      String valueOut = "/value -- Wert der Phasenanschnittsterung und die aktuell entsprechende Leistung in Watt \n/heizen --Heizt bis auf " + String(targetTemperature) + " Grad C. \n";
      String heizenOffOut = "/heizenOff --Stoppt das heizen auf " + String(targetTemperature) + " Grad C.\n";
      String verbrauchOut = "/verbrauch -- Gibt den aktuellen Stromverbrauch in Watt zurück.\n/status --Gibt alle Werte des Programmes aus";

      responseTextBot = tempOut + settempOut + urlaubOnOut + urlaubOffOut + valueOut + heizenOffOut + verbrauchOut;
    } else if (msg.text == "/status") {
      responseTextBot = "" + String(temperatureC) + " Grad Celsius des Wassers \n" + String(value) +
      " Value vom Phasenanschnitt; entspricht " + String(value * 1600 / 1024) + " Watt Leistung\nHat heute schon geheizt: " + temperatureReachedOnThisDay + 
      "\nEs werden gerade " + String(in_out) + " Watt Verbraucht.\nEs wird in der Nacht auf " + String(targetTemperature) + " Grad C geheizt.\nUrlaubsmodus: " + hollidayModus + "";
    } else if (msg.text == "/temp") {
      responseTextBot = String(temperatureC) + " Grad Celsius";
    } else if (msg.text == "/urlaubOn") {
      hollidayModus = true;
      responseTextBot = "Urlaubsmodus gesetzt. Es wird nur noch mit Überschussstrom geheitzt.";
    } else if (msg.text == "/urlaubOff") {
      hollidayModus = false;
      responseTextBot = "Urlaubsmodus aus. Es wird in der Nacht auf " + String(targetTemperature) + " Grad C geheizt.";
    } else if (msg.text == "/verbrauch") {
      responseTextBot = "Es wird gerade " + String(in_out) + " Watt Verbraucht.";
    } else if (msg.text == "/heizen") {
      temperatureReachedOnThisDay = false;
      responseTextBot = "Heitz bis auf " + String(targetTemperature) + " Grad C.";
    } else if (msg.text == "/heizenOff") {
      temperatureReachedOnThisDay = true;
      responseTextBot = "Stoppt das heizen auf" + String(targetTemperature) + " Grad C.";
    } else if (msg.text == "/value") {
      responseTextBot = String(value) + " Value vom Phasenanschnitt; entspricht " + String(value * 1600 / 1024) + " Watt Leistung";
    } else if (msg.text.substring(0, 8) == "/settemp") {
      if (IsNumber(msg.text.substring(9, 11))) {
        float newTargetTemperature = msg.text.substring(9, 11).toFloat();
        if (newTargetTemperature >= 40 && newTargetTemperature <= 65) {
          targetTemperature = newTargetTemperature;
          responseTextBot = String(targetTemperature) + " Grad C wurde als neue Target Temperatur gesezt.";
        } else {
          responseTextBot = msg.text.substring(9, 11) + " <-- außerhalb der Grenzen min 40 max 65";
        }
      }
    } else {
        Serial.println("Debug 11 wäää");

      responseTextBot = "Versteh ich nicht";
    }

    Serial.println("Debug 12");

    myBot.sendMessage(msg.sender.id, responseTextBot);
  }

  Serial.println("Debug 13");

  //if SD card is available --> log data
  if (sdAvailable) {
    Serial.println("Log Data");
    // 5s warten
    //Daten sichern auf SD Kart
    String logData = String(timeClient.getEpochTime()) + ";" + String(value) + ";" + String(in_out) + ";" + String(temperatureC);
    File dataFile = SD.open("log.txt", FILE_WRITE);
    if (dataFile) {
      dataFile.println(logData);
      dataFile.close();
      // print to the serial port too:
      Serial.println(logData);
    }
    // if the file isn't open, pop up an error:
    else {
      Serial.println("error opening datalog.txt");
    }
  }
   delay(1000);
  // Ende loop()
}
//heats the boiler to the set target temperatre
void HeatingToTargetTemperature(float currentTemperature, float targetTemperature) {

  Serial.println("Debug 2");
  // Overnight heating
  int currentHour = timeClient.getHours();
  Serial.println(currentHour);
  Serial.println("Debug 3");
  if (currentTemperature > targetTemperature) {
    //ausschalten
    heating = false;
    Serial.println("Debug 4");
    value = 0;
    temperatureReachedOnThisDay = true;
    Serial.println(currentTemperature);
    Serial.println("Debug 5");
  } else if (!heating && !hollidayModus) {
    value = 1024;  //MAX Leistung
    heating = true;
  } else if (hollidayModus) {
    value = 0;
    heating = false;
  }

  Serial.print("Zeit in h:  ");
  Serial.println(currentHour);
  Serial.println(value);
  Serial.println("Debug 13");
  Serial.println(heating);
}
bool IsNumber(String input) {
  for (int i = 1; i < 2; i++) /* Überprüfe Ziffern bzw Zahl */
  {
    if (input[i] < '0' || input[i] > '9')
      return 0;
  }
  return 1;
}
SimonESchiller commented 1 year ago

here is the error message


User exception (panic/abort/assert)
--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Panic core_esp8266_main.cpp:215 loop_task

>>>stack>>>

ctx: sys
sp: 3fffeee0 end: 3fffffb0 offset: 0000
3fffeee0:  00000000 00000020 3fff1d3c 40100cba  
3fffeef0:  000000fe 00000000 00000000 00000000  
3fffef00:  00000000 00000000 00000000 3ffef5f4  
3fffef10:  c0000000 3fffd9d0 00000000 3ffef5ac  
3fffef20:  3fffdad0 00000000 3ffe8674 40210232  
3fffef30:  4025d746 3fffdab0 00000000 4021029f  
3fffef40:  00000000 3fffdad0 3ffef5ac 4020f954  
3fffef50:  40000f49 40000f49 3fffdab0 40000f49  
3fffef60:  40000e19 00070745 00000000 00000005  
3fffef70:  3fffefc0 aa55aa55 000000f7 40105a45  
3fffef80:  40105a4b 00000000 00000005 40100c94  
3fffef90:  4010000d 00040000 00070745 401000ac  
3fffefa0:  4024d9a8 3fffef3c 4024d961 3ffff738  
3fffefb0:  3fffffc0 00000000 00000000 000000ff  
3fffefc0:  0000005c 00000001 40105891 3ffede08  
3fffefd0:  00000002 00000000 00000020 4010018c  
3fffefe0:  40102add feefeffe 00000002 401026b0  
3fffeff0:  3ffe9c42 4010592b 3ffed3d8 ffffffff  
3ffff000:  00000001 40104b06 3ffede08 0fd1f132  
3ffff010:  40104f77 9f8aef9e 411eb149 d14aec82  
3ffff020:  40105379 3ffede08 3ffed3d8 00040000  
3ffff030:  40104a3f 00000037 00000002 00040000  
3ffff040:  00002200 00080000 00000002 feefeffe  
3ffff050:  40103908 00080000 feefeffe feefeffe  
3ffff060:  feefeffe 00000000 feefeffe feefeffe  
3ffff070:  feefeffe 2c9f0300 4000050c 3fffc278  
3ffff080:  401035f0 3fffc200 00000022 feefeffe  
3ffff090:  4022d10f 00000030 00000006 ffffffff  

.
.
.
.
.
.

3ffffe50:  40215674 00000000 000003e8 4020ebf8  
3ffffe60:  00000000 00000000 00000000 4020a150  
3ffffe70:  00000000 00000000 3ffef42c 00000000  
3ffffe80:  001a001f 00000000 00000000 0015001f  
3ffffe90:  00000000 00000000 0030332e 00000000  
3ffffea0:  00000000 0014001f 00000000 00000000  
3ffffeb0:  000d000f 00000000 00000000 00000000  
3ffffec0:  00000000 00000000 000c000f 00000000  
3ffffed0:  00000000 000b000f 00000000 00000000  
3ffffee0:  001a001f 00000000 3ffef5ac 00000030  
3ffffef0:  401006dc 4020443f 00000020 402207c0  
3fffff00:  00000000 4020443f 00000058 402207f0  
3fffff10:  3ffe9314 00000800 0000004c 40223066  
3fffff20:  00f42400 8be4617a 3fff0400 402042e4  
3fffff30:  40106321 004538ad 3ffef614 00000000  
3fffff40:  3ffeee10 3ffef614 000007d0 3ffef3ac  
3fffff50:  00000001 3ffef614 000007d0 4020f9cc  
3fffff60:  3fff11f4 0019001f 80000000 3fff1f7c  
3fffff70:  02c102cf 80000000 3ffef2f8 402015d1  
3fffff80:  00000000 00000000 00000001 4010018c  
3fffff90:  3fffdad0 00000000 3ffef598 3ffef5ac  
3fffffa0:  3fffdad0 00000000 3ffef598 4020faec  
<<<stack<<<

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v00070750
~ld
shurillu commented 1 year ago

Hello Simon, it's not easy to debug without having a test rig here. Anyway, a good way to understand where/who trigger the exception is installing the Exception Decoder:

https://github.com/me-no-dev/EspExceptionDecoder

Following the instructions and once installed, with the Exception Decoder when the ESP(8266/32) throw an exception, the stack trace will be decoded and you can understand all the calls that have fired the exception. I strongly suggest you to use that tool, it's very helpful for debugging such kind of issues. It works with Arduino IDE, but I guess you can found similar tool for other platform (such as PlatformIO, VisualStudio, etc).

Another question: which version of the CTBot library and ESP8266 toolchain are you using?

Cheers,

Stefano