jpraus / arduino-opentherm

Arduino library and hardware shield to send and receive data from Opentherm devices
Other
160 stars 42 forks source link

ESP8266 exception #10

Closed NijzinkM closed 4 years ago

NijzinkM commented 4 years ago

When testing my ESP8266 build the code seems to run fine, but when connecting the 24V adapter, after about 15 seconds this exception occurs:

Exception 0: Illegal instruction PC: 0x40201774: OPENTHERM::_read() at C:\Users\Mart\Documents\Arduino\libraries\Arduino_Opentherm\opentherm.cpp line 94

I really don't know what this means and how to solve it. When I use one of the examples, the error doesn't occur, so I think it has something to do with the combination of Opentherm communication and WiFi communication.

This is my sketch:

#include <opentherm.h>
#include <ESP8266WiFi.h>
#include <MQTT.h>
#include <ArduinoOTA.h>

// Wemos D1 R2 opentherm pins
#define THERMOSTAT_IN 14
#define THERMOSTAT_OUT 5
#define BOILER_IN 12
#define BOILER_OUT 4

#define MODE_LISTEN_MASTER 0
#define MODE_LISTEN_SLAVE 1

// AP config
#ifndef AP_NETWORK_NAME
  #define AP_NETWORK_NAME "***"
  #define AP_PASSWORD "***"
#endif

#define SUB_TOPIC "opentherm/thermo/temperature/command"
#define PUB_TOPIC "opentherm/thermo/temperature/state"
#define CLIENT_ID "opentherm"
#define USER_NAME "***"
#define USER_PASSWORD "***"
#define QOS 1

void myDataCb(String& topic, String& data);
void myDisconnectedCb();
void myConnectedCb();

MQTT myMqtt(CLIENT_ID, "192.168.4.1", 1883);

WiFiEventHandler gotIpEventHandler;
WiFiEventHandler timeoutEventHandler;

void setup() {
  pinMode(THERMOSTAT_IN, INPUT);
  digitalWrite(THERMOSTAT_IN, HIGH); // pull up
  digitalWrite(THERMOSTAT_OUT, HIGH);
  pinMode(THERMOSTAT_OUT, OUTPUT); // low output = high current, high output = low current
  pinMode(BOILER_IN, INPUT);
  digitalWrite(BOILER_IN, HIGH); // pull up
  digitalWrite(BOILER_OUT, LOW);
  pinMode(BOILER_OUT, OUTPUT); // low output = high voltage, high output = low voltage

  Serial.begin(115200);
  delay(3000);

  myMqtt.onConnected(myConnectedCb);
  myMqtt.onDisconnected(myDisconnectedCb);
  myMqtt.onData(myDataCb);

  myMqtt.setUserPwd(USER_NAME, USER_PASSWORD);

  WiFi.setAutoReconnect(false);
  WiFi.persistent(false);

  connectToMqttBroker();
}

void connectToAP() {
  Serial.println();
  Serial.print("ESP - Connecting to ");
  Serial.print(AP_NETWORK_NAME);
  Serial.println("...");

  WiFi.mode(WIFI_STA); // If this ESP has been used as an Access Point, this method disables broadcasting

  WiFi.begin(AP_NETWORK_NAME, AP_PASSWORD);
}

void connectToMqttBroker() {
  Serial.println("ESP - Attempt to connect to MQTT Broker");

  if (!WiFi.isConnected()) {
    Serial.println("ESP - No WiFi connection");

    gotIpEventHandler = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP& event) {
      Serial.print("ESP - Connected trough WiFi, IP: ");
      Serial.println(WiFi.localIP());

      startOTA();

      Serial.println("ESP - Connecting to MQTT Broker...");  
      myMqtt.connect();
    });

    timeoutEventHandler = WiFi.onStationModeDHCPTimeout([]() {
      Serial.println("ESP - Time out");
    });

    connectToAP();
  } else {
    Serial.println("ESP - WiFi already connected");

    startOTA();

    myMqtt.connect();
  }
}

void startOTA() {
  Serial.println("ESP - Enable OTA");
  ArduinoOTA.begin();
}

OpenthermData message;
int mode = 0;

float roomTemperature;
float roomSetpoint;

unsigned long previousConnectionCheck;

void loop() {
  // MQTT connection
  if (!myMqtt.isConnected() && (unsigned long) (millis() - previousConnectionCheck) > 15000) {
    connectToMqttBroker();
    previousConnectionCheck = millis();
  }

  // Over the air updates
  ArduinoOTA.handle();

  // Opentherm gateway
  if (mode == MODE_LISTEN_MASTER) {
    if (OPENTHERM::isSent() || OPENTHERM::isIdle() || OPENTHERM::isError()) {
      Serial.println("OPENTHERM - Listening to master");
      OPENTHERM::listen(THERMOSTAT_IN, -1);
    } else if (OPENTHERM::getMessage(message)) {
      Serial.print("-> ");
      OPENTHERM::printToSerial(message);
      Serial.println();
      OPENTHERM::send(BOILER_OUT, message); // forward message to boiler
      mode = MODE_LISTEN_SLAVE;
    }
  } else if (mode == MODE_LISTEN_SLAVE) {
    if (OPENTHERM::isSent()) {
      Serial.println("OPENTHERM - Listening to slave");
      OPENTHERM::listen(BOILER_IN, 800); // response needs to be send back by boiler within 800ms
    } else if (OPENTHERM::getMessage(message)) {
      Serial.print("<- ");
      OPENTHERM::printToSerial(message);
      Serial.println();
      Serial.println();
      OPENTHERM::send(THERMOSTAT_OUT, message); // send message back to thermostat
      mode = MODE_LISTEN_MASTER;
    } else if (OPENTHERM::isError()) {
      mode = MODE_LISTEN_MASTER;
      Serial.println("<- Timeout");
      Serial.println();
    }
  }

  delay(1);
}

void publishRoomTemp() {
  Serial.println("ESP - Publishing room temperature");
  char tempStr[6];
  dtostrf(roomTemperature, 5, 1, tempStr);       
  myMqtt.publish(PUB_TOPIC, tempStr, QOS, 1);
}

void myConnectedCb() {
  Serial.println();
  Serial.println("ESP - Connected to MQTT Broker");

  subscribe();

  publishRoomTemp();
}

void subscribe() {
  Serial.print("ESP - Subscribing to ");
  Serial.println(SUB_TOPIC);
  myMqtt.subscribe(SUB_TOPIC, QOS);
}

void myDisconnectedCb() {
  Serial.println("ESP - Disconnected from MQTT Broker");
}

void myDataCb(String& topic, String& data) {
  Serial.print(topic);
  Serial.print(": ");
  Serial.println(data);

  // TODO: queue message
}

Spent half a day on this already.

jpraus commented 4 years ago

Hi! Thanks for reporting. What board configuration are you using?

NijzinkM commented 4 years ago

Hello Jiří,

This is my board config: afbeelding

I also tried other flash sizes and IwIP variants. The self-test example runs successfully.

NijzinkM commented 4 years ago

The circuit is not connected to the real boiler and thermostat yet. I want to make sure everything works before I install the gateway.

When I short the therm terminal to the boiler terminal with two wires, the same exception occurs at OPENTHERM::_listen()

Espressif says that exception(0) can be thrown because of a wild pointer.

NijzinkM commented 4 years ago

By adding some print statements to the library, I found that the problem is related to issue #8. The program got stuck because the timeout was not called. I'll create a pull request so you can review the small changes I made to fix the timeout issue, because you probably know better than me if it will hit other parts of the code.

jpraus commented 4 years ago

Thank you! I am really looking forward to it!

NijzinkM commented 4 years ago

Unfortunately, the problem has returned because _timerISR is called continuously with _mode MODE_READ when master_in is HIGH. In this case, the timeout is never called. Should a read timeout also be implemented? Or should master_in be held LOW manually when testing? I think the latter will also solve the problem.