Skip to content

Instantly share code, notes, and snippets.

@Antti
Created September 21, 2017 19:05
Show Gist options
  • Save Antti/71aa595a1b07194fd5309b893a5421bd to your computer and use it in GitHub Desktop.
Save Antti/71aa595a1b07194fd5309b893a5421bd to your computer and use it in GitHub Desktop.
#include <FS.h> // This needs to be first, or it all crashes and burns
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRutils.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
//#include <ESP8266mDNS.h> // Useful to access to ESP by hostname.local
#include <ArduinoJson.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <Ticker.h> // For LED status
#include <NTPClient.h>
const int configpin = 13; // GPIO13 (D7 on D1 Mini) to enable configuration (connect to ground)
const int ledpin = BUILTIN_LED; // Built in LED defined for WEMOS people
const char *wifi_config_name = "IRBlaster Configuration";
const char serverName[] = "checkip.dyndns.org";
int port = 80;
char passcode[40] = "";
char host_name[40] = "";
char port_str[20] = "80";
DynamicJsonBuffer jsonBuffer;
JsonObject& last_code = jsonBuffer.createObject(); // Stores last code
JsonObject& last_code_2 = jsonBuffer.createObject(); // Stores 2nd to last code
JsonObject& last_code_3 = jsonBuffer.createObject(); // Stores 3rd to last code
JsonObject& last_code_4 = jsonBuffer.createObject(); // Stores 4th to last code
JsonObject& last_code_5 = jsonBuffer.createObject(); // Stores 5th to last code
JsonObject& last_send = jsonBuffer.createObject(); // Stores last sent
JsonObject& last_send_2 = jsonBuffer.createObject(); // Stores 2nd last sent
JsonObject& last_send_3 = jsonBuffer.createObject(); // Stores 3rd last sent
JsonObject& last_send_4 = jsonBuffer.createObject(); // Stores 4th last sent
JsonObject& last_send_5 = jsonBuffer.createObject(); // Stores 5th last sent
ESP8266WebServer server(port);
HTTPClient http;
Ticker ticker;
bool shouldSaveConfig = false; // Flag for saving data
bool holdReceive = false; // Flag to prevent IR receiving while transmitting
const int pinr1 = 14; // Receiving pin
const int pins1 = 4; // Transmitting preset 1
const int pins2 = 5; // Transmitting preset 2
const int pins3 = 12; // Transmitting preset 3
const int pins4 = 13; // Transmitting preset 4
const int motor1_pin = 1;
const int motor2_pin = 3;
const int motor_enable_pin = 15;
IRrecv irrecv(pinr1);
IRsend irsend1(pins1);
IRsend irsend2(pins2);
IRsend irsend3(pins3);
IRsend irsend4(pins4);
const unsigned long resetfrequency = 259200000; // 72 hours in milliseconds
const int timeOffset = 0; // Timezone offset in seconds
const char* poolServerName = "time.nist.gov";
const bool getTime = true; // Set to false to disable querying for the time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, poolServerName, timeOffset, (int)resetfrequency);
const bool getExternalIP = true; // Set to false to disable querying external IP
String _ip = "";
unsigned long lastupdate = 0;
//+=============================================================================
// Callback notifying us of the need to save config
//
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
//+=============================================================================
// Reenable IR receiving
//
void resetReceive() {
if (holdReceive) {
Serial.println("Reenabling receiving");
irrecv.resume();
holdReceive = false;
}
}
//+=============================================================================
// Toggle state
//
void tick()
{
int state = digitalRead(ledpin); // get the current state of GPIO1 pin
digitalWrite(ledpin, !state); // set pin to the opposite state
}
//+=============================================================================
// Get External IP Address
//
String externalIP()
{
if (!getExternalIP) {
return "0.0.0.0"; // User doesn't want the external IP
}
if (_ip != "") {
if (millis() - lastupdate > resetfrequency || lastupdate > millis()) {
Serial.println("Reseting cached external IP address");
_ip = ""; // Reset the cached external IP every 72 hours
} else {
return _ip;
}
}
unsigned long start = millis();
http.setTimeout(5000);
http.begin(serverName, 8245);
int httpCode = http.GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
String payload = http.getString();
int pos_start = payload.indexOf("IP Address") + 12; // add 10 for "IP Address" and 2 for ":" + "space"
int pos_end = payload.indexOf("</body>", pos_start); // add nothing
_ip = payload.substring(pos_start, pos_end);
Serial.print(F("External IP: "));
Serial.println(_ip);
http.end();
} else {
Serial.println("Error retrieving external IP");
}
Serial.print("External IP address request took ");
Serial.print(millis() - start);
Serial.println(" ms");
return _ip;
}
//+=============================================================================
// Turn off the Led after timeout
//
void disableLed()
{
Serial.println("Turning off the LED to save power.");
digitalWrite(ledpin, HIGH); // Shut down the LED
ticker.detach(); // Stopping the ticker
}
//+=============================================================================
// Gets called when WiFiManager enters configuration mode
//
void configModeCallback (WiFiManager *myWiFiManager) {
Serial.println("Entered config mode");
Serial.println(WiFi.softAPIP());
//if you used auto generated SSID, print it
Serial.println(myWiFiManager->getConfigPortalSSID());
//entered config mode, make led toggle faster
ticker.attach(0.2, tick);
}
//+=============================================================================
// First setup of the Wifi.
// If return true, the Wifi is well connected.
// Should not return false if Wifi cannot be connected, it will loop
//
bool setupWifi(bool resetConf) {
// set led pin as output
pinMode(ledpin, OUTPUT);
// start ticker with 0.5 because we start in AP mode and try to connect
ticker.attach(0.6, tick);
// WiFiManager
// Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wifiManager;
// reset settings - for testing
if (resetConf)
wifiManager.resetSettings();
// set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
wifiManager.setAPCallback(configModeCallback);
// set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
if (json.containsKey("hostname")) strncpy(host_name, json["hostname"], 40);
if (json.containsKey("passcode")) strncpy(passcode, json["passcode"], 40);
if (json.containsKey("port_str")) {
strncpy(port_str, json["port_str"], 20);
port = atoi(json["port_str"]);
}
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
// end read
WiFiManagerParameter custom_hostname("hostname", "Choose a hostname to this IRBlaster", host_name, 40);
wifiManager.addParameter(&custom_hostname);
WiFiManagerParameter custom_passcode("passcode", "Choose a passcode", passcode, 40);
wifiManager.addParameter(&custom_passcode);
WiFiManagerParameter custom_port("port_str", "Choose a port", port_str, 40);
wifiManager.addParameter(&custom_port);
// fetches ssid and pass and tries to connect
// if it does not connect it starts an access point with the specified name
// and goes into a blocking loop awaiting configuration
if (!wifiManager.autoConnect(wifi_config_name)) {
Serial.println("failed to connect and hit timeout");
// reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(1000);
}
// if you get here you have connected to the WiFi
strncpy(host_name, custom_hostname.getValue(), 40);
strncpy(passcode, custom_passcode.getValue(), 40);
strncpy(port_str, custom_port.getValue(), 20);
port = atoi(port_str);
if (port != 80) {
Serial.println("Default port changed");
server = ESP8266WebServer(port);
}
Serial.println("WiFi connected! User chose hostname '" + String(host_name) + String("' passcode '") + String(passcode) + "' and port '" + String(port_str) + "'");
// save the custom parameters to FS
if (shouldSaveConfig) {
Serial.println(" config...");
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["hostname"] = host_name;
json["passcode"] = passcode;
json["port_str"] = port_str;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
json.printTo(Serial);
Serial.println("");
json.printTo(configFile);
configFile.close();
//e nd save
}
ticker.detach();
// keep LED on
digitalWrite(ledpin, LOW);
return true;
}
//+=============================================================================
// Setup web server and IR receiver/blaster
//
void setup() {
// Initialize serial
Serial.begin(115200);
Serial.println("");
Serial.println("ESP8266 IR Controller");
pinMode(configpin, INPUT_PULLUP);
if (!setupWifi(digitalRead(configpin) == LOW))
return;
WiFi.hostname(host_name);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
wifi_set_sleep_type(LIGHT_SLEEP_T);
digitalWrite(ledpin, LOW);
// Turn off the led in 2s
ticker.attach(2, disableLed);
// Configure mDNS
//if (MDNS.begin(host_name)) {
// Serial.println("mDNS started. Hostname is set to " + String(host_name) + ".local");
//}
//MDNS.addService("http", "tcp", port); // Announce the ESP as an HTTP service
Serial.println("URL to send commands: http://" + String(host_name) + ".local:" + port_str);
if (getTime) timeClient.begin(); // Get the time
// Configure the server
server.on("/json", []() { // JSON handler for more complicated IR blaster routines
Serial.println("Connection received - JSON");
DynamicJsonBuffer jsonBuffer;
JsonArray& root = jsonBuffer.parseArray(server.arg("plain"));
int simple = 0;
if (server.hasArg("simple")) simple = server.arg("simple").toInt();
if (!root.success()) {
Serial.println("JSON parsing failed");
if (simple) {
server.send(400, "text/plain", "JSON parsing failed");
} else {
sendHomePage("JSON parsing failed", "Error", 3, 400); // 400
}
} else if (server.arg("pass") != passcode) {
Serial.println("Unauthorized access");
if (simple) {
server.send(401, "text/plain", "Unauthorized, invalid passcode");
} else {
sendHomePage("Invalid passcode", "Unauthorized", 3, 401); // 401
}
} else {
digitalWrite(ledpin, LOW);
ticker.attach(0.5, disableLed);
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 out = root[x]["out"];
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 (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(out));
} else if (type == "roku") {
String data = root[x]["data"];
rokuCommand(ip, data);
} else {
String data = root[x]["data"];
long address = root[x]["address"];
int len = root[x]["length"];
irblast(type, data, len, rdelay, pulse, pdelay, repeat, address, pickIRsend(out));
}
}
if (simple) {
server.send(200, "text/html", "Success, code sent");
} else {
sendHomePage("Code sent", "Success", 1); // 200
}
}
});
// Setup simple msg server to mirror version 1.0 functionality
server.on("/msg", []() {
Serial.println("Connection received - MSG");
int simple = 0;
if (server.hasArg("simple")) simple = server.arg("simple").toInt();
if (server.arg("pass") != passcode) {
Serial.println("Unauthorized access");
if (simple) {
server.send(401, "text/plain", "Unauthorized, invalid passcode");
} else {
sendHomePage("Invalid passcode", "Unauthorized", 3, 401); // 401
}
} else {
digitalWrite(ledpin, LOW);
ticker.attach(0.5, disableLed);
String type = server.arg("type");
String data = server.arg("data");
String ip = server.arg("ip");
int len = server.arg("length").toInt();
long address = (server.hasArg("address")) ? server.arg("address").toInt() : 0;
int rdelay = (server.hasArg("rdelay")) ? server.arg("rdelay").toInt() : 1000;
int pulse = (server.hasArg("pulse")) ? server.arg("pulse").toInt() : 1;
int pdelay = (server.hasArg("pdelay")) ? server.arg("pdelay").toInt() : 100;
int repeat = (server.hasArg("repeat")) ? server.arg("repeat").toInt() : 1;
int out = (server.hasArg("out")) ? server.arg("out").toInt() : 0;
if (server.hasArg("code")) {
String code = server.arg("code");
char separator = ':';
data = getValue(code, separator, 0);
type = getValue(code, separator, 1);
len = getValue(code, separator, 2).toInt();
}
if (type == "roku") {
rokuCommand(ip, data);
} else {
irblast(type, data, len, rdelay, pulse, pdelay, repeat, address, pickIRsend(out));
}
if (simple) {
server.send(200, "text/html", "Success, code sent");
} else {
sendHomePage("Code Sent", "Success", 1); // 200
}
}
});
server.on("/received", []() {
Serial.println("Connection received");
int id = server.arg("id").toInt();
String output;
if (id == 1 && last_code.containsKey("time")) {
sendCodePage(last_code);
} else if (id == 2 && last_code_2.containsKey("time")) {
sendCodePage(last_code_2);
} else if (id == 3 && last_code_3.containsKey("time")) {
sendCodePage(last_code_3);
} else if (id == 4 && last_code_4.containsKey("time")) {
sendCodePage(last_code_4);
} else if (id == 5 && last_code_5.containsKey("time")) {
sendCodePage(last_code_5);
} else {
sendHomePage("Code does not exist", "Alert", 2, 404); // 404
}
});
server.on("/", []() {
Serial.println("Connection received");
sendHomePage(); // 200
});
server.on("/motor", []() {
Serial.println("Starting motor");
int dir = 0;
if (server.hasArg("direction")) dir = server.arg("direction").toInt();
// start motor, wait amount, stop motor
if (dir == 0) {
digitalWrite(motor2_pin, LOW);
digitalWrite(motor1_pin, HIGH);
} else {
digitalWrite(motor1_pin, LOW);
digitalWrite(motor2_pin, HIGH);
}
digitalWrite(motor_enable_pin, HIGH);
delay(5000);
digitalWrite(motor_enable_pin, LOW);
});
server.begin();
Serial.println("HTTP Server started on port " + String(port));
externalIP();
irsend1.begin();
irsend2.begin();
irsend3.begin();
irsend4.begin();
irrecv.enableIRIn();
Serial.println("Ready to send and receive IR signals");
}
//+=============================================================================
// IP Address to String
//
String ipToString(IPAddress ip)
{
String s="";
for (int i=0; i<4; i++)
s += i ? "." + String(ip[i]) : String(ip[i]);
return s;
}
//+=============================================================================
// Send command to local roku
//
int rokuCommand(String ip, String data) {
String url = "http://" + ip + ":8060/" + data;
http.begin(url);
Serial.println(url);
Serial.println("Sending roku command");
copyJsonSend(last_send_4, last_send_5);
copyJsonSend(last_send_3, last_send_4);
copyJsonSend(last_send_2, last_send_3);
copyJsonSend(last_send, last_send_2);
last_send["data"] = data;
last_send["len"] = 1;
last_send["type"] = "roku";
last_send["address"] = ip;
last_send["time"] = String(timeClient.getFormattedTime());
return http.POST("");
http.end();
}
//+=============================================================================
// Split string by character
//
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
//+=============================================================================
// Return which IRsend object to act on
//
IRsend pickIRsend (int out) {
switch (out) {
case 1: return irsend1;
case 2: return irsend2;
case 3: return irsend3;
case 4: return irsend4;
default: return irsend1;
}
}
//+=============================================================================
// Display encoding type
//
// Display encoding type
//
String encoding(decode_results *results) {
String output;
switch (results->decode_type) {
default:
case UNKNOWN: output = "UNKNOWN"; break;
case NEC: output = "NEC"; break;
case SONY: output = "SONY"; break;
case RC5: output = "RC5"; break;
case RC6: output = "RC6"; break;
case DISH: output = "DISH"; break;
case SHARP: output = "SHARP"; break;
case JVC: output = "JVC"; break;
case SANYO: output = "SANYO"; break;
case SANYO_LC7461: output = "SANYO_LC7461"; break;
case MITSUBISHI: output = "MITSUBISHI"; break;
case SAMSUNG: output = "SAMSUNG"; break;
case LG: output = "LG"; break;
case WHYNTER: output = "WHYNTER"; break;
case AIWA_RC_T501: output = "AIWA_RC_T501"; break;
case PANASONIC: output = "PANASONIC"; break;
case DENON: output = "DENON"; break;
case COOLIX: output = "COOLIX"; break;
}
return output;
if (results->repeat) Serial.print(" (Repeat)");
}
//+=============================================================================
// Uint64 to String
//
String Uint64toString(uint64_t input, uint8_t base) {
char buf[8 * sizeof(input) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) base = 10;
do {
char c = input % base;
input /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while (input);
std::string s(str);
return s.c_str();
}
//+=============================================================================
// Code to string
//
void fullCode (decode_results *results)
{
Serial.print("One line: ");
serialPrintUint64(results->value, 16);
Serial.print(":");
Serial.print(encoding(results));
Serial.print(":");
Serial.print(results->bits, DEC);
if (results->overflow)
Serial.println("WARNING: IR code too long."
"Edit IRrecv.h and increase RAWBUF");
Serial.println("");
}
//+=============================================================================
// Send header HTML
//
void sendHeader() {
sendHeader(200);
}
void sendHeader(int httpcode) {
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(httpcode, "text/html; charset=utf-8", "");
server.sendContent("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
server.sendContent("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en'>\n");
server.sendContent(" <head>\n");
server.sendContent(" <meta name='viewport' content='width=device-width, initial-scale=.75' />\n");
server.sendContent(" <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' />\n");
server.sendContent(" <style>@media (max-width: 991px) {.nav-pills>li {float: none; margin-left: 0; margin-top: 5px; text-align: center;}}</style>\n");
server.sendContent(" <title>ESP8266 IR Controller (" + String(host_name) + ")</title>\n");
server.sendContent(" </head>\n");
server.sendContent(" <body>\n");
server.sendContent(" <div class='container'>\n");
server.sendContent(" <h1><a href='https://github.com/mdhiggins/ESP8266-HTTP-IR-Blaster'>ESP8266 IR Controller</a></h1>\n");
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <ul class='nav nav-pills'>\n");
server.sendContent(" <li class='active'>\n");
server.sendContent(" <a href='http://" + String(host_name) + ".local" + ":" + String(port) + "'>Hostname <span class='badge'>" + String(host_name) + ".local" + ":" + String(port) + "</span></a></li>\n");
server.sendContent(" <li class='active'>\n");
server.sendContent(" <a href='http://" + ipToString(WiFi.localIP()) + ":" + String(port) + "'>Local <span class='badge'>" + ipToString(WiFi.localIP()) + ":" + String(port) + "</span></a></li>\n");
server.sendContent(" <li class='active'>\n");
server.sendContent(" <a href='http://" + externalIP() + ":" + String(port) + "'>External <span class='badge'>" + externalIP() + ":" + String(port) + "</span></a></li>\n");
server.sendContent(" <li class='active'>\n");
server.sendContent(" <a href='#'>MAC <span class='badge'>" + String(WiFi.macAddress()) + "</span></a></li>\n");
server.sendContent(" </ul>\n");
server.sendContent(" </div>\n");
server.sendContent(" </div><hr />\n");
}
//+=============================================================================
// Send footer HTML
//
void sendFooter() {
server.sendContent(" <div class='row'><div class='col-md-12'><em>" + String(millis()) + "ms uptime</em></div></div>\n");
server.sendContent(" </div>\n");
server.sendContent(" </body>\n");
server.sendContent("</html>\n");
server.client().stop();
}
//+=============================================================================
// Stream home page HTML
//
void sendHomePage() {
sendHomePage("", "");
}
void sendHomePage(String message, String header) {
sendHomePage(message, header, 0);
}
void sendHomePage(String message, String header, int type) {
sendHomePage(message, header, type, 200);
}
void sendHomePage(String message, String header, int type, int httpcode) {
sendHeader(httpcode);
if (type == 1)
server.sendContent(" <div class='row'><div class='col-md-12'><div class='alert alert-success'><strong>" + header + "!</strong> " + message + "</div></div></div>\n");
if (type == 2)
server.sendContent(" <div class='row'><div class='col-md-12'><div class='alert alert-warning'><strong>" + header + "!</strong> " + message + "</div></div></div>\n");
if (type == 3)
server.sendContent(" <div class='row'><div class='col-md-12'><div class='alert alert-danger'><strong>" + header + "!</strong> " + message + "</div></div></div>\n");
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <h3>Codes Transmitted</h3>\n");
server.sendContent(" <table class='table table-striped' style='table-layout: fixed;'>\n");
server.sendContent(" <thead><tr><th>Sent</th><th>Command</th><th>Type</th><th>Length</th><th>Address</th></tr></thead>\n"); //Title
server.sendContent(" <tbody>\n");
if (last_send.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td>" + last_send["time"].as<String>() + "</td><td><code>" + last_send["data"].as<String>() + "</code></td><td><code>" + last_send["type"].as<String>() + "</code></td><td><code>" + last_send["len"].as<String>() + "</code></td><td><code>" + last_send["address"].as<String>() + "</code></td></tr>\n");
if (last_send_2.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td>" + last_send_2["time"].as<String>() + "</td><td><code>" + last_send_2["data"].as<String>() + "</code></td><td><code>" + last_send_2["type"].as<String>() + "</code></td><td><code>" + last_send_2["len"].as<String>() + "</code></td><td><code>" + last_send_2["address"].as<String>() + "</code></td></tr>\n");
if (last_send_3.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td>" + last_send_3["time"].as<String>() + "</td><td><code>" + last_send_3["data"].as<String>() + "</code></td><td><code>" + last_send_3["type"].as<String>() + "</code></td><td><code>" + last_send_3["len"].as<String>() + "</code></td><td><code>" + last_send_3["address"].as<String>() + "</code></td></tr>\n");
if (last_send_4.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td>" + last_send_4["time"].as<String>() + "</td><td><code>" + last_send_4["data"].as<String>() + "</code></td><td><code>" + last_send_4["type"].as<String>() + "</code></td><td><code>" + last_send_4["len"].as<String>() + "</code></td><td><code>" + last_send_4["address"].as<String>() + "</code></td></tr>\n");
if (last_send_5.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td>" + last_send_5["time"].as<String>() + "</td><td><code>" + last_send_5["data"].as<String>() + "</code></td><td><code>" + last_send_5["type"].as<String>() + "</code></td><td><code>" + last_send_5["len"].as<String>() + "</code></td><td><code>" + last_send_5["address"].as<String>() + "</code></td></tr>\n");
if (!last_send.containsKey("time") && !last_send_2.containsKey("time") && !last_send_3.containsKey("time") && !last_send_4.containsKey("time") && !last_send_5.containsKey("time"))
server.sendContent(" <tr><td colspan='5' class='text-center'><em>No codes sent</em></td></tr>");
server.sendContent(" </tbody></table>\n");
server.sendContent(" </div></div>\n");
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <h3>Codes Received</h3>\n");
server.sendContent(" <table class='table table-striped' style='table-layout: fixed;'>\n");
server.sendContent(" <thead><tr><th>Time Sent</th><th>Command</th><th>Type</th><th>Length</th><th>Address</th></tr></thead>\n"); //Title
server.sendContent(" <tbody>\n");
if (last_code.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td><a href='/received?id=1'>" + last_code["time"].as<String>() + "</a></td><td><code>" + last_code["data"].as<String>() + "</code></td><td><code>" + last_code["encoding"].as<String>() + "</code></td><td><code>" + last_code["bits"].as<String>() + "</code></td><td><code>" + last_code["address"].as<String>() + "</code></td></tr>\n");
if (last_code_2.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td><a href='/received?id=2'>" + last_code_2["time"].as<String>() + "</a></td><td><code>" + last_code_2["data"].as<String>() + "</code></td><td><code>" + last_code_2["encoding"].as<String>() + "</code></td><td><code>" + last_code_2["bits"].as<String>() + "</code></td><td><code>" + last_code_2["address"].as<String>() + "</code></td></tr>\n");
if (last_code_3.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td><a href='/received?id=3'>" + last_code_3["time"].as<String>() + "</a></td><td><code>" + last_code_3["data"].as<String>() + "</code></td><td><code>" + last_code_3["encoding"].as<String>() + "</code></td><td><code>" + last_code_3["bits"].as<String>() + "</code></td><td><code>" + last_code_3["address"].as<String>() + "</code></td></tr>\n");
if (last_code_4.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td><a href='/received?id=4'>" + last_code_4["time"].as<String>() + "</a></td><td><code>" + last_code_4["data"].as<String>() + "</code></td><td><code>" + last_code_4["encoding"].as<String>() + "</code></td><td><code>" + last_code_4["bits"].as<String>() + "</code></td><td><code>" + last_code_4["address"].as<String>() + "</code></td></tr>\n");
if (last_code_5.containsKey("time"))
server.sendContent(" <tr class='text-uppercase'><td><a href='/received?id=5'>" + last_code_5["time"].as<String>() + "</a></td><td><code>" + last_code_5["data"].as<String>() + "</code></td><td><code>" + last_code_5["encoding"].as<String>() + "</code></td><td><code>" + last_code_5["bits"].as<String>() + "</code></td><td><code>" + last_code_5["address"].as<String>() + "</code></td></tr>\n");
if (!last_code.containsKey("time") && !last_code_2.containsKey("time") && !last_code_3.containsKey("time") && !last_code_4.containsKey("time") && !last_code_5.containsKey("time"))
server.sendContent(" <tr><td colspan='5' class='text-center'><em>No codes received</em></td></tr>");
server.sendContent(" </tbody></table>\n");
server.sendContent(" </div></div>\n");
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <ul class='list-unstyled'>\n");
server.sendContent(" <li><span class='badge'>GPIO " + String(pinr1) + "</span> Receiving </li>\n");
server.sendContent(" <li><span class='badge'>GPIO " + String(pins1) + "</span> Transmitter 1 </li>\n");
server.sendContent(" <li><span class='badge'>GPIO " + String(pins2) + "</span> Transmitter 2 </li>\n");
server.sendContent(" <li><span class='badge'>GPIO " + String(pins3) + "</span> Transmitter 3 </li>\n");
server.sendContent(" <li><span class='badge'>GPIO " + String(pins4) + "</span> Transmitter 4 </li></ul>\n");
server.sendContent(" </div>\n");
server.sendContent(" </div>\n");
sendFooter();
}
//+=============================================================================
// Stream code page HTML
//
void sendCodePage(JsonObject& selCode) {
sendCodePage(selCode, 200);
}
void sendCodePage(JsonObject& selCode, int httpcode){
sendHeader(httpcode);
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <h2><span class='label label-success'>" + selCode["data"].as<String>() + ":" + selCode["encoding"].as<String>() + ":" + selCode["bits"].as<String>() + "</span></h2><br/>\n");
server.sendContent(" <dl class='dl-horizontal'>\n");
server.sendContent(" <dt>Data</dt>\n");
server.sendContent(" <dd><code>" + selCode["data"].as<String>() + "</code></dd></dl>\n");
server.sendContent(" <dl class='dl-horizontal'>\n");
server.sendContent(" <dt>Type</dt>\n");
server.sendContent(" <dd><code>" + selCode["encoding"].as<String>() + "</code></dd></dl>\n");
server.sendContent(" <dl class='dl-horizontal'>\n");
server.sendContent(" <dt>Length</dt>\n");
server.sendContent(" <dd><code>" + selCode["bits"].as<String>() + "</code></dd></dl>\n");
server.sendContent(" <dl class='dl-horizontal'>\n");
server.sendContent(" <dt>Address</dt>\n");
server.sendContent(" <dd><code>" + selCode["address"].as<String>() + "</code></dd></dl>\n");
server.sendContent(" <dl class='dl-horizontal'>\n");
server.sendContent(" <dt>Raw</dt>\n");
server.sendContent(" <dd><code>" + selCode["uint16_t"].as<String>() + "</code></dd></dl>\n");
server.sendContent(" </div></div>\n");
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <div class='alert alert-warning'>Don't forget to add your passcode to the URLs below if you set one</div>\n");
server.sendContent(" </div></div>\n");
if (selCode["encoding"] == "UNKNOWN") {
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <ul class='list-unstyled'>\n");
server.sendContent(" <li>Hostname <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':[" + selCode["uint16_t"].as<String>() + "], 'type':'raw', 'khz':38}]</pre></li>\n");
server.sendContent(" <li>Local IP <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + ipToString(WiFi.localIP()) + ":" + String(port) + "/json?plain=[{'data':[" + selCode["uint16_t"].as<String>() + "], 'type':'raw', 'khz':38}]</pre></li>\n");
server.sendContent(" <li>External IP <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':[" + selCode["uint16_t"].as<String>() + "], 'type':'raw', 'khz':38}]</pre></li></ul>\n");
} else {
server.sendContent(" <div class='row'>\n");
server.sendContent(" <div class='col-md-12'>\n");
server.sendContent(" <ul class='list-unstyled'>\n");
server.sendContent(" <li>Hostname <span class='label label-default'>MSG</span></li>\n");
server.sendContent(" <li><pre>http://" + String(host_name) + ".local:" + String(port) + "/msg?code=" + selCode["data"].as<String>() + ":" + selCode["encoding"].as<String>() + ":" + selCode["bits"].as<String>() + "</pre></li>\n");
server.sendContent(" <li>Local IP <span class='label label-default'>MSG</span></li>\n");
server.sendContent(" <li><pre>http://" + ipToString(WiFi.localIP()) + ":" + String(port) + "/msg?code=" + selCode["data"].as<String>() + ":" + selCode["encoding"].as<String>() + ":" + selCode["bits"].as<String>() + "</pre></li>\n");
server.sendContent(" <li>External IP <span class='label label-default'>MSG</span></li>\n");
server.sendContent(" <li><pre>http://" + externalIP() + ":" + String(port) + "/msg?code=" + selCode["data"].as<String>() + ":" + selCode["encoding"].as<String>() + ":" + selCode["bits"].as<String>() + "</pre></li></ul>\n");
server.sendContent(" <ul class='list-unstyled'>\n");
server.sendContent(" <li>Hostname <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + String(host_name) + ".local:" + String(port) + "/json?plain=[{'data':'" + selCode["data"].as<String>() + "', 'type':'" + selCode["encoding"].as<String>() + "', 'length':" + selCode["bits"].as<String>() + "}]</pre></li>\n");
server.sendContent(" <li>Local IP <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + ipToString(WiFi.localIP()) + ":" + String(port) + "/json?plain=[{'data':'" + selCode["data"].as<String>() + "', 'type':'" + selCode["encoding"].as<String>() + "', 'length':" + selCode["bits"].as<String>() + "}]</pre></li>\n");
server.sendContent(" <li>External IP <span class='label label-default'>JSON</span></li>\n");
server.sendContent(" <li><pre>http://" + externalIP() + ":" + String(port) + "/json?plain=[{'data':'" + selCode["data"].as<String>() + "', 'type':'" + selCode["encoding"].as<String>() + "', 'length':" + selCode["bits"].as<String>() + "}]</pre></li></ul>\n");
}
server.sendContent(" </div>\n");
server.sendContent(" </div>\n");
sendFooter();
}
//+=============================================================================
// Code to JsonObject
//
void codeJson(JsonObject &codeData, decode_results *results)
{
codeData["data"] = Uint64toString(results->value, 16);
codeData["encoding"] = encoding(results);
codeData["bits"] = results->bits;
String r = "";
for (uint16_t i = 1; i < results->rawlen; i++) {
r += results->rawbuf[i] * USECPERTICK;
if (i < results->rawlen - 1)
r += ","; // ',' not needed on last one
if (!(i & 1)) r += " ";
}
codeData["uint16_t"] = r;
if (results->decode_type != UNKNOWN) {
codeData["address"] = "0x" + String(results->address, HEX);
codeData["command"] = "0x" + String(results->command, HEX);
} else {
codeData["address"] = "0x";
codeData["command"] = "0x";
}
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpInfo(decode_results *results) {
if (results->overflow)
Serial.println("WARNING: IR code too long."
"Edit IRrecv.h and increase RAWBUF");
// Show Encoding standard
Serial.print("Encoding : ");
Serial.print(encoding(results));
Serial.println("");
// Show Code & length
Serial.print("Code : ");
serialPrintUint64(results->value, 16);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpRaw(decode_results *results) {
// Print Raw data
Serial.print("Timing[");
Serial.print(results->rawlen - 1, DEC);
Serial.println("]: ");
for (uint16_t i = 1; i < results->rawlen; i++) {
if (i % 100 == 0)
yield(); // Preemptive yield every 100th entry to feed the WDT.
uint32_t x = results->rawbuf[i] * USECPERTICK;
if (!(i & 1)) { // even
Serial.print("-");
if (x < 1000) Serial.print(" ");
if (x < 100) Serial.print(" ");
Serial.print(x, DEC);
} else { // odd
Serial.print(" ");
Serial.print("+");
if (x < 1000) Serial.print(" ");
if (x < 100) Serial.print(" ");
Serial.print(x, DEC);
if (i < results->rawlen - 1)
Serial.print(", "); // ',' not needed for last one
}
if (!(i % 8)) Serial.println("");
}
Serial.println(""); // Newline
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpCode(decode_results *results) {
// Start declaration
Serial.print("uint16_t "); // variable type
Serial.print("rawData["); // array name
Serial.print(results->rawlen - 1, DEC); // array size
Serial.print("] = {"); // Start declaration
// Dump data
for (uint16_t i = 1; i < results->rawlen; i++) {
Serial.print(results->rawbuf[i] * USECPERTICK, DEC);
if (i < results->rawlen - 1)
Serial.print(","); // ',' not needed on last one
if (!(i & 1)) Serial.print(" ");
}
// End declaration
Serial.print("};"); //
// Comment
Serial.print(" // ");
Serial.print(encoding(results));
Serial.print(" ");
serialPrintUint64(results->value, 16);
// Newline
Serial.println("");
// Now dump "known" codes
if (results->decode_type != UNKNOWN) {
// Some protocols have an address &/or command.
// NOTE: It will ignore the atypical case when a message has been decoded
// but the address & the command are both 0.
if (results->address > 0 || results->command > 0) {
Serial.print("uint32_t address = 0x");
Serial.print(results->address, HEX);
Serial.println(";");
Serial.print("uint32_t command = 0x");
Serial.print(results->command, HEX);
Serial.println(";");
}
// All protocols have data
Serial.print("uint64_t data = 0x");
serialPrintUint64(results->value, 16);
Serial.println(";");
}
}
//+=============================================================================
// Convert string to hex, borrowed from ESPBasic
//
unsigned long HexToLongInt(String h)
{
// this function replace the strtol as this function is not able to handle hex numbers greather than 7fffffff
// I'll take char by char converting from hex to char then shifting 4 bits at the time
int i;
unsigned long tmp = 0;
unsigned char c;
int s = 0;
h.toUpperCase();
for (i = h.length() - 1; i >= 0 ; i--)
{
// take the char starting from the right
c = h[i];
// convert from hex to int
c = c - '0';
if (c > 9)
c = c - 7;
// add and shift of 4 bits per each char
tmp += c << s;
s += 4;
}
return tmp;
}
//+=============================================================================
// Send IR codes to variety of sources
//
void irblast(String type, String dataStr, unsigned int len, int rdelay, int pulse, int pdelay, int repeat, long address, IRsend irsend) {
Serial.println("Blasting off");
type.toLowerCase();
unsigned long data = HexToLongInt(dataStr);
holdReceive = true;
Serial.println("Blocking incoming IR signals");
// Repeat Loop
for (int r = 0; r < repeat; r++) {
// Pulse Loop
for (int p = 0; p < pulse; p++) {
Serial.print(data, HEX);
Serial.print(":");
Serial.print(type);
Serial.print(":");
Serial.println(len);
if (type == "nec") {
irsend.sendNEC(data, len);
} else if (type == "sony") {
irsend.sendSony(data, len);
} else if (type == "coolix") {
irsend.sendCOOLIX(data, len);
} else if (type == "whynter") {
irsend.sendWhynter(data, len);
} else if (type == "panasonic") {
Serial.println(address);
irsend.sendPanasonic(address, data);
} else if (type == "jvc") {
irsend.sendJVC(data, len, 0);
} else if (type == "samsung") {
irsend.sendSAMSUNG(data, len);
} else if (type == "sharp") {
irsend.sendSharpRaw(data, len);
} else if (type == "dish") {
irsend.sendDISH(data, len);
} else if (type == "rc5") {
irsend.sendRC5(data, len);
} else if (type == "rc6") {
irsend.sendRC6(data, len);
} else if (type == "roomba") {
roomba_send(atoi(dataStr.c_str()), pulse, pdelay, irsend);
}
delay(pdelay);
}
delay(rdelay);
}
copyJsonSend(last_send_4, last_send_5);
copyJsonSend(last_send_3, last_send_4);
copyJsonSend(last_send_2, last_send_3);
copyJsonSend(last_send, last_send_2);
last_send["data"] = dataStr;
last_send["len"] = len;
last_send["type"] = type;
last_send["address"] = address;
last_send["time"] = String(timeClient.getFormattedTime());
resetReceive();
}
void rawblast(JsonArray &raw, int khz, int rdelay, int pulse, int pdelay, int repeat, IRsend irsend) {
Serial.println("Raw transmit");
holdReceive = true;
Serial.println("Blocking incoming IR signals");
// Repeat Loop
for (int r = 0; r < repeat; r++) {
// Pulse Loop
for (int p = 0; p < pulse; p++) {
Serial.println("Sending code");
irsend.enableIROut(khz);
int first = raw[0];
for (unsigned int i = 0; i < raw.size(); i++) {
unsigned int val = raw[i];
if (i & 1) irsend.space(val);
else irsend.mark(val);
}
irsend.space(0);
delay(pdelay);
}
delay(rdelay);
}
resetReceive();
}
void roomba_send(int code, int pulse, int pdelay, IRsend irsend)
{
Serial.print("Sending Roomba code ");
Serial.println(code);
holdReceive = true;
Serial.println("Blocking incoming IR signals");
int length = 8;
uint16_t raw[length * 2];
unsigned int one_pulse = 3000;
unsigned int one_break = 1000;
unsigned int zero_pulse = one_break;
unsigned int zero_break = one_pulse;
uint16_t len = 15;
uint16_t hz = 38;
int arrayposition = 0;
for (int counter = length - 1; counter >= 0; --counter) {
if (code & (1 << counter)) {
raw[arrayposition] = one_pulse;
raw[arrayposition + 1] = one_break;
}
else {
raw[arrayposition] = zero_pulse;
raw[arrayposition + 1] = zero_break;
}
arrayposition = arrayposition + 2;
}
for (int i = 0; i < pulse; i++) {
irsend.sendRaw(raw, len, hz);
delay(pdelay);
}
resetReceive();
}
void copyJson(JsonObject& j1, JsonObject& j2) {
if (j1.containsKey("data")) j2["data"] = j1["data"];
if (j1.containsKey("encoding")) j2["encoding"] = j1["encoding"];
if (j1.containsKey("bits")) j2["bits"] = j1["bits"];
if (j1.containsKey("address")) j2["address"] = j1["address"];
if (j1.containsKey("command")) j2["command"] = j1["command"];
if (j1.containsKey("time")) j2["time"] = j1["time"];
if (j1.containsKey("uint16_t")) j2["uint16_t"] = j1["uint16_t"];
}
void copyJsonSend(JsonObject& j1, JsonObject& j2) {
if (j1.containsKey("data")) j2["data"] = j1["data"];
if (j1.containsKey("type")) j2["type"] = j1["type"];
if (j1.containsKey("len")) j2["len"] = j1["len"];
if (j1.containsKey("address")) j2["address"] = j1["address"];
if (j1.containsKey("time")) j2["time"] = j1["time"];
}
void loop() {
server.handleClient();
decode_results results; // Somewhere to store the results
if (getTime) timeClient.update(); // Update the time
if (irrecv.decode(&results) && !holdReceive) { // Grab an IR code
Serial.println("Signal received:");
fullCode(&results); // Print the singleline value
dumpCode(&results); // Output the results as source code
copyJson(last_code_4, last_code_5); // Pass
copyJson(last_code_3, last_code_4); // Pass
copyJson(last_code_2, last_code_3); // Pass
copyJson(last_code, last_code_2); // Pass
codeJson(last_code, &results); // Store the results
last_code["time"] = String(timeClient.getFormattedTime()); // Set the new update time
Serial.println(""); // Blank line between entries
irrecv.resume(); // Prepare for the next value
digitalWrite(ledpin, LOW); // Turn on the LED for 0.5 seconds
ticker.attach(0.5, disableLed);
}
delay(200);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment