Opentherm gateway + esp8266 weekend project

For OpenTherm-gateway related questions in Domoticz
Post Reply
Domotibart
Posts: 9
Joined: Tuesday 06 December 2016 19:53
Target OS: NAS (Synology & others)
Domoticz version: V3.8153
Location: holland
Contact:

Opentherm gateway + esp8266 weekend project

Post by Domotibart » Sunday 02 December 2018 22:40

I have been using the OTGW in domoticz for 2 years now. The first year by USB connection. To get more flexibility i added a esp8266 to connect the OTGW via LAN.
Unfortunately i never got this to work flawless. In the beginning i could not even connect domoticz to the esp8266. For some reason the otmonitor tool worked at once without any problems.
After experimenting with some different firmware for the esp8266 i got it working. At this time use the latest espeasy firmware. But ..... stil i get a lot of errors. It works at startup but after a few hours i reguelarly get zero valuesto eventualy lose the connection. Setting the room temperature from domoticz never worked.
So why does everything work perfect with the otmonitor software and does not in domoticz. I beleive the big difference is that domoticz uses PS=1 to control OTGW and otmonitor does not.
So here my weekend project was born. Let domoticz do the same as otmonitor does. This is how;
The plan is that the ESP is going to parse all the opentherm codes and put the all the sensor data in its dummy sensors. The ESPeasy firmware takes care of sending all the data to Domoticz. Bonus with this method is that i can use domoticz and otmonitor at the same time.
1- First step is to change the ser2net ino from the easyesp firmware. I added some code to parse the opentherm data and create events in the ESP8266. I had never done this so this took some time with trial and error.
2 - Second step make some rules in espeasy to add the parsed data in the corresponding dummy sensors. This is a standard procedure and was easy to do.
3 - Third step was to create virtual sensors in domoticz so that the esp8266 transfers the data to domoticz.
4 - fouth step was to make a virtual setpoint device in domoticz and send the new temperature setpoint to the esp8266 so that the esp8266 can send a serial command to the OTGW setting a temporary temperature. This was the most challenging part becouse i had to make a lua script in domoticz so send a command to the esp8266.
Its now sunday and i have got three values in domoticz; room temperature, room setpoint , boiler temperature and a setpoint temperature thermostat.
It is working for a few hours but it look promising, its the first time that i can set a the therostat from domoticz!
I will put the codes from the ser2net.ino , the lua script, and the esp rules in my next post maybe someone who has more coding skills can optimize them. If not, for me it is a nice workaround to get opentherm working without any errors in domoticz.
Synology DS214play - esp8266 Opentherm Gateway - esp8266 somfyremote - esp8266 poolcontrol - esp8266 energyreader - zwave - Philips hue

Domotibart
Posts: 9
Joined: Tuesday 06 December 2016 19:53
Target OS: NAS (Synology & others)
Domoticz version: V3.8153
Location: holland
Contact:

Re: Opentherm gateway + esp8266 weekend project

Post by Domotibart » Sunday 02 December 2018 23:04

Here's the first part of my project. The INO code from the easyesp firmware. I made a new firmware.bin so that you can download it on the esp8266.

#ifdef USES_P020
//#######################################################################################################
//#################################### Plugin 020: Ser2Net ##############################################
//#######################################################################################################

#define PLUGIN_020
#define PLUGIN_ID_020 20
#define PLUGIN_NAME_020 "Communication - Serial Server"
#define PLUGIN_VALUENAME1_020 "Ser2Net"

#define P020_BUFFER_SIZE 128
boolean Plugin_020_init = false;
byte Plugin_020_SerialProcessing = 0;

WiFiServer *ser2netServer;
WiFiClient ser2netClient;

boolean Plugin_020(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte connectionState = 0;

switch (function)
{

case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_020;
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
Device[deviceCount].Custom = true;
Device[deviceCount].TimerOption = false;
break;
}

case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_020);
break;
}

case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_020));
break;
}

case PLUGIN_GET_DEVICEGPIONAMES:
{
event->String1 = formatGpioName_bidirectional(F("Reset"));
break;
}

case PLUGIN_WEBFORM_LOAD:
{
addFormNumericBox(F("TCP Port"), F("p020_port"), ExtraTaskSettings.TaskDevicePluginConfigLong[0]);
addFormNumericBox(F("Baud Rate"), F("p020_baud"), ExtraTaskSettings.TaskDevicePluginConfigLong[1]);
addFormNumericBox(F("Data bits"), F("p020_data"), ExtraTaskSettings.TaskDevicePluginConfigLong[2]);

byte choice = ExtraTaskSettings.TaskDevicePluginConfigLong[3];
String options[3];
options[0] = F("No parity");
options[1] = F("Even");
options[2] = F("Odd");
int optionValues[3];
optionValues[0] = 0;
optionValues[1] = 2;
optionValues[2] = 3;
addFormSelector(F("Parity"), F("p020_parity"), 3, options, optionValues, choice);

addFormNumericBox(F("Stop bits"), F("p020_stop"), ExtraTaskSettings.TaskDevicePluginConfigLong[4]);

addFormPinSelect(F("Reset target after boot"), F("taskdevicepin1"), Settings.TaskDevicePin1[event->TaskIndex]);

addFormNumericBox(F("RX Receive Timeout (mSec)"), F("p020_rxwait"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);


byte choice2 = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
String options2[3];
options2[0] = F("None");
options2[1] = F("Generic");
options2[2] = F("OpenTherm");
addFormSelector(F("Event processing"), F("p020_events"), 3, options2, NULL, choice2);

success = true;
break;
}

case PLUGIN_WEBFORM_SAVE:
{
ExtraTaskSettings.TaskDevicePluginConfigLong[0] = getFormItemInt(F("p020_port"));
ExtraTaskSettings.TaskDevicePluginConfigLong[1] = getFormItemInt(F("p020_baud"));
ExtraTaskSettings.TaskDevicePluginConfigLong[2] = getFormItemInt(F("p020_data"));
ExtraTaskSettings.TaskDevicePluginConfigLong[3] = getFormItemInt(F("p020_parity"));
ExtraTaskSettings.TaskDevicePluginConfigLong[4] = getFormItemInt(F("p020_stop"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("p020_rxwait"));
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("p020_events"));
success = true;
break;
}

case PLUGIN_INIT:
{
LoadTaskSettings(event->TaskIndex);
if ((ExtraTaskSettings.TaskDevicePluginConfigLong[0] != 0) && (ExtraTaskSettings.TaskDevicePluginConfigLong[1] != 0))
{
#if defined(ESP8266)
byte serialconfig = 0x10;
#endif
#if defined(ESP32)
uint32_t serialconfig = 0x8000010;
#endif
serialconfig += ExtraTaskSettings.TaskDevicePluginConfigLong[3];
serialconfig += (ExtraTaskSettings.TaskDevicePluginConfigLong[2] - 5) << 2;
if (ExtraTaskSettings.TaskDevicePluginConfigLong[4] == 2)
serialconfig += 0x20;
#if defined(ESP8266)
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], (SerialConfig)serialconfig);
#endif
#if defined(ESP32)
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], serialconfig);
#endif
ser2netServer = new WiFiServer(ExtraTaskSettings.TaskDevicePluginConfigLong[0]);
ser2netServer->begin();

if (Settings.TaskDevicePin1[event->TaskIndex] != -1)
{
pinMode(Settings.TaskDevicePin1[event->TaskIndex], OUTPUT);
digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], LOW);
delay(500);
digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], HIGH);
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
}

Plugin_020_init = true;
}
Plugin_020_SerialProcessing = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
success = true;
break;
}

case PLUGIN_TEN_PER_SECOND:
{
if (Plugin_020_init)
{
size_t bytes_read;
if (ser2netServer->hasClient())
{
if (ser2netClient) ser2netClient.stop();
ser2netClient = ser2netServer->available();
addLog(LOG_LEVEL_ERROR, F("Ser2N: Client connected!"));
}

if (ser2netClient.connected())
{
connectionState = 1;
uint8_t net_buf[P020_BUFFER_SIZE];
int count = ser2netClient.available();
if (count > 0)
{
if (count > P020_BUFFER_SIZE)
count = P020_BUFFER_SIZE;
bytes_read = ser2netClient.read(net_buf, count);
Serial.write(net_buf, bytes_read);
Serial.flush(); // Waits for the transmission of outgoing serial data to complete

if (count == P020_BUFFER_SIZE) // if we have a full buffer, drop the last position to stuff with string end marker
{
count--;
addLog(LOG_LEVEL_ERROR, F("Ser2N: network buffer full!"));
}
net_buf[count] = 0; // before logging as a char array, zero terminate the last position to be safe.
char log[P020_BUFFER_SIZE + 40];
sprintf_P(log, PSTR("Ser2N: N>: %s"), (char*)net_buf);
addLog(LOG_LEVEL_DEBUG, log);
}
}
else
{
if (connectionState == 1) // there was a client connected before...
{
connectionState = 0;
// workaround see: https://github.com/esp8266/Arduino/issu ... -373023864
ser2netClient = WiFiClient();
addLog(LOG_LEVEL_ERROR, F("Ser2N: Client disconnected!"));
}

while (Serial.available())
Serial.read();
}

success = true;
}
break;
}

case PLUGIN_SERIAL_IN:
{
if (Plugin_020_init)
{
uint8_t serial_buf[P020_BUFFER_SIZE];
int RXWait = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
if (RXWait == 0)
RXWait = 1;
int timeOut = RXWait;
size_t bytes_read = 0;
while (timeOut > 0)
{
while (Serial.available()) {
if (bytes_read < P020_BUFFER_SIZE) {
serial_buf[bytes_read] = Serial.read();
bytes_read++;
}
else
Serial.read(); // when the buffer is full, just read remaining input, but do not store...

timeOut = RXWait; // if serial received, reset timeout counter
}
delay(1);
timeOut--;
}

if (bytes_read != P020_BUFFER_SIZE)
{
if (bytes_read > 0) {
if (Plugin_020_init && ser2netClient.connected())
{
ser2netClient.write((const uint8_t*)serial_buf, bytes_read);
ser2netClient.flush();
}
}
}
else // if we have a full buffer, drop the last position to stuff with string end marker
{
while (Serial.available()) // read possible remaining data to avoid sending rubbish...
Serial.read();
bytes_read--;
// and log buffer full situation
addLog(LOG_LEVEL_ERROR, F("Ser2N: serial buffer full!"));
}
serial_buf[bytes_read] = 0; // before logging as a char array, zero terminate the last position to be safe.
char log[P020_BUFFER_SIZE + 40];
sprintf_P(log, PSTR("Ser2N: S>: %s"), (char*)serial_buf);
addLog(LOG_LEVEL_DEBUG, log);

// We can also use the rules engine for local control!
if (Settings.UseRules)
{
String message = (char*)serial_buf;
String message1 = "";
String dot = ".";
int NewLinePos = message.indexOf(F("\r\n"));
if (NewLinePos > 0)
message = message.substring(0, NewLinePos);
String eventString = "";

switch (Plugin_020_SerialProcessing)
{
case 0:
{
break;
}

case 1: // Generic
{
eventString = F("!Serial#");
eventString += message;
break;
}

case 2: // opentherm
{
if (message.substring(3,5) == "10")
{
message1 = strtol(message.substring(7,9).c_str(),0,16);
message = strtol(message.substring(5,7).c_str(),0,16);
eventString = ("room#setpoint=");
eventString += message += dot += message1;
}
if (message.substring(3,5) == "18")
{
message1 = strtol(message.substring(7,9).c_str(),0,16);
message = strtol(message.substring(5,7).c_str(),0,16);
eventString = F("room#temp=");
eventString += message += dot += message1;
}
if (message.substring(3,5) == "1A")
{
message1 = strtol(message.substring(7,9).c_str(),0,16);
message = strtol(message.substring(5,7).c_str(),0,16);
eventString = F("tapwater#temp=");
eventString += message += dot += message1;
}
if (message.startsWith("B4019"))
{
message1 = strtol(message.substring(7,9).c_str(),0,16);
message = strtol(message.substring(5,7).c_str(),0,16);
eventString = ("boiler#temp=");
eventString += message += dot += message1;
}
break;
}
} // switch

if (eventString.length() > 0)
rulesProcessing(eventString);

} // if rules
success = true;
break;
}
}

case PLUGIN_WRITE:
{
String command = parseString(string, 1);
if (command == F("serialsend"))
{
success = true;
String tmpString = string.substring(11);
Serial.println(tmpString); // FIXME TD-er: Should this also use the serial write buffer?
}
break;
}

}
return success;
}
#endif // USES_P020
Synology DS214play - esp8266 Opentherm Gateway - esp8266 somfyremote - esp8266 poolcontrol - esp8266 energyreader - zwave - Philips hue

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest