diff --git a/IRController/IRController.ino b/IRController/IRController.ino index 1834fce..3f2dbf9 100644 --- a/IRController/IRController.ino +++ b/IRController/IRController.ino @@ -13,6 +13,7 @@ #include #include #include "sha256.h" +#include #include // For LED status #include @@ -45,6 +46,12 @@ char passcode[20] = ""; char host_name[20] = ""; char port_str[6] = "80"; char user_id[60] = ""; +char mqtt_host[20] = ""; +char mqtt_user[20] = ""; +char mqtt_pass[20] = ""; +const char* mqtt_topic_rc = "/received"; +const char* mqtt_topic_snd = "/sent"; +const char* mqtt_topic_cmd = "/cmd"; const char* fingerprint = "8D 83 C3 5F 0A 09 84 AE B0 64 39 23 8F 05 9E 4D 5E 08 60 06"; char static_ip[16] = "10.0.1.10"; @@ -57,6 +64,10 @@ JsonObject& deviceState = jsonBuffer.createObject(); ESP8266WebServer *server = NULL; Ticker ticker; +WiFiClient espClient; +PubSubClient mqtt_client(espClient); +long mqtt_lastReconnectAttempt = 0; + bool shouldSaveConfig = false; // Flag for saving data bool holdReceive = false; // Flag to prevent IR receiving while transmitting @@ -133,10 +144,10 @@ void resetReceive() { // bool validUID(char* user_id) { if (!String(user_id).startsWith("amzn1.account.")) { - Serial.println("Warning, user_id appears to be in the wrong format, security check will most likely fail. Should start with amzn1.account.***"); - return false; - } - return true; + Serial.println("Warning, user_id appears to be in the wrong format, security check will most likely fail. Should start with amzn1.account.***"); + return false; + } + return true; } @@ -173,54 +184,54 @@ String epochToString(time_t timenow) { // Valid command request using HMAC // bool validateHMAC(String epid, String mid, String timestamp, String signature) { - userIDError = false; - authError = false; - ntpError = false; - timeAuthError = 0; - - userIDError = !(validUID(user_id)); + userIDError = false; + authError = false; + ntpError = false; + timeAuthError = 0; - time_t timethen = timestamp.toInt(); - time_t timenow = timeClient.getUnixTime() - timeOffset; - time_t timediff = abs(timethen - timenow); - if (timediff > 30) { - Serial.println("Failed security check, signature is too old"); - Serial.print("Server: "); - Serial.println(timethen); - Serial.print("Local: "); - Serial.println(timenow); - Serial.print("MID: "); - Serial.println(mid); - timeAuthError = timediff; - validEPOCH(timenow); - return false; - } + userIDError = !(validUID(user_id)); - uint8_t *hash; - String key = String(user_id); - Sha256.initHmac((uint8_t*)key.c_str(), key.length()); // key, and length of key in bytes - Sha256.print(epid); - Sha256.print(mid); - Sha256.print(timestamp); - hash = Sha256.resultHmac(); - String computedSignature = bin2hex(hash, HASH_LENGTH); - - if (computedSignature != signature) { - Serial.println("Failed security check, signatures do not match"); - Serial.print("1: "); - Serial.println(signature); - Serial.print("2: "); - Serial.println(computedSignature); - Serial.print("MID: "); - Serial.println(mid); - authError = true; - return false; - } + time_t timethen = timestamp.toInt(); + time_t timenow = timeClient.getUnixTime() - timeOffset; + time_t timediff = abs(timethen - timenow); + if (timediff > 30) { + Serial.println("Failed security check, signature is too old"); + Serial.print("Server: "); + Serial.println(timethen); + Serial.print("Local: "); + Serial.println(timenow); + Serial.print("MID: "); + Serial.println(mid); + timeAuthError = timediff; + validEPOCH(timenow); + return false; + } - Serial.println("Passed security check"); + uint8_t *hash; + String key = String(user_id); + Sha256.initHmac((uint8_t*)key.c_str(), key.length()); // key, and length of key in bytes + Sha256.print(epid); + Sha256.print(mid); + Sha256.print(timestamp); + hash = Sha256.resultHmac(); + String computedSignature = bin2hex(hash, HASH_LENGTH); + + if (computedSignature != signature) { + Serial.println("Failed security check, signatures do not match"); + Serial.print("1: "); + Serial.println(signature); + Serial.print("2: "); + Serial.println(computedSignature); Serial.print("MID: "); Serial.println(mid); - return true; + authError = true; + return false; + } + + Serial.println("Passed security check"); + Serial.print("MID: "); + Serial.println(mid); + return true; } @@ -393,6 +404,9 @@ bool setupWifi(bool resetConf) { if (json.containsKey("hostname")) strncpy(host_name, json["hostname"], 20); if (json.containsKey("passcode")) strncpy(passcode, json["passcode"], 20); + if (json.containsKey("mqtt_host")) strncpy(mqtt_host, json["mqtt_host"], 20); + if (json.containsKey("mqtt_user")) strncpy(mqtt_user, json["mqtt_user"], 20); + if (json.containsKey("mqtt_pass")) strncpy(mqtt_pass, json["mqtt_pass"], 20); if (json.containsKey("user_id")) strncpy(user_id, json["user_id"], 60); if (json.containsKey("port_str")) { strncpy(port_str, json["port_str"], 6); @@ -418,6 +432,12 @@ bool setupWifi(bool resetConf) { wifiManager.addParameter(&custom_port); WiFiManagerParameter custom_userid("user_id", "Enter your Amazon user_id", user_id, 60); wifiManager.addParameter(&custom_userid); + WiFiManagerParameter custom_mqtt_host("mqtt_host", "Choose a MQTT host", mqtt_host, 20); + wifiManager.addParameter(&custom_mqtt_host); + WiFiManagerParameter custom_mqtt_user("mqtt_user", "Choose a MQTT user", mqtt_user, 20); + wifiManager.addParameter(&custom_mqtt_user); + WiFiManagerParameter custom_mqtt_pass("mqtt_pass", "Choose a MQTT password", mqtt_pass, 20); + wifiManager.addParameter(&custom_mqtt_pass); IPAddress sip, sgw, ssn; sip.fromString(static_ip); @@ -441,6 +461,9 @@ bool setupWifi(bool resetConf) { strncpy(passcode, custom_passcode.getValue(), 20); strncpy(port_str, custom_port.getValue(), 6); strncpy(user_id, custom_userid.getValue(), 60); + strncpy(mqtt_host, custom_mqtt_host.getValue(), 20); + strncpy(mqtt_user, custom_mqtt_user.getValue(), 20); + strncpy(mqtt_pass, custom_mqtt_pass.getValue(), 20); port = atoi(port_str); if (server != NULL) { @@ -448,6 +471,21 @@ bool setupWifi(bool resetConf) { } server = new ESP8266WebServer(port); + if (String(mqtt_host).length() > 0) { + IPAddress mqtt_ip; + if (mqtt_ip.fromString(mqtt_host)) { + Serial.println("MQTT IP " + String(mqtt_host)); + mqtt_client.setServer(mqtt_ip, 1883); + } else { + Serial.println("MQTT host: '" + String(mqtt_host) + "'"); + mqtt_client.setServer(mqtt_host, 1883); + } + mqtt_client.setCallback(mqtt_calback); + } else { + Serial.println("MQTT host not set"); + } + + // Reset device if lost wifi Connection WiFi.onStationModeDisconnected(&lostWifiCallback); @@ -462,6 +500,9 @@ bool setupWifi(bool resetConf) { json["passcode"] = passcode; json["port_str"] = port_str; json["user_id"] = user_id; + json["mqtt_host"] = mqtt_host; + json["mqtt_user"] = mqtt_user; + json["mqtt_pass"] = mqtt_pass; json["ip"] = WiFi.localIP().toString(); json["gw"] = WiFi.gatewayIP().toString(); json["sn"] = WiFi.subnetMask().toString(); @@ -627,65 +668,7 @@ void setup() { server->send(200, "text/html", "Success, code sent"); } - String message = "Code sent"; - - for (int x = 0; x < root.size(); x++) { - String type = root[x]["type"]; - String ip = root[x]["ip"]; - int rdelay = root[x]["rdelay"]; - int pulse = root[x]["pulse"]; - int pdelay = root[x]["pdelay"]; - int repeat = root[x]["repeat"]; - int xout = root[x]["out"]; - if (xout == 0) { - xout = out; - } - int duty = root[x]["duty"]; - - if (pulse <= 0) pulse = 1; // Make sure pulse isn't 0 - if (repeat <= 0) repeat = 1; // Make sure repeat isn't 0 - if (pdelay <= 0) pdelay = 100; // Default pdelay - if (rdelay <= 0) rdelay = 1000; // Default rdelay - if (duty <= 0) duty = 50; // Default duty - - // Handle device state limitations on a per JSON object basis - String device = root[x]["device"]; - if (device != "") { - int state = root[x]["state"]; - if (deviceState.containsKey(device)) { - int currentState = deviceState[device]; - if (state == currentState) { - Serial.println("Not sending command to " + device + ", already in state " + state); - message = "Code sent. Some components of the code were held because device was already in appropriate state"; - continue; - } else { - Serial.println("Setting device " + device + " to state " + state); - deviceState[device] = state; - } - } else { - Serial.println("Setting device " + device + " to state " + state); - deviceState[device] = state; - } - } - - if (type == "delay") { - delay(rdelay); - } else if (type == "raw") { - JsonArray &raw = root[x]["data"]; // Array of unsigned int values for the raw signal - int khz = root[x]["khz"]; - if (khz <= 0) khz = 38; // Default to 38khz if not set - rawblast(raw, khz, rdelay, pulse, pdelay, repeat, pickIRsend(xout),duty); - } else if (type == "roku") { - String data = root[x]["data"]; - rokuCommand(ip, data, repeat, rdelay); - } else { - String data = root[x]["data"]; - String addressString = root[x]["address"]; - long address = strtoul(addressString.c_str(), 0, 0); - int len = root[x]["length"]; - irblast(type, data, len, rdelay, pulse, pdelay, repeat, address, pickIRsend(xout)); - } - } + String message = blastJson(root, out); if (!simple) { Serial.println("Sending home page"); @@ -840,6 +823,69 @@ void setup() { Serial.println("Ready to send and receive IR signals"); } +String blastJson(JsonArray& root, int out) { + String message = "Code sent"; + + for (int x = 0; x < root.size(); x++) { + String type = root[x]["type"]; + String ip = root[x]["ip"]; + int rdelay = root[x]["rdelay"]; + int pulse = root[x]["pulse"]; + int pdelay = root[x]["pdelay"]; + int repeat = root[x]["repeat"]; + int xout = root[x]["out"]; + if (xout == 0) { + xout = out; + } + int duty = root[x]["duty"]; + + if (pulse <= 0) pulse = 1; // Make sure pulse isn't 0 + if (repeat <= 0) repeat = 1; // Make sure repeat isn't 0 + if (pdelay <= 0) pdelay = 100; // Default pdelay + if (rdelay <= 0) rdelay = 1000; // Default rdelay + if (duty <= 0) duty = 50; // Default duty + + // Handle device state limitations on a per JSON object basis + String device = root[x]["device"]; + if (device != "") { + int state = root[x]["state"]; + if (deviceState.containsKey(device)) { + int currentState = deviceState[device]; + if (state == currentState) { + Serial.println("Not sending command to " + device + ", already in state " + state); + message = "Code sent. Some components of the code were held because device was already in appropriate state"; + continue; + } else { + Serial.println("Setting device " + device + " to state " + state); + deviceState[device] = state; + } + } else { + Serial.println("Setting device " + device + " to state " + state); + deviceState[device] = state; + } + } + + if (type == "delay") { + delay(rdelay); + } else if (type == "raw") { + JsonArray &raw = root[x]["data"]; // Array of unsigned int values for the raw signal + int khz = root[x]["khz"]; + if (khz <= 0) khz = 38; // Default to 38khz if not set + rawblast(raw, khz, rdelay, pulse, pdelay, repeat, pickIRsend(xout), duty); + } else if (type == "roku") { + String data = root[x]["data"]; + rokuCommand(ip, data, repeat, rdelay); + } else { + String data = root[x]["data"]; + String addressString = root[x]["address"]; + long address = strtoul(addressString.c_str(), 0, 0); + int len = root[x]["length"]; + irblast(type, data, len, rdelay, pulse, pdelay, repeat, address, pickIRsend(xout)); + } + } + return message; +} + //+============================================================================= // Send command to local roku // @@ -853,22 +899,24 @@ int rokuCommand(String ip, String data, int repeat, int rdelay) { http.begin(url); Serial.println(url); Serial.println("Sending roku command"); - + copyCode(last_send_4, last_send_5); copyCode(last_send_3, last_send_4); copyCode(last_send_2, last_send_3); copyCode(last_send, last_send_2); - + strncpy(last_send.data, data.c_str(), 40); last_send.bits = 1; strncpy(last_send.encoding, "roku", 14); strncpy(last_send.address, ip.c_str(), 20); last_send.timestamp = timeClient.getUnixTime(); last_send.valid = true; - + output = http.POST(""); http.end(); + mqtt_client.publish(("/" + String(host_name) + String(mqtt_topic_snd)).c_str(), ("[{'data':'" + String(last_send.data) + "','type':'" + String(last_send.encoding) + "','length':" + String(last_send.bits) + ",'address':'" + String(last_send.address) + "'}]").c_str(), true); + if (r + 1 < repeat) delay(rdelay); } return output; @@ -912,7 +960,7 @@ IRsend pickIRsend (int out) { //+============================================================================= // Display encoding type // -String encoding(decode_results *results) { +String encoding(decode_results * results) { String output; switch (results->decode_type) { default: @@ -942,7 +990,7 @@ String encoding(decode_results *results) { //+============================================================================= // Code to string // -void fullCode (decode_results *results) +void fullCode (decode_results * results) { Serial.print("One line: "); serialPrintUint64(results->value, 16); @@ -1002,20 +1050,20 @@ void sendFooter() { server->sendContent(" "); server->sendContent(" "); if (strlen(user_id) != 0) - server->sendContent("
Device secured with SHA256 authentication. Only commands sent and verified with Amazon Alexa and the IR Controller Skill will be processed
"); + server->sendContent("
Device secured with SHA256 authentication. Only commands sent and verified with Amazon Alexa and the IR Controller Skill will be processed
"); if (authError) - server->sendContent("
Error - last authentication failed because HMAC signatures did not match, see serial output for debugging details
"); + server->sendContent("
Error - last authentication failed because HMAC signatures did not match, see serial output for debugging details
"); if (timeAuthError > 0) - server->sendContent("
Error - last authentication failed because your timestamps are out of sync, see serial output for debugging details. Timediff: " + String(timeAuthError) + "
"); + server->sendContent("
Error - last authentication failed because your timestamps are out of sync, see serial output for debugging details. Timediff: " + String(timeAuthError) + "
"); if (externalIPError) - server->sendContent("
Error - unable to retrieve external IP address, this may be due to bad network settings. There is currently a bug with the latest versions of ESP8266 for Arduino, please use version 2.4.0 along with lwIP v1.4 Prebuilt to resolve this
"); + server->sendContent("
Error - unable to retrieve external IP address, this may be due to bad network settings. There is currently a bug with the latest versions of ESP8266 for Arduino, please use version 2.4.0 along with lwIP v1.4 Prebuilt to resolve this
"); time_t timenow = timeClient.getUnixTime() - timeOffset; if (!validEPOCH(timenow)) - server->sendContent("
Error - EPOCH time is inappropraitely low, likely connection to external time server has failed, check your network settings
"); + server->sendContent("
Error - EPOCH time is inappropraitely low, likely connection to external time server has failed, check your network settings
"); if (userIDError) - server->sendContent("
Error - your userID is in the wrong format and authentication will not work
"); + server->sendContent("
Error - your userID is in the wrong format and authentication will not work
"); if (ntpError) - server->sendContent("
Error - last attempt to connect to the NTP server failed, check NTP settings and networking settings
"); + server->sendContent("
Error - last attempt to connect to the NTP server failed, check NTP settings and networking settings
"); server->sendContent(" \n"); server->sendContent(" \n"); server->sendContent("\n"); @@ -1040,11 +1088,11 @@ void sendHomePage(String message, String header, int type) { void sendHomePage(String message, String header, int type, int httpcode) { sendHeader(httpcode); if (type == 1) - server->sendContent("
" + header + "! " + message + "
\n"); + server->sendContent("
" + header + "! " + message + "
\n"); if (type == 2) - server->sendContent("
" + header + "! " + message + "
\n"); + server->sendContent("
" + header + "! " + message + "
\n"); if (type == 3) - server->sendContent("
" + header + "! " + message + "
\n"); + server->sendContent("
" + header + "! " + message + "
\n"); server->sendContent("
\n"); server->sendContent("
\n"); server->sendContent("

Codes Transmitted

\n"); @@ -1052,17 +1100,17 @@ void sendHomePage(String message, String header, int type, int httpcode) { server->sendContent(" SentCommandTypeLengthAddress\n"); //Title server->sendContent(" \n"); if (last_send.valid) - server->sendContent(" " + epochToString(last_send.timestamp) + "" + String(last_send.data) + "" + String(last_send.encoding) + "" + String(last_send.bits) + "" + String(last_send.address) + "\n"); + server->sendContent(" " + epochToString(last_send.timestamp) + "" + String(last_send.data) + "" + String(last_send.encoding) + "" + String(last_send.bits) + "" + String(last_send.address) + "\n"); if (last_send_2.valid) - server->sendContent(" " + epochToString(last_send_2.timestamp) + "" + String(last_send_2.data) + "" + String(last_send_2.encoding) + "" + String(last_send_2.bits) + "" + String(last_send_2.address) + "\n"); + server->sendContent(" " + epochToString(last_send_2.timestamp) + "" + String(last_send_2.data) + "" + String(last_send_2.encoding) + "" + String(last_send_2.bits) + "" + String(last_send_2.address) + "\n"); if (last_send_3.valid) - server->sendContent(" " + epochToString(last_send_3.timestamp) + "" + String(last_send_3.data) + "" + String(last_send_3.encoding) + "" + String(last_send_3.bits) + "" + String(last_send_3.address) + "\n"); + server->sendContent(" " + epochToString(last_send_3.timestamp) + "" + String(last_send_3.data) + "" + String(last_send_3.encoding) + "" + String(last_send_3.bits) + "" + String(last_send_3.address) + "\n"); if (last_send_4.valid) - server->sendContent(" " + epochToString(last_send_4.timestamp) + "" + String(last_send_4.data) + "" + String(last_send_4.encoding) + "" + String(last_send_4.bits) + "" + String(last_send_4.address) + "\n"); + server->sendContent(" " + epochToString(last_send_4.timestamp) + "" + String(last_send_4.data) + "" + String(last_send_4.encoding) + "" + String(last_send_4.bits) + "" + String(last_send_4.address) + "\n"); if (last_send_5.valid) - server->sendContent(" " + epochToString(last_send_5.timestamp) + "" + String(last_send_5.data) + "" + String(last_send_5.encoding) + "" + String(last_send_5.bits) + "" + String(last_send_5.address) + "\n"); + server->sendContent(" " + epochToString(last_send_5.timestamp) + "" + String(last_send_5.data) + "" + String(last_send_5.encoding) + "" + String(last_send_5.bits) + "" + String(last_send_5.address) + "\n"); if (!last_send.valid && !last_send_2.valid && !last_send_3.valid && !last_send_4.valid && !last_send_5.valid) - server->sendContent(" No codes sent"); + server->sendContent(" No codes sent"); server->sendContent(" \n"); server->sendContent("
\n"); server->sendContent("
\n"); @@ -1072,17 +1120,17 @@ void sendHomePage(String message, String header, int type, int httpcode) { server->sendContent(" ReceivedCommandTypeLengthAddress\n"); //Title server->sendContent(" \n"); if (last_recv.valid) - server->sendContent(" " + epochToString(last_recv.timestamp) + "" + String(last_recv.data) + "" + String(last_recv.encoding) + "" + String(last_recv.bits) + "" + String(last_recv.address) + "\n"); + server->sendContent(" " + epochToString(last_recv.timestamp) + "" + String(last_recv.data) + "" + String(last_recv.encoding) + "" + String(last_recv.bits) + "" + String(last_recv.address) + "\n"); if (last_recv_2.valid) - server->sendContent(" " + epochToString(last_recv_2.timestamp) + "" + String(last_recv_2.data) + "" + String(last_recv_2.encoding) + "" + String(last_recv_2.bits) + "" + String(last_recv_2.address) + "\n"); + server->sendContent(" " + epochToString(last_recv_2.timestamp) + "" + String(last_recv_2.data) + "" + String(last_recv_2.encoding) + "" + String(last_recv_2.bits) + "" + String(last_recv_2.address) + "\n"); if (last_recv_3.valid) - server->sendContent(" " + epochToString(last_recv_3.timestamp) + "" + String(last_recv_3.data) + "" + String(last_recv_3.encoding) + "" + String(last_recv_3.bits) + "" + String(last_recv_3.address) + "\n"); + server->sendContent(" " + epochToString(last_recv_3.timestamp) + "" + String(last_recv_3.data) + "" + String(last_recv_3.encoding) + "" + String(last_recv_3.bits) + "" + String(last_recv_3.address) + "\n"); if (last_recv_4.valid) - server->sendContent(" " + epochToString(last_recv_4.timestamp) + "" + String(last_recv_4.data) + "" + String(last_recv_4.encoding) + "" + String(last_recv_4.bits) + "" + String(last_recv_4.address) + "\n"); + server->sendContent(" " + epochToString(last_recv_4.timestamp) + "" + String(last_recv_4.data) + "" + String(last_recv_4.encoding) + "" + String(last_recv_4.bits) + "" + String(last_recv_4.address) + "\n"); if (last_recv_5.valid) - server->sendContent(" " + epochToString(last_recv_5.timestamp) + "" + String(last_recv_5.data) + "" + String(last_recv_5.encoding) + "" + String(last_recv_5.bits) + "" + String(last_recv_5.address) + "\n"); + server->sendContent(" " + epochToString(last_recv_5.timestamp) + "" + String(last_recv_5.data) + "" + String(last_recv_5.encoding) + "" + String(last_recv_5.bits) + "" + String(last_recv_5.address) + "\n"); if (!last_recv.valid && !last_recv_2.valid && !last_recv_3.valid && !last_recv_4.valid && !last_recv_5.valid) - server->sendContent(" No codes received"); + server->sendContent(" No codes received"); server->sendContent(" \n"); server->sendContent("
\n"); server->sendContent("
\n"); @@ -1105,7 +1153,7 @@ void sendCodePage(Code selCode) { sendCodePage(selCode, 200); } -void sendCodePage(Code selCode, int httpcode){ +void sendCodePage(Code selCode, int httpcode) { sendHeader(httpcode); server->sendContent("
\n"); server->sendContent("
\n"); @@ -1134,50 +1182,50 @@ void sendCodePage(Code selCode, int httpcode){ server->sendContent("
Don't forget to add your passcode to the URLs below if you set one
\n"); server->sendContent("
\n"); if (String(selCode.encoding) == "UNKNOWN") { - server->sendContent("
\n"); - server->sendContent("
\n"); - server->sendContent("
    \n"); - server->sendContent("
  • Hostname JSON
  • \n"); - server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
  • \n"); - server->sendContent("
  • Local IP JSON
  • \n"); - server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
  • \n"); - server->sendContent("
  • External IP JSON
  • \n"); - server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
\n"); + server->sendContent("
\n"); + server->sendContent("
\n"); + server->sendContent("
    \n"); + server->sendContent("
  • Hostname JSON
  • \n"); + server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
  • \n"); + server->sendContent("
  • Local IP JSON
  • \n"); + server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
  • \n"); + server->sendContent("
  • External IP JSON
  • \n"); + server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':[" + String(selCode.raw) + "],'type':'raw','khz':38}]
\n"); } else if (String(selCode.encoding) == "PANASONIC") { - //} else if (strtoul(selCode.address, 0, 0) > 0) { - server->sendContent("
\n"); - server->sendContent("
\n"); - server->sendContent("
    \n"); - server->sendContent("
  • Hostname MSG
  • \n"); - server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
  • \n"); - server->sendContent("
  • Local IP MSG
  • \n"); - server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
  • \n"); - server->sendContent("
  • External IP MSG
  • \n"); - server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/msg?code=" + selCode.data + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
\n"); - server->sendContent("
    \n"); - server->sendContent("
  • Hostname JSON
  • \n"); - server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
  • \n"); - server->sendContent("
  • Local IP JSON
  • \n"); - server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
  • \n"); - server->sendContent("
  • External IP JSON
  • \n"); - server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
\n"); + //} else if (strtoul(selCode.address, 0, 0) > 0) { + server->sendContent("
\n"); + server->sendContent("
\n"); + server->sendContent("
    \n"); + server->sendContent("
  • Hostname MSG
  • \n"); + server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
  • \n"); + server->sendContent("
  • Local IP MSG
  • \n"); + server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
  • \n"); + server->sendContent("
  • External IP MSG
  • \n"); + server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/msg?code=" + selCode.data + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "&address=" + String(selCode.address) + "
\n"); + server->sendContent("
    \n"); + server->sendContent("
  • Hostname JSON
  • \n"); + server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
  • \n"); + server->sendContent("
  • Local IP JSON
  • \n"); + server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
  • \n"); + server->sendContent("
  • External IP JSON
  • \n"); + server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + ",'address':'" + String(selCode.address) + "'}]
\n"); } else { - server->sendContent("
\n"); - server->sendContent("
\n"); - server->sendContent("
    \n"); - server->sendContent("
  • Hostname MSG
  • \n"); - server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
  • \n"); - server->sendContent("
  • Local IP MSG
  • \n"); - server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
  • \n"); - server->sendContent("
  • External IP MSG
  • \n"); - server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/msg?code=" + selCode.data + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
\n"); - server->sendContent("
    \n"); - server->sendContent("
  • Hostname JSON
  • \n"); - server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
  • \n"); - server->sendContent("
  • Local IP JSON
  • \n"); - server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
  • \n"); - server->sendContent("
  • External IP JSON
  • \n"); - server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
\n"); + server->sendContent("
\n"); + server->sendContent("
\n"); + server->sendContent("
    \n"); + server->sendContent("
  • Hostname MSG
  • \n"); + server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
  • \n"); + server->sendContent("
  • Local IP MSG
  • \n"); + server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/msg?code=" + String(selCode.data) + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
  • \n"); + server->sendContent("
  • External IP MSG
  • \n"); + server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/msg?code=" + selCode.data + ":" + String(selCode.encoding) + ":" + String(selCode.bits) + "
\n"); + server->sendContent("
    \n"); + server->sendContent("
  • Hostname JSON
  • \n"); + server->sendContent("
  • http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
  • \n"); + server->sendContent("
  • Local IP JSON
  • \n"); + server->sendContent("
  • http://" + WiFi.localIP().toString() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
  • \n"); + server->sendContent("
  • External IP JSON
  • \n"); + server->sendContent("
  • http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':'" + String(selCode.data) + "','type':'" + String(selCode.encoding) + "','length':" + String(selCode.bits) + "}]
\n"); } server->sendContent("
\n"); server->sendContent("
\n"); @@ -1187,18 +1235,18 @@ void sendCodePage(Code selCode, int httpcode){ //+============================================================================= // Code to JsonObject // -void cvrtCode(Code& codeData, decode_results *results) +void cvrtCode(Code & codeData, decode_results * results) { strncpy(codeData.data, uint64ToString(results->value, 16).c_str(), 40); strncpy(codeData.encoding, encoding(results).c_str(), 14); codeData.bits = results->bits; String r = ""; - for (uint16_t i = 1; i < results->rawlen; i++) { - r += results->rawbuf[i] * RAWTICK; - if (i < results->rawlen - 1) - r += ","; // ',' not needed on last one - //if (!(i & 1)) r += " "; - } + for (uint16_t i = 1; i < results->rawlen; i++) { + r += results->rawbuf[i] * RAWTICK; + if (i < results->rawlen - 1) + r += ","; // ',' not needed on last one + //if (!(i & 1)) r += " "; + } codeData.raw = r; if (results->decode_type != UNKNOWN) { strncpy(codeData.address, ("0x" + String(results->address, HEX)).c_str(), 20); @@ -1212,7 +1260,7 @@ void cvrtCode(Code& codeData, decode_results *results) //+============================================================================= // Dump out the decode_results structure. // -void dumpInfo(decode_results *results) { +void dumpInfo(decode_results * results) { if (results->overflow) Serial.println("WARNING: IR code too long. " "Edit IRrecv.h and increase RAWBUF"); @@ -1234,7 +1282,7 @@ void dumpInfo(decode_results *results) { //+============================================================================= // Dump out the decode_results structure. // -void dumpRaw(decode_results *results) { +void dumpRaw(decode_results * results) { // Print Raw data Serial.print("Timing["); Serial.print(results->rawlen - 1, DEC); @@ -1267,7 +1315,7 @@ void dumpRaw(decode_results *results) { //+============================================================================= // Dump out the decode_results structure. // -void dumpCode(decode_results *results) { +void dumpCode(decode_results * results) { // Start declaration Serial.print("uint16_t "); // variable type Serial.print("rawData["); // array name @@ -1408,10 +1456,12 @@ void irblast(String type, String dataStr, unsigned int len, int rdelay, int puls last_send.valid = true; resetReceive(); + + mqtt_client.publish(("/" + String(host_name) + String(mqtt_topic_snd)).c_str(), ("[{'data':'" + String(last_send.data) + "','type':'" + String(last_send.encoding) + "','length':" + String(last_send.bits) + ",'address':'" + String(last_send.address) + "'}]").c_str(), true); } -void rawblast(JsonArray &raw, int khz, int rdelay, int pulse, int pdelay, int repeat, IRsend irsend,int duty) { +void rawblast(JsonArray & raw, int khz, int rdelay, int pulse, int pdelay, int repeat, IRsend irsend, int duty) { Serial.println("Raw transmit"); holdReceive = true; Serial.println("Blocking incoming IR signals"); @@ -1420,7 +1470,7 @@ void rawblast(JsonArray &raw, int khz, int rdelay, int pulse, int pdelay, int re // Pulse Loop for (int p = 0; p < pulse; p++) { Serial.println("Sending code"); - irsend.enableIROut(khz,duty); + irsend.enableIROut(khz, duty); for (unsigned int i = 0; i < raw.size(); i++) { int val = raw[i]; if (i & 1) irsend.space(std::max(0, val)); @@ -1447,6 +1497,8 @@ void rawblast(JsonArray &raw, int khz, int rdelay, int pulse, int pdelay, int re last_send.valid = true; resetReceive(); + + mqtt_client.publish(("/" + String(host_name) + String(mqtt_topic_snd)).c_str(), ("[{'data':'" + String(last_send.data) + "','type':'" + String(last_send.encoding) + "','length':" + String(last_send.bits) + ",'address':'" + String(last_send.address) + "'}]").c_str(), true); } @@ -1486,7 +1538,7 @@ void roomba_send(int code, int pulse, int pdelay, IRsend irsend) resetReceive(); } -void copyCode (Code& c1, Code& c2) { +void copyCode (Code & c1, Code & c2) { strncpy(c2.data, c1.data, 40); strncpy(c2.encoding, c1.encoding, 14); //strncpy(c2.timestamp, c1.timestamp, 40); @@ -1500,6 +1552,24 @@ void copyCode (Code& c1, Code& c2) { void loop() { server->handleClient(); + + if (String(mqtt_host).length() > 0) { + if (!mqtt_client.connected()) { + long now = millis(); + if (now - mqtt_lastReconnectAttempt > 5000) { + mqtt_lastReconnectAttempt = now; + // Attempt to reconnect + if (mqtt_reconnect()) { + mqtt_lastReconnectAttempt = 0; + } + } + } else { + // Client connected + + mqtt_client.loop(); + } + } + ArduinoOTA.handle(); decode_results results; // Somewhere to store the results @@ -1518,6 +1588,32 @@ void loop() { irrecv.resume(); // Prepare for the next value digitalWrite(ledpin, LOW); // Turn on the LED for 0.5 seconds ticker.attach(0.5, disableLed); + mqtt_client.publish(("/" + String(host_name) + String(mqtt_topic_rc)).c_str(), ("[{'data':'" + String(last_recv.data) + "','type':'" + String(last_recv.encoding) + "','length':" + String(last_recv.bits) + ",'address':'" + String(last_recv.address) + "'}]").c_str(), true); } delay(200); } + +boolean mqtt_reconnect() { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + if ((String(mqtt_user).length() == 0 && mqtt_client.connect(host_name)) || mqtt_client.connect(host_name, mqtt_user, mqtt_pass)) { + Serial.println("connected"); + mqtt_client.subscribe(("/" + String(host_name) + String(mqtt_topic_cmd)).c_str()); + } else { + Serial.print("failed, rc="); + Serial.println(mqtt_client.state()); + } + return mqtt_client.connected(); +} + +void mqtt_calback(char* topic, byte * payload, unsigned int length) { + Serial.print("MQTT message received..."); + DynamicJsonBuffer jsonBuffer; + JsonArray& root = jsonBuffer.parseArray(payload); + if (!root.success()) { + Serial.println("JSON parsing failed"); + } else { + blastJson(root, 1); + } + jsonBuffer.clear(); +}