Skip to content

Commit

Permalink
Add IR receiving support for IRMQTTServer.
Browse files Browse the repository at this point in the history
- Should capture IR messages from an IR RX module and send them to MQTT
  in a "compatiable" format similar to that required for sening via MQTT.
- Slight HTML layout changes/improvements.
- Minor changes to IRutils to make life easier.
- Tested on my own D1 and Nodemcu devices. Seems to work fine.
- The new capability can be completely disabled, but should work fine
  even if no IR RX module is attached.

Inspired by Issue #536
  • Loading branch information
crankyoldgit committed Oct 3, 2018
1 parent cd834f0 commit 25dde27
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 19 deletions.
93 changes: 84 additions & 9 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/*
* Send arbitrary IR codes via a web server or MQTT.
* Send & receive arbitrary IR codes via a web server or MQTT.
* Copyright David Conran 2016, 2017, 2018
*
* NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2). See IR_LED
* NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2) if
* you want to send IR messages. See IR_LED below.
* A compatible IR RX modules *MUST* be connected to ESP8266 GPIO14 (D5)
* if you want to capture & decode IR nessages. See IR_RX below.
*
* WARN: This is very advanced & complicated example code. Not for beginners.
* You are strongly suggested to try & look at other example code first.
Expand Down Expand Up @@ -94,6 +97,19 @@
* # Listen to MQTT acknowledgements.
* $ mosquitto_sub -h 10.20.0.253 -t ir_server/sent
*
* Incoming IR messages (from an IR remote control) will be transmitted to
* the MQTT topic 'ir_server/received'. The MQTT message will be formatted
* similar to what is required to for the 'sent' topic.
* e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes
* or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes
* Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol.
* You can't use that to recreate/resend an IR message. It's only for
* matching purposes and shouldn't be trusted.
*
* Unix command line usage example:
* # Listen via MQTT for IR messages captured by this server.
* $ mosquitto_sub -h 10.20.0.253 -t ir_server/received
*
* If DEBUG is turned on, there is additional information printed on the Serial
* Port.
*
Expand Down Expand Up @@ -136,6 +152,10 @@
// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2.
#define IR_LED 4
// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7.
//
// GPIO the IR RX module is connected to/controlled by. GPIO 14 = D5.
// Comment this out to disable receiving/decoding IR messages entirely.
#define IR_RX 14
const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on.
// Name of the device you want in mDNS.
// NOTE: Changing this will change the MQTT path too unless you override it
Expand Down Expand Up @@ -164,17 +184,31 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries.
// independent of the hostname.
#define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on
#define MQTTcommand MQTTprefix "/send" // Topic we get new commands from.
#define MQTTrecv MQTTprefix "/received" // Topic we send received IRs to.
#endif // MQTT_ENABLE

// HTML arguments we will parse for IR code information.
#define argType "type"
#define argData "code"
#define argBits "bits"
#define argRepeat "repeats"

#define _MY_VERSION_ "v0.6.0"

#if IR_LED != 1 // Disable debug output if the LED is on the TX (D1) pin.
// Let's use a larger than normal buffer so we can handle AirCon remote codes.
const uint16_t kCaptureBufferSize = 1024;
#if DECODE_AC
// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
// A value this large may swallow repeats of some protocols
const uint8_t kCaptureTimeout = 50;
#else // DECODE_AC
// Suits most messages, while not swallowing many repeats.
const uint8_t kCaptureTimeout = 15;
#endif // DECODE_AC
// Ignore unknown messages with <10 pulses
const uint16_t kMinUnknownSize = 20;

#define _MY_VERSION_ "v0.7.0"

// Disable debug output if any of the IR pins are on the TX (D1) pin.
#if (IR_LED != 1 && IR_RX != 1)
#undef DEBUG
#define DEBUG true // Change to 'false' to disable all serial output.
#else
Expand All @@ -187,6 +221,10 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries.
// Globals
ESP8266WebServer server(kHttpPort);
IRsend irsend = IRsend(IR_LED);
#ifdef IR_RX
IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true);
decode_results capture; // Somewhere to store inbound IR messages.
#endif // IR_RX
MDNSResponder mdns;
WiFiClient espClient;
WiFiManager wifiManager;
Expand All @@ -203,6 +241,11 @@ int8_t offset; // The calculated period offset for this chip and library.
#ifdef MQTT_ENABLE
String lastMqttCmd = "None";
uint32_t lastMqttCmdTime = 0;
#ifdef IR_RX
String lastIrReceived = "None";
uint32_t lastIrReceivedTime = 0;
uint32_t irRecvCounter = 0;
#endif // IR_RX


// MQTT client parameters
Expand Down Expand Up @@ -278,17 +321,28 @@ void handleRoot() {
"Period Offset: " + String(offset) + "us<br>"
"IR Lib Version: " _IRREMOTEESP8266_VERSION_ "<br>"
"ESP8266 Core Version: " + ESP.getCoreVersion() + "<br>"
"IR Send GPIO: " + String(IR_LED) + "<br>"
"Total send requests: " + String(sendReqCounter) + "<br>"
"Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") +
" <i>(" + timeSince(lastSendTime) + ")</i></p>"
" <i>(" + timeSince(lastSendTime) + ")</i><br>"
#ifdef IR_RX
"IR Recv GPIO: " + String(IR_RX) + "<br>"
"Total IR Received: " + String(irRecvCounter) + "<br>"
"Last IR Received: " + lastIrReceived +
" <i>(" + timeSince(lastIrReceivedTime) + ")</i><br>"
#endif // IR_RX
"</p>"
#ifdef MQTT_ENABLE
"<h4>MQTT Information</h4>"
"<p>Server: " MQTT_SERVER ":" + String(kMqttPort) + " <i>(" +
(mqtt_client.connected() ? "Connected" : "Disconnected") + ")</i><br>"
"Client id: " + mqtt_clientid + "<br>"
"Command topic: " MQTTcommand "<br>"
"Acknowledgements topic: " MQTTack "<br>"
"Last command seen: " +
#ifdef IR_RX
"IR Received topic: " MQTTrecv "<br>"
#endif // IR_RX
"Last MQTT command seen: " +
// lastMqttCmd is unescaped untrusted input.
// Avoid any possible HTML/XSS when displaying it.
(hasUnsafeHTMLChars(lastMqttCmd) ?
Expand Down Expand Up @@ -945,6 +999,13 @@ void setup_wifi() {
void setup(void) {
irsend.begin();
offset = irsend.calibrate();
#if IR_RX
#if DECODE_HASH
// Ignore messages with less than minimum on or off pulses.
irrecv.setUnknownThreshold(kMinUnknownSize);
#endif // DECODE_HASH
irrecv.enableIRIn(); // Start the receiver
#endif // IR_RX

#ifdef DEBUG
// Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use.
Expand Down Expand Up @@ -1054,7 +1115,7 @@ bool reconnect() {
#endif // MQTT_ENABLE

void loop(void) {
server.handleClient();
server.handleClient(); // Handle any web activity

#ifdef MQTT_ENABLE
// MQTT client connection management
Expand All @@ -1080,6 +1141,20 @@ void loop(void) {
mqtt_client.loop();
}
#endif // MQTT_ENABLE
#ifdef IR_RX
// Check if an IR code has been received via the IR RX module.
if (irrecv.decode(&capture)) {
lastIrReceivedTime = millis();
lastIrReceived = String(capture.decode_type) + "," +
resultToHexidecimal(&capture);
// If it isn't an AC code, add the bits.
if (!hasACState(capture.decode_type))
lastIrReceived += "," + String(capture.bits);
mqtt_client.publish(MQTTrecv, lastIrReceived.c_str());
irRecvCounter++;
debug("Incoming IR message sent to MQTT: " + lastIrReceived);
}
#endif // IR_RX
delay(100);
}

Expand Down
1 change: 1 addition & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ printState KEYWORD2
readbits KEYWORD2
renderTime KEYWORD2
reset KEYWORD2
resultToHexidecimal KEYWORD2
resultToHumanReadableBasic KEYWORD2
resultToSourceCode KEYWORD2
resultToTimingInfo KEYWORD2
Expand Down
33 changes: 23 additions & 10 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,28 @@ std::string resultToTimingInfo(const decode_results *results) {
return output;
}

// Convert the decode_results structure's value/state to simple hexadecimal.
//
#ifdef ARDUINO
String resultToHexidecimal(const decode_results *result) {
String output = "";
#else
std::string resultToHexidecimal(const decode_results *result) {
std::string output = "";
#endif
if (hasACState(result->decode_type)) {
#if DECODE_AC
for (uint16_t i = 0; result->bits > i * 8; i++) {
if (result->state[i] < 0x10) output += "0"; // Zero pad
output += uint64ToString(result->state[i], 16);
}
#endif // DECODE_AC
} else {
output += uint64ToString(result->value, 16);
}
return output;
}

// Dump out the decode_results structure.
//
#ifdef ARDUINO
Expand All @@ -318,16 +340,7 @@ std::string resultToHumanReadableBasic(const decode_results *results) {

// Show Code & length
output += "Code : ";
if (hasACState(results->decode_type)) {
#if DECODE_AC
for (uint16_t i = 0; results->bits > i * 8; i++) {
if (results->state[i] < 0x10) output += "0"; // Zero pad
output += uint64ToString(results->state[i], 16);
}
#endif // DECODE_AC
} else {
output += uint64ToString(results->value, 16);
}
output += resultToHexidecimal(results);
output += " (" + uint64ToString(results->bits) + " bits)\n";
return output;
}
Expand Down
2 changes: 2 additions & 0 deletions src/IRutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ void serialPrintUint64(uint64_t input, uint8_t base = 10);
String resultToSourceCode(const decode_results *results);
String resultToTimingInfo(const decode_results *results);
String resultToHumanReadableBasic(const decode_results *results);
String resultToHexidecimal(const decode_results *result);
#else
std::string uint64ToString(uint64_t input, uint8_t base = 10);
std::string typeToString(const decode_type_t protocol,
const bool isRepeat = false);
std::string resultToSourceCode(const decode_results *results);
std::string resultToTimingInfo(const decode_results *results);
std::string resultToHumanReadableBasic(const decode_results *results);
std::string resultToHexidecimal(const decode_results *result);
#endif
bool hasACState(const decode_type_t protocol);
uint16_t getCorrectedRawLength(const decode_results *results);
Expand Down

0 comments on commit 25dde27

Please sign in to comment.