From ee758a1bb99feeec36d935369593cb9e3db24ba8 Mon Sep 17 00:00:00 2001 From: esp8266nu Date: Sat, 29 Aug 2015 15:45:46 +0000 Subject: [PATCH] Preliminary support for IO extender based on Arduino Pro Mini through I2C Added GPIO-9 and GPIO-10 for boards equipped with an ESP12E module (NodeMCU V1.0) Finished support for PCF8591 4 channel Analog to Digital converter. Added support for MCP23017 input ports Unit Name is shown in html title Show 'well known' devices in the I2C scanner based on their I2C address Added option to use simple formulas on device values like "%value%/1000" Fixed char string handling that should be 25 chars max Added option to turn on/off internal pullup for switch input pins Added option to set a name for each task (also used for OpenHAB MQTT publishing) Preliminary support for OpenHAB MQTT protocol --- Controller.ino | 355 +++++++++++++++++++++++++++++----------------- Devices.ino | 202 ++++++++++++++++++++++---- ESPEasy.ino | 167 ++++++++++++++++------ Misc.ino | 263 +++++++++++++++++++++++++++++++++- Serial.ino | 69 ++++++++- WebServer.ino | 295 ++++++++++++++++++++------------------ _ReleaseNotes.ino | 185 +++++++++++++----------- 7 files changed, 1091 insertions(+), 445 deletions(-) diff --git a/Controller.ino b/Controller.ino index 4d3a300f7..c26318dc1 100644 --- a/Controller.ino +++ b/Controller.ino @@ -61,31 +61,34 @@ boolean Domoticz_getData(int idx, float *data) //******************************************************************************** // Interface for Sending to Controllers //******************************************************************************** -boolean sendData(byte sensorType, int idx, byte varIndex) +boolean sendData(byte TaskNr, byte sensorType, int idx, byte varIndex) { switch (Settings.Protocol) { - case 1: + case PROTOCOL_DOMOTICZ_HTTP: Domoticz_sendData(sensorType, idx, varIndex); break; - case 2: + case PROTOCOL_DOMOTICZ_MQTT: Domoticz_sendDataMQTT(sensorType, idx, varIndex); break; - case 3: + case PROTOCOL_NODO_TELNET: NodoTelnet_sendData(sensorType, idx, varIndex); - case 4: + case PROTOCOL_THINGSPEAK: ThingsSpeak_sendData(sensorType, idx, varIndex); break; + case PROTOCOL_OPENHAB_MQTT: + OpenHAB_sendDataMQTT(TaskNr, sensorType, idx, varIndex); + break; } if (Settings.MessageDelay != 0) - { - char log[30]; - sprintf_P(log, PSTR("HTTP : Delay %u ms"), Settings.MessageDelay); - addLog(LOG_LEVEL_DEBUG_MORE,log); - unsigned long timer = millis() + Settings.MessageDelay; - while (millis() < timer) - backgroundtasks(); - } + { + char log[30]; + sprintf_P(log, PSTR("HTTP : Delay %u ms"), Settings.MessageDelay); + addLog(LOG_LEVEL_DEBUG_MORE, log); + unsigned long timer = millis() + Settings.MessageDelay; + while (millis() < timer) + backgroundtasks(); + } } //#endif @@ -100,7 +103,7 @@ boolean Domoticz_sendData(byte sensorType, int idx, byte varIndex) sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]); sprintf_P(log, PSTR("%s%s"), "HTTP : connecting to ", host); - addLog(LOG_LEVEL_DEBUG,log); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -111,7 +114,7 @@ boolean Domoticz_sendData(byte sensorType, int idx, byte varIndex) if (!client.connect(host, Settings.ControllerPort)) { connectionFailures++; - addLog(LOG_LEVEL_ERROR,(char*)"HTTP : connection failed"); + addLog(LOG_LEVEL_ERROR, (char*)"HTTP : connection failed"); if (printToWeb) printWebString += F("connection failed
"); return false; @@ -154,8 +157,8 @@ boolean Domoticz_sendData(byte sensorType, int idx, byte varIndex) break; } - url.toCharArray(log, 79); - addLog(LOG_LEVEL_DEBUG_MORE,log); + url.toCharArray(log, 80); + addLog(LOG_LEVEL_DEBUG_MORE, log); if (printToWeb) { printWebString += log; @@ -174,18 +177,18 @@ boolean Domoticz_sendData(byte sensorType, int idx, byte varIndex) // Read all the lines of the reply from server and print them to Serial while (client.available()) { String line = client.readStringUntil('\n'); - line.toCharArray(log,79); - addLog(LOG_LEVEL_DEBUG_MORE,log); + line.toCharArray(log, 80); + addLog(LOG_LEVEL_DEBUG_MORE, log); if (line.substring(0, 15) == "HTTP/1.1 200 OK") { - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : Succes!"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : Succes!"); if (printToWeb) printWebString += F("Success
"); success = true; } delay(1); } - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : closing connection"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : closing connection"); if (printToWeb) printWebString += F("closing connection
"); @@ -206,7 +209,7 @@ boolean NodoTelnet_sendData(byte sensorType, int var, byte varIndex) sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]); sprintf_P(log, PSTR("%s%s"), "TELNT: connecting to ", host); - addLog(LOG_LEVEL_DEBUG,log); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -217,7 +220,7 @@ boolean NodoTelnet_sendData(byte sensorType, int var, byte varIndex) if (!client.connect(host, Settings.ControllerPort)) { connectionFailures++; - addLog(LOG_LEVEL_ERROR,(char*)"TELNT: connection failed"); + addLog(LOG_LEVEL_ERROR, (char*)"TELNT: connection failed"); if (printToWeb) printWebString += F("connection failed
"); return false; @@ -242,30 +245,30 @@ boolean NodoTelnet_sendData(byte sensorType, int var, byte varIndex) timer = millis() + 1000; while (client.available() && millis() < timer && !success) - { - String line = client.readStringUntil('\n'); - Serial.println(line); - if (line.substring(0, 20) == "Enter your password:") - { - success = true; - Serial.println("Password request ok"); - } - delay(1); + { + String line = client.readStringUntil('\n'); + Serial.println(line); + if (line.substring(0, 20) == "Enter your password:") + { + success = true; + Serial.println("Password request ok"); } - + delay(1); + } + Serial.println("Sending pw"); client.println(Settings.ControllerPassword); delay(100); while (client.available()) Serial.write(client.read()); - + Serial.println("Sending cmd"); client.print(url); delay(10); while (client.available()) Serial.write(client.read()); - addLog(LOG_LEVEL_DEBUG,(char*)"TELNT: closing connection"); + addLog(LOG_LEVEL_DEBUG, (char*)"TELNT: closing connection"); if (printToWeb) printWebString += F("closing connection
"); @@ -297,7 +300,8 @@ void syslog(char *message) // handle MQTT messages void callback(const MQTT::Publish& pub) { String message = pub.payload_string(); - MQTTMessage(&message); + String topic = pub.topic(); + MQTTMessage(&topic, &message); } @@ -319,7 +323,18 @@ void MQTTConnect() if (MQTTclient.connect(clientid)) { Serial.println(F("MQTT : Connected to broker")); - MQTTclient.subscribe("domoticz/out"); + switch (Settings.Protocol) + { + case PROTOCOL_DOMOTICZ_MQTT: + MQTTclient.subscribe("domoticz/out"); + break; + case PROTOCOL_OPENHAB_MQTT: + String pubname = "/"; + pubname += Settings.Name; + pubname += "/#"; + MQTTclient.subscribe(pubname); + break; + } break; } else @@ -333,54 +348,82 @@ void MQTTConnect() /*********************************************************************************************\ * Parse incoming MQTT message \*********************************************************************************************/ -void MQTTMessage(String *message) +void MQTTMessage(String *topic, String *message) { - char json[512]; - json[0] = 0; - message->toCharArray(json, 511); - // if (Settings.Debug == 3) - // Serial.println(json); - StaticJsonBuffer<512> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - - //for (JsonObject::iterator it=root.begin(); it!=root.end(); ++it) - //{ - // Serial.println(it->key); - // Serial.println(it->value.asString()); - //} - - if (root.success()) + switch (Settings.Protocol) { - long idx = root["idx"]; - float nvalue = root["nvalue"]; - long nvaluealt = root["nvalue"]; - const char* name = root["name"]; - const char* svalue = root["svalue"]; - const char* svalue1 = root["svalue1"]; - const char* svalue2 = root["svalue2"]; - const char* svalue3 = root["svalue3"]; + case PROTOCOL_DOMOTICZ_MQTT: + { + char json[512]; + json[0] = 0; + message->toCharArray(json, 512); - if (nvalue == 0) - nvalue = nvaluealt; + StaticJsonBuffer<512> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(json); - Serial.print(F("MQTT : idx=")); - Serial.print(idx); - Serial.print(" name="); - Serial.print(name); - Serial.print(" nvalue="); - Serial.print(nvalue); - Serial.print(" svalue="); - Serial.print(svalue); - Serial.print(" svalue1="); - Serial.print(svalue1); - Serial.print(" svalue2="); - Serial.println(svalue2); - Serial.print(" svalue3="); - Serial.println(svalue3); + if (root.success()) + { + long idx = root["idx"]; + float nvalue = root["nvalue"]; + long nvaluealt = root["nvalue"]; + const char* name = root["name"]; + const char* svalue = root["svalue"]; + const char* svalue1 = root["svalue1"]; + const char* svalue2 = root["svalue2"]; + const char* svalue3 = root["svalue3"]; + + if (nvalue == 0) + nvalue = nvaluealt; + + Serial.print(F("MQTT : idx=")); + Serial.print(idx); + Serial.print(" name="); + Serial.print(name); + Serial.print(" nvalue="); + Serial.print(nvalue); + Serial.print(" svalue="); + Serial.print(svalue); + Serial.print(" svalue1="); + Serial.print(svalue1); + Serial.print(" svalue2="); + Serial.println(svalue2); + Serial.print(" svalue3="); + Serial.println(svalue3); + } + else + Serial.println(F("MQTT : json parse error")); + break; + } + case PROTOCOL_OPENHAB_MQTT: + { + Serial.println(*topic); + String sdevice = ""; + String spin = ""; + String command = topic->substring(1); + int SlashIndex = command.indexOf('/'); + if (SlashIndex > 0) + { + command = command.substring(SlashIndex + 1); + SlashIndex = command.indexOf('/'); + if (SlashIndex > 0) + { + sdevice = command.substring(0, SlashIndex); + spin = command.substring(SlashIndex + 1); + int pin = spin.toInt(); + int value = message->toFloat(); + if (sdevice == "gpio") + { + char cmd[80]; + sprintf_P(cmd, PSTR("gpio,%u,%u"), pin, value); + Serial.println(cmd); + ExecuteCommand(cmd); + } + } + } + break; + } } - else - Serial.println(F("MQTT : json parse error")); } @@ -403,7 +446,7 @@ boolean Domoticz_sendDataMQTT(byte sensorType, int idx, byte varIndex) case 1: // single value sensor, used for Dallas, BH1750, etc root["nvalue"] = 0; values = UserVar[varIndex - 1]; - values.toCharArray(str, 79); + values.toCharArray(str, 80); root["svalue"] = str; break; case 2: // temp + hum + hum_stat, used for DHT11 @@ -412,7 +455,7 @@ boolean Domoticz_sendDataMQTT(byte sensorType, int idx, byte varIndex) values += ";"; values += UserVar[varIndex]; values += ";0"; - values.toCharArray(str, 79); + values.toCharArray(str, 80); root["svalue"] = str; break; case 3: // temp + hum + hum_stat + bar + bar_fore, used for BMP085 @@ -421,7 +464,7 @@ boolean Domoticz_sendDataMQTT(byte sensorType, int idx, byte varIndex) values += ";0;0;"; values += UserVar[varIndex]; values += ";0"; - values.toCharArray(str, 79); + values.toCharArray(str, 80); root["svalue"] = str; break; case 10: // switch @@ -433,30 +476,76 @@ boolean Domoticz_sendDataMQTT(byte sensorType, int idx, byte varIndex) break; } - // JsonArray& data = root.createNestedArray("data"); - // data.add(48.756080, 6); // 6 is the number of decimals to print - // data.add(2.302038, 6); // if not specified, 2 digits are printed - - // test drive mqtt - //String json = "{\"idx\": 161, \"nvalue\": 1, \"svalue\": \""; - //json += millis()/1000; - //json += "\" }"; - //Serial.println(json); - char json[256]; root.printTo(json, sizeof(json)); Serial.print("MQTT : "); Serial.println(json); - addLog(LOG_LEVEL_DEBUG,json); + addLog(LOG_LEVEL_DEBUG, json); if (!MQTTclient.publish("domoticz/in", json)) { Serial.println(F("MQTT publish failed")); MQTTConnect(); connectionFailures++; } - else - if (connectionFailures) - connectionFailures--; + else if (connectionFailures) + connectionFailures--; +} + +/*********************************************************************************************\ + * Send data to Domoticz using MQTT message +\*********************************************************************************************/ +boolean OpenHAB_sendDataMQTT(byte TaskNr, byte sensorType, int idx, byte varIndex) +{ + // MQTT publish structure: + // ////state + // and are web configurable by the user + // value name is prefixed in the device section for each type of device + + char str[80]; + + String pubname = ""; + String value = ""; + + switch (sensorType) + { + case 1: // single value sensor, used for Dallas, BH1750, etc + pubname = "/"; + pubname += Settings.Name; + pubname += "/"; + pubname += Settings.TaskDeviceName[TaskNr]; + pubname += "/"; + pubname += Device[Settings.TaskDeviceNumber[TaskNr]].ValueNames[0]; + pubname += "/state"; + value = String(UserVar[varIndex - 1]); + Serial.println(pubname); + Serial.println(value); + MQTTclient.publish(pubname, value); + break; + case 2: + case 3: + pubname = "/"; + pubname += Settings.Name; + pubname += "/"; + pubname += Settings.TaskDeviceName[TaskNr]; + pubname += "/"; + pubname += Device[Settings.TaskDeviceNumber[TaskNr]].ValueNames[0]; + pubname += "/state"; + value = String(UserVar[varIndex - 1]); + MQTTclient.publish(pubname, value); + pubname = "/"; + pubname += "/"; + pubname += Settings.TaskDeviceName[TaskNr]; + pubname += "/"; + pubname += Device[Settings.TaskDeviceNumber[TaskNr]].ValueNames[1]; + pubname += "/state"; + value = String(UserVar[varIndex]); + MQTTclient.publish(pubname, value); + break; + case 10: // switch + // if (UserVar[varIndex - 1] == 0) + // else + break; + } } struct NodeStruct @@ -477,7 +566,7 @@ boolean ThingsSpeak_sendData(byte sensorType, int idx, byte varIndex) sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]); sprintf_P(log, PSTR("%s%s"), "HTTP : connecting to ", host); - addLog(LOG_LEVEL_DEBUG,log); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -488,7 +577,7 @@ boolean ThingsSpeak_sendData(byte sensorType, int idx, byte varIndex) if (!client.connect(host, Settings.ControllerPort)) { connectionFailures++; - addLog(LOG_LEVEL_ERROR,(char*)"HTTP : connection failed"); + addLog(LOG_LEVEL_ERROR, (char*)"HTTP : connection failed"); if (printToWeb) printWebString += F("connection failed
"); return false; @@ -501,19 +590,19 @@ boolean ThingsSpeak_sendData(byte sensorType, int idx, byte varIndex) switch (sensorType) { case 1: // single value sensor, used for Dallas, BH1750, etc - postDataStr +="&field"; + postDataStr += "&field"; postDataStr += idx; postDataStr += "="; postDataStr += String(UserVar[varIndex - 1]); break; case 2: // dual value case 3: - postDataStr +="&field"; + postDataStr += "&field"; postDataStr += idx; postDataStr += "="; postDataStr += String(UserVar[varIndex - 1]); - postDataStr +="&field"; - postDataStr += idx+1; + postDataStr += "&field"; + postDataStr += idx + 1; postDataStr += "="; postDataStr += String(UserVar[varIndex]); break; @@ -522,16 +611,16 @@ boolean ThingsSpeak_sendData(byte sensorType, int idx, byte varIndex) } postDataStr += "\r\n\r\n"; - String postStr = F("POST /update HTTP/1.1\n"); - postStr += F("Host: api.thingspeak.com\n"); - postStr += F("Connection: close\n"); + String postStr = F("POST /update HTTP/1.1\n"); + postStr += F("Host: api.thingspeak.com\n"); + postStr += F("Connection: close\n"); postStr += F("X-THINGSPEAKAPIKEY: "); postStr += Settings.ControllerPassword; postStr += "\n"; - postStr += F("Content-Type: application/x-www-form-urlencoded\n"); - postStr += F("Content-Length: "); - postStr += postDataStr.length(); - postStr += F("\n\n"); + postStr += F("Content-Type: application/x-www-form-urlencoded\n"); + postStr += F("Content-Length: "); + postStr += postDataStr.length(); + postStr += F("\n\n"); postStr += postDataStr; if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE) @@ -547,28 +636,28 @@ boolean ThingsSpeak_sendData(byte sensorType, int idx, byte varIndex) // Read all the lines of the reply from server and print them to Serial while (client.available()) { if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE) - { - sprintf_P(log,PSTR("C1 WS %u FM %u"),WiFi.status(),FreeMem()); - Serial.println(log); - } + { + sprintf_P(log, PSTR("C1 WS %u FM %u"), WiFi.status(), FreeMem()); + Serial.println(log); + } String line = client.readStringUntil('\n'); if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE) - { - sprintf_P(log,PSTR("C2 WS %u FM %u"),WiFi.status(),FreeMem()); - Serial.println(log); - } - line.toCharArray(log,79); - addLog(LOG_LEVEL_DEBUG_MORE,log); + { + sprintf_P(log, PSTR("C2 WS %u FM %u"), WiFi.status(), FreeMem()); + Serial.println(log); + } + line.toCharArray(log, 80); + addLog(LOG_LEVEL_DEBUG_MORE, log); if (line.substring(0, 15) == "HTTP/1.1 200 OK") { - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : Succes!"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : Succes!"); if (printToWeb) printWebString += F("Success
"); success = true; } delay(1); } - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : closing connection"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : closing connection"); if (printToWeb) printWebString += F("closing connection
"); @@ -591,7 +680,7 @@ boolean nodeVariableCopy(byte var, byte unit) if (Nodes[unit].ip[0] == 0) { strcpy(log, "Remote Node unknown"); - addLog(LOG_LEVEL_DEBUG,log); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -603,7 +692,7 @@ boolean nodeVariableCopy(byte var, byte unit) sprintf_P(host, PSTR("%u.%u.%u.%u"), Nodes[unit].ip[0], Nodes[unit].ip[1], Nodes[unit].ip[2], Nodes[unit].ip[3]); sprintf_P(log, PSTR("%s%s"), "connecting to ", host); - addLog(LOG_LEVEL_DEBUG,log); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -614,7 +703,7 @@ boolean nodeVariableCopy(byte var, byte unit) if (!client.connect(host, 80)) { connectionFailures++; - addLog(LOG_LEVEL_ERROR,(char*)"HTTP : connection failed"); + addLog(LOG_LEVEL_ERROR, (char*)"HTTP : connection failed"); if (printToWeb) printWebString += F("connection failed
"); return false; @@ -628,8 +717,8 @@ boolean nodeVariableCopy(byte var, byte unit) url += ","; url += value; - url.toCharArray(log, 79); - addLog(LOG_LEVEL_DEBUG,log); + url.toCharArray(log, 80); + addLog(LOG_LEVEL_DEBUG, log); if (printToWeb) { printWebString += log; @@ -650,13 +739,13 @@ boolean nodeVariableCopy(byte var, byte unit) String line = client.readStringUntil('\n'); if (line.substring(0, 15) == "HTTP/1.1 200 OK") { - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : Succes!"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : Succes!"); if (printToWeb) printWebString += F("Success
"); success = true; } } - addLog(LOG_LEVEL_DEBUG,(char*)"HTTP : closing connection"); + addLog(LOG_LEVEL_DEBUG, (char*)"HTTP : closing connection"); if (printToWeb) printWebString += F("closing connection
"); @@ -684,7 +773,7 @@ void checkUDP() if (packetBuffer[0] != 255) { packetBuffer[len] = 0; - addLog(LOG_LEVEL_DEBUG,packetBuffer); + addLog(LOG_LEVEL_DEBUG, packetBuffer); #ifdef ESP_CONNEXIO ExecuteLine(packetBuffer, VALUE_SOURCE_SERIAL); #endif @@ -719,8 +808,8 @@ void checkUDP() char ipaddress[16]; sprintf_P(ipaddress, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]); char log[80]; - sprintf_P(log, PSTR("UDP : %s,%s,%u"), macaddress,ipaddress,unit); - addLog(LOG_LEVEL_DEBUG,log); + sprintf_P(log, PSTR("UDP : %s,%s,%u"), macaddress, ipaddress, unit); + addLog(LOG_LEVEL_DEBUG, log); } } } @@ -745,7 +834,7 @@ void sendSysInfoUDP(byte repeats) { if (Settings.UDPPort == 0) return; - + // 1 byte 'binary token 255' // 1 byte id // 6 byte mac @@ -754,7 +843,7 @@ void sendSysInfoUDP(byte repeats) // ??? build // ?? // send my info to the world... - addLog(LOG_LEVEL_DEBUG,(char*)"UDP : Send Sysinfo message"); + addLog(LOG_LEVEL_DEBUG, (char*)"UDP : Send Sysinfo message"); for (byte counter = 0; counter < repeats; counter++) { uint8_t mac[] = {0, 0, 0, 0, 0, 0}; diff --git a/Devices.ino b/Devices.ino index b2c61cc3a..568d24b65 100644 --- a/Devices.ino +++ b/Devices.ino @@ -1,32 +1,100 @@ void Device_Init() { - Device[1].Number = DEVICE_DS18B20; - strcpy(Device[1].Name,"Temperature DS18b20"); - Device[1].Type = DEVICE_TYPE_SINGLE; - Device[2].Number = DEVICE_DHT11; - strcpy(Device[2].Name,"Temp + Hum DHT 11"); - Device[2].Type = DEVICE_TYPE_SINGLE; - Device[3].Number = DEVICE_DHT22; - strcpy(Device[3].Name,"Temp + Hum DHT 22"); - Device[3].Type = DEVICE_TYPE_SINGLE; - Device[4].Number = DEVICE_BMP085; - strcpy(Device[4].Name,"Temp + Baro BMP085"); - Device[4].Type = DEVICE_TYPE_I2C; - Device[5].Number = DEVICE_BH1750; - strcpy(Device[5].Name,"LUX BH1750"); - Device[5].Type = DEVICE_TYPE_I2C; - Device[6].Number = DEVICE_ANALOG; - strcpy(Device[6].Name,"Analog input"); - Device[6].Type = DEVICE_TYPE_ANALOG; - Device[7].Number = DEVICE_RFID; - strcpy(Device[7].Name,"RFID Reader"); - Device[7].Type = DEVICE_TYPE_DUAL; - Device[8].Number = DEVICE_PULSE; - strcpy(Device[8].Name,"Pulse Counter"); - Device[8].Type = DEVICE_TYPE_SINGLE; - Device[9].Number = DEVICE_SWITCH; - strcpy(Device[9].Name,"Switch input"); - Device[9].Type = DEVICE_TYPE_SINGLE; + byte x=0; + + Device[++x].Number = DEVICE_DS18B20; + strcpy(Device[x].Name,"Temperature DS18b20"); + Device[x].Type = DEVICE_TYPE_SINGLE; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Temperature"); + + Device[++x].Number = DEVICE_DHT11; + strcpy(Device[x].Name,"Temp + Hum DHT 11"); + Device[x].Type = DEVICE_TYPE_SINGLE; + Device[x].VType = 2; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Temperature"); + strcpy(Device[x].ValueNames[1],"Humidity"); + + Device[++x].Number = DEVICE_DHT22; + strcpy(Device[x].Name,"Temp + Hum DHT 22"); + Device[x].Type = DEVICE_TYPE_SINGLE; + Device[x].VType = 2; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Temperature"); + strcpy(Device[x].ValueNames[1],"Humidity"); + + Device[++x].Number = DEVICE_BMP085; + strcpy(Device[x].Name,"Temp + Baro BMP085"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 3; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Temperature"); + strcpy(Device[x].ValueNames[1],"Pressure"); + + Device[++x].Number = DEVICE_BH1750; + strcpy(Device[x].Name,"LUX BH1750"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"LUX"); + + Device[++x].Number = DEVICE_ANALOG; + strcpy(Device[x].Name,"Analog input"); + Device[x].Type = DEVICE_TYPE_ANALOG; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Analog"); + + Device[++x].Number = DEVICE_RFID; + strcpy(Device[x].Name,"RFID Reader"); + Device[x].Type = DEVICE_TYPE_DUAL; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"RFID"); + + Device[++x].Number = DEVICE_PULSE; + strcpy(Device[x].Name,"Pulse Counter"); + Device[x].Type = DEVICE_TYPE_SINGLE; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Count"); + + Device[++x].Number = DEVICE_SWITCH; + strcpy(Device[x].Name,"Switch input"); + Device[x].Type = DEVICE_TYPE_SINGLE; + Device[x].VType = 1; + Device[x].Ports = 0; + strcpy(Device[x].ValueNames[0],"Switch"); + + Device[++x].Number = DEVICE_PCF8591; + strcpy(Device[x].Name,"PCF8591 Analog input"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 1; + Device[x].Ports = 4; + strcpy(Device[x].ValueNames[0],"Analog"); + + Device[++x].Number = DEVICE_MCP23017; + strcpy(Device[x].Name,"MCP23017 input"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 1; + Device[x].Ports = 16; + strcpy(Device[x].ValueNames[0],"Switch"); + + Device[++x].Number = DEVICE_PRO_MINI_ANALOG; + strcpy(Device[x].Name,"ProMini Extender Analog"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 1; + Device[x].Ports = 6; + strcpy(Device[x].ValueNames[0],"Analog"); + + Device[++x].Number = DEVICE_PRO_MINI_DIGITAL; + strcpy(Device[x].Name,"ProMini Extender Digital"); + Device[x].Type = DEVICE_TYPE_I2C; + Device[x].VType = 1; + Device[x].Ports = 14; + strcpy(Device[x].ValueNames[0],"Switch"); for (byte x=0; x < TASKS_MAX; x++) { @@ -50,9 +118,17 @@ void Device_Init() pulseinit(Settings.TaskDevicePin1[x], x); break; case DEVICE_SWITCH: - Serial.print(F("INIT : InputPullup ")); + if (Settings.TaskDevicePin1PullUp[x]) + { + Serial.print(F("INIT : InputPullup ")); + pinMode(Settings.TaskDevicePin1[x], INPUT_PULLUP); + } + else + { + Serial.print(F("INIT : Input ")); + pinMode(Settings.TaskDevicePin1[x], INPUT); + } Serial.println(Settings.TaskDevicePin1[x]); - pinMode(Settings.TaskDevicePin1[x], INPUT_PULLUP); break; } } @@ -136,6 +212,33 @@ void pulseinit(byte Par1, byte Index) } } +/*********************************************************************************************\ + * Extender (Arduino Mini Pro connected through I2C) + * The Mini Pro needs a small sketch to support this +\*********************************************************************************************/ +int extender(byte Cmd, byte Port, int Value) +{ + uint8_t address = 0x7f; + Wire.beginTransmission(address); + Wire.write(Cmd); + Wire.write(Port); + Wire.write(Value & 0xff); + Wire.write((Value >> 8)); + Wire.endTransmission(); + if (Cmd == 2 || Cmd == 4) + { + delay(1); // remote unit needs some time to do the adc stuff + Wire.requestFrom(address, (uint8_t)0x1); + if (Wire.available()) + { + int portvalue = Wire.read(); + //portvalue += Wire.read()*256; + return portvalue; + } + } + return -1; +} + /*********************************************************************************************\ * Analog port \*********************************************************************************************/ @@ -259,13 +362,49 @@ boolean pcf8591(byte Par1, byte Par2) Wire.read(); // Read older value first (stored in chip) UserVar[Par2 - 1] = (float)Wire.read(); // now read actual value and store into Nodo var Serial.print("PCF : Analog Value : "); - Serial.println(UserVar[Par1 - 1]); + Serial.println(UserVar[Par2 - 1]); success = true; } return success; } + /*********************************************************************************************\ - * MCP23017 + * MCP23017 INPUT +\*********************************************************************************************/ +boolean mcp23017Read(byte Par1, byte Par2) +{ + Serial.println("MCP23017 Read"); + boolean success = false; + byte portvalue = 0; + byte unit = (Par1 - 1) / 16; + byte port = Par1 - (unit * 16); + uint8_t address = 0x20 + unit; + byte IOBankConfigReg = 0; + byte IOBankValueReg = 0x12; + if (port > 8) + { + port = port - 8; + IOBankConfigReg++; + IOBankValueReg++; + } + // get the current pin status + Wire.beginTransmission(address); + Wire.write(IOBankValueReg); // IO data register + Wire.endTransmission(); + Wire.requestFrom(address, (uint8_t)0x1); + if (Wire.available()) + { + portvalue = ((Wire.read() & _BV(port-1)) >> (port-1)); + UserVar[Par2 - 1] = (float)portvalue; + Serial.print("MCP : Input Value : "); + Serial.println(UserVar[Par2 - 1]); + success = true; + } + return success; +} + +/*********************************************************************************************\ + * MCP23017 OUTPUT \*********************************************************************************************/ boolean mcp23017(byte Par1, byte Par2) { @@ -321,6 +460,7 @@ boolean mcp23017(byte Par1, byte Par2) } } + /*********************************************************************************************\ * DALLAS \*********************************************************************************************/ diff --git a/ESPEasy.ino b/ESPEasy.ino index 1fbdfb8e9..89baa6468 100644 --- a/ESPEasy.ino +++ b/ESPEasy.ino @@ -41,10 +41,12 @@ // RFID Wiegand-26 reader // MCP23017 I2C IO Expanders // Analog input (ESP-7/12 only) +// PCF8591 4 port Analog to Digital converter (I2C) // LCD I2C display 4x20 chars // Pulse counters // Simple switch inputs // Direct GPIO output control to drive relais, mosfets, etc +// Arduino Pro Mini with IO extender sketch, connected through I2C // ******************************************************************************** // User specific configuration @@ -65,6 +67,8 @@ // 1 = Domoticz HTTP // 2 = Domoticz MQTT // 3 = Nodo Telnet + // 4 = ThingSpeak + // 5 = OpenHAB MQTT #define UNIT 0 // ******************************************************************************** @@ -77,37 +81,48 @@ // 3 = temp + hum + baro (BMP085) // 10 = switch -#define ESP_PROJECT_PID 2015050101L +#define ESP_PROJECT_PID 2015050101L #define ESP_EASY -#define VERSION 4 -#define BUILD 15 +#define VERSION 5 +#define BUILD 16 #define REBOOT_ON_MAX_CONNECTION_FAILURES 30 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_INFO 2 -#define LOG_LEVEL_DEBUG 3 -#define LOG_LEVEL_DEBUG_MORE 4 +#define PROTOCOL_DOMOTICZ_HTTP 1 +#define PROTOCOL_DOMOTICZ_MQTT 2 +#define PROTOCOL_NODO_TELNET 3 +#define PROTOCOL_THINGSPEAK 4 +#define PROTOCOL_OPENHAB_MQTT 5 -#define CMD_REBOOT 89 -#define CMD_WIFI_DISCONNECT 135 +#define LOG_LEVEL_ERROR 1 +#define LOG_LEVEL_INFO 2 +#define LOG_LEVEL_DEBUG 3 +#define LOG_LEVEL_DEBUG_MORE 4 -#define DEVICE_DS18B20 1 -#define DEVICE_DHT11 2 -#define DEVICE_DHT22 3 -#define DEVICE_BMP085 4 -#define DEVICE_BH1750 5 -#define DEVICE_ANALOG 6 -#define DEVICE_RFID 7 -#define DEVICE_PULSE 8 -#define DEVICE_SWITCH 9 +#define CMD_REBOOT 89 +#define CMD_WIFI_DISCONNECT 135 -#define DEVICES_MAX 10 -#define TASKS_MAX 8 +#define DEVICE_DS18B20 1 +#define DEVICE_DHT11 2 +#define DEVICE_DHT22 3 +#define DEVICE_BMP085 4 +#define DEVICE_BH1750 5 +#define DEVICE_ANALOG 6 +#define DEVICE_RFID 7 +#define DEVICE_PULSE 8 +#define DEVICE_SWITCH 9 +#define DEVICE_PCF8591 10 +#define DEVICE_MCP23017 11 +#define DEVICE_PRO_MINI_ANALOG 12 +#define DEVICE_PRO_MINI_DIGITAL 13 -#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin -#define DEVICE_TYPE_I2C 2 // connected through I2C -#define DEVICE_TYPE_ANALOG 3 // tout pin -#define DEVICE_TYPE_DUAL 4 // connected through 2 datapins +#define DEVICES_MAX 14 +#define TASKS_MAX 8 +#define VARS_PER_TASK 2 + +#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin +#define DEVICE_TYPE_I2C 2 // connected through I2C +#define DEVICE_TYPE_ANALOG 3 // tout pin +#define DEVICE_TYPE_DUAL 4 // connected through 2 datapins #include #include @@ -160,7 +175,7 @@ struct SettingsStruct int8_t _Pin_wired_out_2; byte Syslog_IP[4]; unsigned int UDPPort; - unsigned int Switch1; + unsigned int _Switch1; byte Protocol; byte IP[4]; byte Gateway[4]; @@ -179,6 +194,10 @@ struct SettingsStruct unsigned int TaskDeviceID[TASKS_MAX]; int8_t TaskDevicePin1[TASKS_MAX]; int8_t TaskDevicePin2[TASKS_MAX]; + char TaskDeviceName[TASKS_MAX][26]; + byte TaskDevicePort[TASKS_MAX]; + char TaskDeviceFormula[TASKS_MAX][VARS_PER_TASK][26]; + boolean TaskDevicePin1PullUp[TASKS_MAX]; } Settings; struct LogStruct @@ -193,12 +212,15 @@ struct DeviceStruct byte Number; char Name[26]; byte Type; + byte VType; + byte Ports; + char ValueNames[2][26]; } Device[DEVICES_MAX]; boolean printToWeb = false; String printWebString = ""; -float UserVar[2 * TASKS_MAX]; +float UserVar[VARS_PER_TASK * TASKS_MAX]; unsigned long pulseCounter[TASKS_MAX]; unsigned long pulseTotalCounter[TASKS_MAX]; byte switchstate[TASKS_MAX]; @@ -269,7 +291,7 @@ void setup() lcd.print("ESP Easy"); // Setup MQTT Client - if (Settings.Protocol == 2) + if ((Settings.Protocol == 2) || (Settings.Protocol == 5)) MQTTConnect(); sendSysInfoUDP(3); @@ -369,7 +391,7 @@ void inputCheck() { switchstate[x] = state; UserVar[(x*2+1) - 1] = state; - sendData(10, Settings.TaskDeviceID[x], x*2+1); + sendData(x, 10, Settings.TaskDeviceID[x], x*2+1); delay(100); } } @@ -386,7 +408,7 @@ void inputCheck() Serial.print("RFID : Tag : "); Serial.println(rfid_id); UserVar[(x*2+1) - 1] = rfid_id; - sendData(1, Settings.TaskDeviceID[x], x*2+1); + sendData(x, 1, Settings.TaskDeviceID[x], x*2+1); } } } @@ -399,46 +421,99 @@ void SensorSend() { if (Settings.TaskDeviceID[x] != 0) { + byte uvarNr=x * VARS_PER_TASK + 1; + boolean success = false; switch(Settings.TaskDeviceNumber[x]) { case DEVICE_DS18B20: - dallas(Settings.TaskDevicePin1[x], x*2+1); - sendData(1, Settings.TaskDeviceID[x], x*2+1); + dallas(Settings.TaskDevicePin1[x], uvarNr); + success = true; break; case DEVICE_DHT11: - dht(11, Settings.TaskDevicePin1[x], x*2+1); - if (!isnan(UserVar[(x*2+1) - 1]) && (UserVar[(x*2+2) - 1] > 0)) - sendData(2,Settings.TaskDeviceID[x], x*2+1); + dht(11, Settings.TaskDevicePin1[x], uvarNr); + if (!isnan(UserVar[uvarNr - 1]) && (UserVar[(uvarNr+1) - 1] > 0)) + success = true; break; case DEVICE_DHT22: - dht(22, Settings.TaskDevicePin1[x], x*2+1); - if (!isnan(UserVar[(x*2+1) - 1]) && (UserVar[(x*2+2) - 1] > 0)) - sendData(2, Settings.TaskDeviceID[x], x*2+1); + dht(22, Settings.TaskDevicePin1[x], uvarNr); + if (!isnan(UserVar[uvarNr - 1]) && (UserVar[(uvarNr+1) - 1] > 0)) + success = true; break; case DEVICE_BMP085: - bmp085(x*2+1); - if ((UserVar[(x*2+2) - 1] >= 300) && (UserVar[(x*2+2) - 1] <= 1100)) - sendData(3, Settings.TaskDeviceID[x], x*2+1); + bmp085(uvarNr); + if ((UserVar[(uvarNr+1) - 1] >= 300) && (UserVar[(uvarNr+1) - 1] <= 1100)) + success = true; break; case DEVICE_BH1750: - lux(x*2+1); // read BH1750 LUX sensor and store to var 6 - sendData(1, Settings.TaskDeviceID[x], x*2+1); + lux(uvarNr); + success = true; break; case DEVICE_ANALOG: - analog(x*2+1); // read ADC and store to var 7 - sendData(1, Settings.TaskDeviceID[x], x*2+1); + analog(uvarNr); + success = true; break; case DEVICE_PULSE: - UserVar[(x*2+1) - 1] = pulseCounter[x]; - sendData(1, Settings.TaskDeviceID[x], x*2+1); + UserVar[uvarNr - 1] = pulseCounter[x]; + success = true; pulseCounter[x] = 0; break; + + case DEVICE_PCF8591: + pcf8591(Settings.TaskDevicePort[x], uvarNr); + success = true; + break; + + case DEVICE_MCP23017: + mcp23017Read(Settings.TaskDevicePort[x], uvarNr); + success = true; + break; + + case DEVICE_PRO_MINI_ANALOG: + UserVar[uvarNr - 1] = extender(4,Settings.TaskDevicePort[x],0); + success = true; + break; + + case DEVICE_PRO_MINI_DIGITAL: + UserVar[uvarNr - 1] = extender(2,Settings.TaskDevicePort[x],0); + success = true; + break; + } + if (success) + { + for (byte varNr=0; varNr < VARS_PER_TASK; varNr++) + { + if (Settings.TaskDeviceFormula[x][varNr][0] !=0) + { + String formula = Settings.TaskDeviceFormula[x][varNr]; + float value = UserVar[uvarNr + varNr - 1]; + float result = 0; + String svalue = String(value); + formula.replace("%value%",svalue); + Serial.print("CALC : "); + Serial.print(formula); + Serial.print(" = "); + char TmpStr[26]; + formula.toCharArray(TmpStr,25); + byte error = Calculate(TmpStr,&result); + if(error == 0) + { + Serial.println(result); + UserVar[uvarNr + varNr - 1] = result; + } + else + { + Serial.print("? error="); + Serial.println(error); + } + } + } + sendData(x, Device[Settings.TaskDeviceNumber[x]].VType, Settings.TaskDeviceID[x], uvarNr); } } } diff --git a/Misc.ino b/Misc.ino index 64315fb63..8231dc96d 100644 --- a/Misc.ino +++ b/Misc.ino @@ -47,4 +47,265 @@ void delayedReboot(int rebootDelay) delay(1000); } ESP.reset(); - } + } + +//################### Calculate ################################# +#define CALCULATE_OK 0 +#define CALCULATE_ERROR_STACK_OVERFLOW 1 +#define CALCULATE_ERROR_BAD_OPERATOR 2 +#define CALCULATE_ERROR_PARENTHESES_MISMATCHED 3 +#define CALCULATE_ERROR_UNKNOWN_TOKEN 4 +#define STACK_SIZE 10 // was 50 +#define TOKEN_MAX 20 + +float globalstack[STACK_SIZE]; +float *sp = globalstack-1; +float *sp_max = &globalstack[STACK_SIZE-1]; + +#define is_operator(c) (c == '+' || c == '-' || c == '*' || c == '/' ) + +int push(float value) +{ + if(sp != sp_max) // Full + { + *(++sp) = value; + return 0; + } + else + return CALCULATE_ERROR_STACK_OVERFLOW; +} + +float pop() +{ + if(sp != (globalstack-1)) // empty + return *(sp--); +} + +float apply_operator(char op, float first, float second) + { + switch(op) + { + case '+': + return first + second; + case '-': + return first - second; + case '*': + return first * second; + case '/': + return first / second; + return 0; + } +} + +char *next_token(char *linep) +{ + while(isspace(*(linep++))); + while(*linep && !isspace(*(linep++))); + return linep; +} + +int RPNCalculate(char* token) +{ + if(token[0]==0) + return 0; // geen moeite doen voor een lege string + + if(is_operator(token[0])) + { + float second = pop(); + float first = pop(); + + if(push(apply_operator(token[0], first, second))) + return CALCULATE_ERROR_STACK_OVERFLOW; + } + else // Als er nog een is, dan deze ophalen + if(push(atof(token))) // is het een waarde, dan op de stack plaatsen + return CALCULATE_ERROR_STACK_OVERFLOW; + + return 0; +} + +// operators +// precedence operators associativity +// 3 ! right to left +// 2 * / % left to right +// 1 + - ^ left to right +int op_preced(const char c) +{ + switch(c) + { + case '*': + case '/': + return 2; + case '+': + case '-': + return 1; + } + return 0; +} + +bool op_left_assoc(const char c) +{ + switch(c) + { + case '*': + case '/': + case '+': + case '-': + return true; // left to right + //case '!': return false; // right to left + } + return false; +} + +unsigned int op_arg_count(const char c) +{ + switch(c) + { + case '*': + case '/': + case '+': + case '-': + return 2; + //case '!': return 1; + } + return 0; +} + + +int Calculate(const char *input, float* result) +{ + const char *strpos = input, *strend = input + strlen(input); + char token[25]; + char c, *TokenPos = token; + char stack[32]; // operator stack + unsigned int sl = 0; // stack length + char sc; // used for record stack element + int error=0; + + //*sp=0; // bug, it stops calculating after 50 times + sp = globalstack-1; + + while(strpos < strend) + { + // read one token from the input stream + c = *strpos; + if(c != ' ') + { + // If the token is a number (identifier), then add it to the token queue. + if((c >= '0' && c <= '9') || c=='.') + { + *TokenPos = c; + ++TokenPos; + } + + // If the token is an operator, op1, then: + else if(is_operator(c)) + { + *(TokenPos)=0; + error=RPNCalculate(token); + TokenPos=token; + if(error)return error; + while(sl > 0) + { + sc = stack[sl - 1]; + // While there is an operator token, op2, at the top of the stack + // op1 is left-associative and its precedence is less than or equal to that of op2, + // or op1 has precedence less than that of op2, + // The differing operator priority decides pop / push + // If 2 operators have equal priority then associativity decides. + if(is_operator(sc) && ((op_left_assoc(c) && (op_preced(c) <= op_preced(sc))) || (op_preced(c) < op_preced(sc)))) + { + // Pop op2 off the stack, onto the token queue; + *TokenPos = sc; + ++TokenPos; + *(TokenPos)=0; + error=RPNCalculate(token); + TokenPos=token; + if(error)return error; + sl--; + } + else + break; + } + // push op1 onto the stack. + stack[sl] = c; + ++sl; + } + // If the token is a left parenthesis, then push it onto the stack. + else if(c == '(') + { + stack[sl] = c; + ++sl; + } + // If the token is a right parenthesis: + else if(c == ')') + { + bool pe = false; + // Until the token at the top of the stack is a left parenthesis, + // pop operators off the stack onto the token queue + while(sl > 0) + { + *(TokenPos)=0; + error=RPNCalculate(token); + TokenPos=token; + if(error)return error; + sc = stack[sl - 1]; + if(sc == '(') + { + pe = true; + break; + } + else + { + *TokenPos = sc; + ++TokenPos; + sl--; + } + } + // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. + if(!pe) + return CALCULATE_ERROR_PARENTHESES_MISMATCHED; + + // Pop the left parenthesis from the stack, but not onto the token queue. + sl--; + + // If the token at the top of the stack is a function token, pop it onto the token queue. + if(sl > 0) + sc = stack[sl - 1]; + + } + else + return CALCULATE_ERROR_UNKNOWN_TOKEN; + } + ++strpos; + } + // When there are no more tokens to read: + // While there are still operator tokens in the stack: + while(sl > 0) + { + sc = stack[sl - 1]; + if(sc == '(' || sc == ')') + return CALCULATE_ERROR_PARENTHESES_MISMATCHED; + + *(TokenPos)=0; + error=RPNCalculate(token); + TokenPos=token; + if(error)return error; + *TokenPos = sc; + ++TokenPos; + --sl; + } + + *(TokenPos)=0; + error=RPNCalculate(token); + TokenPos=token; + if(error) + { + *result=0; + return error; + } + *result=*sp; + return CALCULATE_OK; +} +//################### Einde Calculate ################################# + diff --git a/Serial.ino b/Serial.ino index 037c81320..992631a10 100644 --- a/Serial.ino +++ b/Serial.ino @@ -20,6 +20,21 @@ void ExecuteCommand(char *Line) // commands to execute io tasks // **************************************** + if (strcasecmp(Command, "calc") == 0) + { + float result; + if (GetArgv(Line, TmpStr1, 2)) + { + String formula = TmpStr1; + float value = 123.45; + String svalue = String(value); + formula.replace("%value%",svalue); + formula.toCharArray(TmpStr1,25); + if(Calculate(TmpStr1,&result)==CALCULATE_OK) + Serial.println(result); + } + } + if (strcasecmp(Command, "GPIO") == 0) { if (Par1 >= 0 && Par1 <= 16) @@ -54,12 +69,38 @@ void ExecuteCommand(char *Line) } } + if (strcasecmp(Command, "ExtRead") == 0) + { + uint8_t address = 0x7f; + Wire.requestFrom(address, (uint8_t)Par1); + if (Wire.available()) + { + for (byte x=0; x"); + str += Settings.Name; + str += F(""); str += F("