IRMQTTServer.ino 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608
  1. /*
  2. * Send & receive arbitrary IR codes via a web server or MQTT.
  3. * Copyright David Conran 2016, 2017, 2018
  4. *
  5. * NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2) if
  6. * you want to send IR messages. See IR_LED below.
  7. * A compatible IR RX modules *MUST* be connected to ESP8266 GPIO14 (D5)
  8. * if you want to capture & decode IR nessages. See IR_RX below.
  9. *
  10. * WARN: This is very advanced & complicated example code. Not for beginners.
  11. * You are strongly suggested to try & look at other example code first.
  12. *
  13. * # Instructions
  14. *
  15. * ## Before First Boot (i.e. Compile time)
  16. * - Either:
  17. * o Set the MQTT_SERVER define below to the address of your MQTT server.
  18. * or
  19. * o Disable MQTT by commenting out the line "#define MQTT_ENABLE" down below.
  20. *
  21. * - Arduino IDE:
  22. * o Install the following libraries via Library Manager
  23. * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14)
  24. * - PubSubClient (https://pubsubclient.knolleary.net/)
  25. * o You MUST change <PubSubClient.h> to have the following (or larger) value:
  26. * #define MQTT_MAX_PACKET_SIZE 512
  27. * - PlatformIO IDE:
  28. * If you are using PlatformIO, this should already been done for you in
  29. * the accompanying platformio.ini file.
  30. *
  31. * ## First Boot (Initial setup)
  32. * The ESP8266 board will boot into the WiFiManager's AP mode.
  33. * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc.
  34. * Connect to that SSID. Then point your browser to http://192.168.4.1/ and
  35. * configure the ESP8266 to connect to your desired WiFi network.
  36. * It will remember the new WiFi connection details on next boot.
  37. * More information can be found here:
  38. * https://github.com/tzapu/WiFiManager#how-it-works
  39. *
  40. * If you need to reset the WiFi settings, visit:
  41. * http://<your_esp8266's_ip_address>/reset
  42. *
  43. * ## Normal Use (After setup)
  44. * Enter 'http://<your_esp8266's_ip_address/' in your browser & follow the
  45. * instructions there to send IR codes via HTTP/HTML.
  46. * You can send URLs like the following, with similar data type limitations as
  47. * the MQTT formating in the next section. e.g:
  48. * http://<your_esp8266's_ip_address>/ir?type=7&code=E0E09966
  49. * http://<your_esp8266's_ip_address>/ir?type=4&code=0xf50&bits=12
  50. * http://<your_esp8266's_ip_address>/ir?code=C1A2E21D&repeats=8&type=19
  51. * http://<your_esp8266's_ip_address>/ir?type=31&code=40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058
  52. * http://<your_esp8266's_ip_address>/ir?type=18&code=190B8050000000E0190B8070000010f0
  53. * http://<your_esp8266's_ip_address>/ir?repeats=1&type=25&code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40
  54. *
  55. * or
  56. *
  57. * Send a MQTT message to the topic 'ir_server/send' using the following
  58. * format (Order is important):
  59. * protocol_num,hexcode e.g. 7,E0E09966 which is Samsung(7), Power On code,
  60. * default bit size, default nr. of repeats.
  61. * protocol_num,hexcode,bits e.g. 4,f50,12 which is Sony(4), Power Off code,
  62. * 12 bits & default nr. of repeats.
  63. * protocol_num,hexcode,bits,repeats e.g. 19,C1A2E21D,0,8 which is
  64. * Sherwood(19), Vol Up, default bit size &
  65. * repeated 8 times.
  66. * 30,frequency,raw_string e.g. 30,38000,9000,4500,500,1500,500,750,500,750
  67. * Raw (30) @ 38kHz with a raw code of "9000,4500,500,1500,500,750,500,750"
  68. * 31,code_string e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058
  69. * GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up)
  70. * 25,Rrepeats,hex_code_string e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40
  71. * Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." (Sherwood Amp Tape Input)
  72. * ac_protocol_num,really_long_hexcode e.g. 18,190B8050000000E0190B8070000010F0
  73. * Kelvinator (18) Air Con on, Low Fan, 25 deg etc.
  74. * NOTE: Ensure you zero-pad to the correct number of
  75. * digits for the bit/byte size you want to send
  76. * as some A/C units have units have different
  77. * sized messages. e.g. Fujitsu A/C units.
  78. * In short:
  79. * No spaces after/before commas.
  80. * Values are comma separated.
  81. * The first value is always in Decimal.
  82. * For simple protocols, the next value (hexcode) is always hexadecimal.
  83. * The optional bit size is in decimal.
  84. *
  85. * Unix command line usage example:
  86. * # Install a MQTT client
  87. * $ sudo apt install mosquitto-clients
  88. * # Send a 32-bit NEC code of 0x1234abcd via MQTT.
  89. * $ mosquitto_pub -h 10.20.0.253 -t ir_server/send -m '3,1234abcd,32'
  90. *
  91. * This server will send (back) what ever IR message it just transmitted to
  92. * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works
  93. * for messages requested via MQTT or via HTTP.
  94. * Note: Other status messages are also sent to 'ir_server/sent' from time to
  95. * time.
  96. * Unix command line usage example:
  97. * # Listen to MQTT acknowledgements.
  98. * $ mosquitto_sub -h 10.20.0.253 -t ir_server/sent
  99. *
  100. * Incoming IR messages (from an IR remote control) will be transmitted to
  101. * the MQTT topic 'ir_server/received'. The MQTT message will be formatted
  102. * similar to what is required to for the 'sent' topic.
  103. * e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes
  104. * or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes
  105. * Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol.
  106. * You can't use that to recreate/resend an IR message. It's only for
  107. * matching purposes and shouldn't be trusted.
  108. *
  109. * Unix command line usage example:
  110. * # Listen via MQTT for IR messages captured by this server.
  111. * $ mosquitto_sub -h 10.20.0.253 -t ir_server/received
  112. *
  113. * If DEBUG is turned on, there is additional information printed on the Serial
  114. * Port.
  115. *
  116. * ## Updates
  117. * You can upload new firmware over the air (OTA) via the form on the device's
  118. * main page. No need to connect to the device again via USB. \o/
  119. * Your WiFi settings should be remembered between updates. \o/ \o/
  120. *
  121. * Copyright Notice:
  122. * Code for this has been borrowed from lots of other OpenSource projects &
  123. * resources. I'm *NOT* claiming complete Copyright ownership of all the code.
  124. * Likewise, feel free to borrow from this as much as you want.
  125. */
  126. #define MQTT_ENABLE // Comment this out if you don't want to use MQTT at all.
  127. #include <Arduino.h>
  128. #include <ESP8266WiFi.h>
  129. #include <WiFiClient.h>
  130. #include <DNSServer.h>
  131. #include <ESP8266WebServer.h>
  132. #include <WiFiManager.h>
  133. #include <ESP8266mDNS.h>
  134. #include <IRremoteESP8266.h>
  135. #include <IRrecv.h>
  136. #include <IRsend.h>
  137. #include <IRutils.h>
  138. #ifdef MQTT_ENABLE
  139. // --------------------------------------------------------------------
  140. // * * * IMPORTANT * * *
  141. // You must change <PubSubClient.h> to have the following value.
  142. // #define MQTT_MAX_PACKET_SIZE 512
  143. // --------------------------------------------------------------------
  144. #include <PubSubClient.h>
  145. #endif // MQTT_ENABLE
  146. #include <algorithm>
  147. #include <string>
  148. // Configuration parameters
  149. // GPIO the IR LED is connected to/controlled by. GPIO 4 = D2.
  150. #define IR_LED 4
  151. // define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7.
  152. //
  153. // GPIO the IR RX module is connected to/controlled by. GPIO 14 = D5.
  154. // Comment this out to disable receiving/decoding IR messages entirely.
  155. #define IR_RX 14
  156. const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on.
  157. // Name of the device you want in mDNS.
  158. // NOTE: Changing this will change the MQTT path too unless you override it
  159. // via MQTTprefix below.
  160. #define HOSTNAME "ir_server"
  161. // We obtain our network config via DHCP by default but allow an easy way to
  162. // use a static IP config.
  163. #define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP.
  164. #if USE_STATIC_IP
  165. const IPAddress kIPAddress = IPAddress(10, 0, 1, 78);
  166. const IPAddress kGateway = IPAddress(10, 0, 1, 1);
  167. const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0);
  168. #endif // USE_STATIC_IP
  169. #ifdef MQTT_ENABLE
  170. // Address of your MQTT server.
  171. #define MQTT_SERVER "10.20.0.253" // <=- CHANGE ME
  172. const uint16_t kMqttPort = 1883; // Default port used by MQTT servers.
  173. // Set if your MQTT server requires a Username & Password to connect.
  174. const char* mqtt_user = "";
  175. const char* mqtt_password = "";
  176. const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries.
  177. #define MQTTprefix HOSTNAME // Change this if you want the MQTT topic to be
  178. // independent of the hostname.
  179. #define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on
  180. #define MQTTcommand MQTTprefix "/send" // Topic we get new commands from.
  181. #define MQTTrecv MQTTprefix "/received" // Topic we send received IRs to.
  182. #endif // MQTT_ENABLE
  183. // HTML arguments we will parse for IR code information.
  184. #define argType "type"
  185. #define argData "code"
  186. #define argBits "bits"
  187. #define argRepeat "repeats"
  188. // Let's use a larger than normal buffer so we can handle AirCon remote codes.
  189. const uint16_t kCaptureBufferSize = 1024;
  190. #if DECODE_AC
  191. // Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
  192. // A value this large may swallow repeats of some protocols
  193. const uint8_t kCaptureTimeout = 50;
  194. #else // DECODE_AC
  195. // Suits most messages, while not swallowing many repeats.
  196. const uint8_t kCaptureTimeout = 15;
  197. #endif // DECODE_AC
  198. // Ignore unknown messages with <10 pulses
  199. const uint16_t kMinUnknownSize = 20;
  200. #define _MY_VERSION_ "v0.7.0"
  201. // Disable debug output if any of the IR pins are on the TX (D1) pin.
  202. #if (IR_LED != 1 && IR_RX != 1)
  203. #undef DEBUG
  204. #define DEBUG true // Change to 'false' to disable all serial output.
  205. #else
  206. #undef DEBUG
  207. #define DEBUG false
  208. #endif
  209. // NOTE: Make sure you set your Serial Monitor to the same speed.
  210. #define BAUD_RATE 115200 // Serial port Baud rate.
  211. // Globals
  212. ESP8266WebServer server(kHttpPort);
  213. IRsend irsend = IRsend(IR_LED);
  214. #ifdef IR_RX
  215. IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true);
  216. decode_results capture; // Somewhere to store inbound IR messages.
  217. #endif // IR_RX
  218. MDNSResponder mdns;
  219. WiFiClient espClient;
  220. WiFiManager wifiManager;
  221. uint16_t *codeArray;
  222. uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number
  223. bool boot = true;
  224. bool ir_lock = false; // Primitive locking for gating the IR LED.
  225. uint32_t sendReqCounter = 0;
  226. bool lastSendSucceeded = false; // Store the success status of the last send.
  227. uint32_t lastSendTime = 0;
  228. int8_t offset; // The calculated period offset for this chip and library.
  229. #ifdef MQTT_ENABLE
  230. String lastMqttCmd = "None";
  231. uint32_t lastMqttCmdTime = 0;
  232. uint32_t lastConnectedTime = 0;
  233. uint32_t lastDisconnectedTime = 0;
  234. uint32_t mqttDisconnectCounter = 0;
  235. bool wasConnected = true;
  236. #ifdef IR_RX
  237. String lastIrReceived = "None";
  238. uint32_t lastIrReceivedTime = 0;
  239. uint32_t irRecvCounter = 0;
  240. #endif // IR_RX
  241. // MQTT client parameters
  242. void callback(char* topic, byte* payload, unsigned int length);
  243. PubSubClient mqtt_client(MQTT_SERVER, kMqttPort, callback, espClient);
  244. // Create a unique MQTT client id.
  245. String mqtt_clientid = MQTTprefix + String(ESP.getChipId(), HEX);
  246. #endif // MQTT_ENABLE
  247. // Debug messages get sent to the serial port.
  248. void debug(String str) {
  249. #ifdef DEBUG
  250. uint32_t now = millis();
  251. Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str());
  252. #endif // DEBUG
  253. }
  254. String timeSince(uint32_t const start) {
  255. if (start == 0)
  256. return "Never";
  257. uint32_t diff = 0;
  258. uint32_t now = millis();
  259. if (start < now)
  260. diff = now - start;
  261. else
  262. diff = UINT32_MAX - start + now;
  263. diff /= 1000; // Convert to seconds.
  264. if (diff == 0) return "Now";
  265. // Note: millis() can only count up to 45 days, so uint8_t is safe.
  266. uint8_t days = diff / (60 * 60 * 24);
  267. uint8_t hours = (diff / (60 * 60)) % 24;
  268. uint8_t minutes = (diff / 60) % 60;
  269. uint8_t seconds = diff % 60;
  270. String result = "";
  271. if (days)
  272. result += String(days) + " day";
  273. if (days > 1) result += "s";
  274. if (hours)
  275. result += " " + String(hours) + " hour";
  276. if (hours > 1) result += "s";
  277. if (minutes)
  278. result += " " + String(minutes) + " minute";
  279. if (minutes > 1) result += "s";
  280. if (seconds)
  281. result += " " + String(seconds) + " second";
  282. if (seconds > 1) result += "s";
  283. result.trim();
  284. return result + " ago";
  285. }
  286. // Quick and dirty check for any unsafe chars in a string
  287. // that may cause HTML shenanigans. e.g. An XSS.
  288. bool hasUnsafeHTMLChars(String input) {
  289. static char unsafe[] = "';!-\"<>=&{}()";
  290. for (uint8_t i = 0; unsafe[i]; i++)
  291. if (input.indexOf(unsafe[i]) != -1) return true;
  292. return false;
  293. }
  294. // Root web page with example usage etc.
  295. void handleRoot() {
  296. server.send(200, "text/html",
  297. "<html><head><title>IR MQTT server</title></head>"
  298. "<body>"
  299. "<center><h1>ESP8266 IR MQTT Server</h1></center>"
  300. "<br><hr>"
  301. "<h3>Information</h3>"
  302. "<p>IP address: " + WiFi.localIP().toString() + "<br>"
  303. "Booted: " + timeSince(1) + "<br>" +
  304. "Version: " _MY_VERSION_ "<br>"
  305. "Period Offset: " + String(offset) + "us<br>"
  306. "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "<br>"
  307. "ESP8266 Core Version: " + ESP.getCoreVersion() + "<br>"
  308. "IR Send GPIO: " + String(IR_LED) + "<br>"
  309. "Total send requests: " + String(sendReqCounter) + "<br>"
  310. "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") +
  311. " <i>(" + timeSince(lastSendTime) + ")</i><br>"
  312. #ifdef IR_RX
  313. "IR Recv GPIO: " + String(IR_RX) + "<br>"
  314. "Total IR Received: " + String(irRecvCounter) + "<br>"
  315. "Last IR Received: " + lastIrReceived +
  316. " <i>(" + timeSince(lastIrReceivedTime) + ")</i><br>"
  317. #endif // IR_RX
  318. "</p>"
  319. #ifdef MQTT_ENABLE
  320. "<h4>MQTT Information</h4>"
  321. "<p>Server: " MQTT_SERVER ":" + String(kMqttPort) + " <i>(" +
  322. (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime)
  323. : "Disconnected " + timeSince(lastConnectedTime)) +
  324. ")</i><br>"
  325. "Disconnections: " + String(mqttDisconnectCounter - 1) + "<br>"
  326. "Client id: " + mqtt_clientid + "<br>"
  327. "Command topic: " MQTTcommand "<br>"
  328. "Acknowledgements topic: " MQTTack "<br>"
  329. #ifdef IR_RX
  330. "IR Received topic: " MQTTrecv "<br>"
  331. #endif // IR_RX
  332. "Last MQTT command seen: " +
  333. // lastMqttCmd is unescaped untrusted input.
  334. // Avoid any possible HTML/XSS when displaying it.
  335. (hasUnsafeHTMLChars(lastMqttCmd) ?
  336. "<i>Contains unsafe HTML characters</i>" : lastMqttCmd) +
  337. " <i>(" + timeSince(lastMqttCmdTime) + ")</i></p>"
  338. #endif // MQTT_ENABLE
  339. "<br><hr>"
  340. "<h3>Hardcoded examples</h3>"
  341. "<p><a href=\"ir?code=38000,1,69,341,171,21,64,21,64,21,21,21,21,21,21,21,"
  342. "21,21,21,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,64,21,21,21,64,"
  343. "21,21,21,21,21,21,21,64,21,21,21,21,21,21,21,21,21,64,21,64,21,64,21,"
  344. "21,21,64,21,64,21,64,21,1600,341,85,21,3647&type=31\">"
  345. "Sherwood Amp On (GlobalCache)</a></p>"
  346. "<p><a href=\"ir?code=38000,8840,4446,546,1664,546,1664,546,546,546,546,"
  347. "546,546,546,546,546,546,546,1664,546,1664,546,546,546,1664,546,546,"
  348. "546,546,546,546,546,1664,546,546,546,1664,546,546,546,1664,546,1664,"
  349. "546,1664,546,546,546,546,546,546,546,546,546,1664,546,546,546,546,546,"
  350. "546,546,1664,546,1664,546,1664,546,41600,8840,2210,546&type=30\">"
  351. "Sherwood Amp Off (Raw)</a></p>"
  352. "<p><a href=\"ir?code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040"
  353. ",0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040"
  354. ",0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015"
  355. ",0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015"
  356. ",0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040"
  357. ",0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40"
  358. "&type=25&repeats=1\">"
  359. "Sherwood Amp Input TAPE (Pronto)</a></p>"
  360. "<p><a href=\"ir?type=7&code=E0E09966\">TV on (Samsung)</a></p>"
  361. "<p><a href=\"ir?type=4&code=0xf50&bits=12\">Power Off (Sony 12bit)</a></p>"
  362. "<br><hr>"
  363. "<h3>Send a simple IR message</h3><p>"
  364. "<form method='POST' action='/ir' enctype='multipart/form-data'>"
  365. "Type: "
  366. "<select name='type'>"
  367. "<option value='9'>Aiwa RC T501</option>"
  368. "<option value='37'>Carrier AC</option>"
  369. "<option value='15'>Coolix</option>"
  370. "<option value='17'>Denon</option>"
  371. "<option value='13'>Dish</option>"
  372. "<option value='43'>GICable</option>"
  373. "<option value='6'>JVC</option>"
  374. "<option value='36'>Lasertag</option>"
  375. "<option value='10'>LG</option>"
  376. "<option value='51'>LG2</option>"
  377. "<option value='47'>Lutron</option>"
  378. "<option value='35'>MagiQuest</option>"
  379. "<option value='34'>Midea</option>"
  380. "<option value='12'>Mitsubishi</option>"
  381. "<option value='39'>Mitsubishi2</option>"
  382. "<option selected='selected' value='3'>NEC</option>" // Default
  383. "<option value='29'>Nikai</option>"
  384. "<option value='5'>Panasonic</option>"
  385. "<option value='50'>Pioneer</option>"
  386. "<option value='1'>RC-5</option>"
  387. "<option value='23'>RC-5X</option>"
  388. "<option value='2'>RC-6</option>"
  389. "<option value='21'>RC-MM</option>"
  390. "<option value='7'>Samsung</option>"
  391. "<option value='11'>Sanyo</option>"
  392. "<option value='22'>Sanyo LC7461</option>"
  393. "<option value='14'>Sharp</option>"
  394. "<option value='19'>Sherwood</option>"
  395. "<option value='4'>Sony</option>"
  396. "<option value='8'>Whynter</option>"
  397. "</select>"
  398. " Code: 0x<input type='text' name='code' min='0' value='0' size='16'"
  399. " maxlength='16'>"
  400. " Bit size: "
  401. "<select name='bits'>"
  402. "<option selected='selected' value='0'>Default</option>" // Default
  403. // Common bit length options for most protocols.
  404. "<option value='12'>12</option>"
  405. "<option value='13'>13</option>"
  406. "<option value='14'>14</option>"
  407. "<option value='15'>15</option>"
  408. "<option value='16'>16</option>"
  409. "<option value='20'>20</option>"
  410. "<option value='21'>21</option>"
  411. "<option value='24'>24</option>"
  412. "<option value='28'>28</option>"
  413. "<option value='32'>32</option>"
  414. "<option value='35'>35</option>"
  415. "<option value='36'>36</option>"
  416. "<option value='48'>48</option>"
  417. "<option value='56'>56</option>"
  418. "</select>"
  419. " Repeats: <input type='number' name='repeats' min='0' max='99' value='0'"
  420. "size='2' maxlength='2'>"
  421. " <input type='submit' value='Send IR'>"
  422. "</form>"
  423. "<br><hr>"
  424. "<h3>Send an IRremote Raw IR message</h3><p>"
  425. "<form method='POST' action='/ir' enctype='multipart/form-data'>"
  426. "<input type='hidden' name='type' value='30'>"
  427. "String: (freq,array data) <input type='text' name='code' size='132'"
  428. " value='38000,4420,4420,520,1638,520,1638,520,1638,520,520,520,520,520,"
  429. "520,520,520,520,520,520,1638,520,1638,520,1638,520,520,520,"
  430. "520,520,520,520,520,520,520,520,520,520,1638,520,520,520,520,520,"
  431. "520,520,520,520,520,520,520,520,1638,520,520,520,1638,520,1638,520,"
  432. "1638,520,1638,520,1638,520,1638,520'>"
  433. " <input type='submit' value='Send Raw'>"
  434. "</form>"
  435. "<br><hr>"
  436. "<h3>Send a <a href='https://irdb.globalcache.com/'>GlobalCache</a>"
  437. " IR message</h3><p>"
  438. "<form method='POST' action='/ir' enctype='multipart/form-data'>"
  439. "<input type='hidden' name='type' value='31'>"
  440. "String: 1:1,1,<input type='text' name='code' size='132'"
  441. " value='38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,"
  442. "20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,"
  443. "20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,63,20,"
  444. "63,20,63,20,63,20,1798'>"
  445. " <input type='submit' value='Send GlobalCache'>"
  446. "</form>"
  447. "<br><hr>"
  448. "<h3>Send a <a href='http://www.remotecentral.com/cgi-bin/files/rcfiles.cgi"
  449. "?area=pronto&db=discrete'>Pronto code</a> IR message</h3><p>"
  450. "<form method='POST' action='/ir' enctype='multipart/form-data'>"
  451. "<input type='hidden' name='type' value='25'>"
  452. "String (comma separated): <input type='text' name='code' size='132'"
  453. " value='0000,0067,0000,0015,0060,0018,0018,0018,0030,0018,0030,0018,"
  454. "0030,0018,0018,0018,0030,0018,0018,0018,0018,0018,0030,0018,0018,"
  455. "0018,0030,0018,0030,0018,0030,0018,0018,0018,0018,0018,0030,0018,"
  456. "0018,0018,0018,0018,0030,0018,0018,03f6'>"
  457. " Repeats: <input type='number' name='repeats' min='0' max='99' value='0'"
  458. "size='2' maxlength='2'>"
  459. " <input type='submit' value='Send Pronto'>"
  460. "</form>"
  461. "<br><hr>"
  462. "<h3>Send an Air Conditioner IR message</h3><p>"
  463. "<form method='POST' action='/ir' enctype='multipart/form-data'>"
  464. "Type: "
  465. "<select name='type'>"
  466. "<option value='27'>Argo</option>"
  467. "<option value='16'>Daikin</option>"
  468. "<option value='48'>Electra</option>"
  469. "<option value='33'>Fujitsu</option>"
  470. "<option value='24'>Gree</option>"
  471. "<option value='38'>Haier (9 bytes)</option>"
  472. "<option value='44'>Haier (14 bytes/YR-W02)</option>"
  473. "<option value='40'>Hitachi (28 bytes)</option>"
  474. "<option value='41'>Hitachi1 (13 bytes)</option>"
  475. "<option value='42'>Hitachi2 (53 bytes)</option>"
  476. "<option selected='selected' value='18'>Kelvinator</option>" // Default
  477. "<option value='20'>Mitsubishi</option>"
  478. "<option value='52'>MWM</option>"
  479. "<option value='46'>Samsung</option>"
  480. "<option value='32'>Toshiba</option>"
  481. "<option value='28'>Trotec</option>"
  482. "<option value='45'>Whirlpool</option>"
  483. "</select>"
  484. " State code: 0x"
  485. "<input type='text' name='code' size='" + String(kStateSizeMax * 2) +
  486. "' maxlength='" + String(kStateSizeMax * 2) + "'"
  487. " value='190B8050000000E0190B8070000010F0'>"
  488. " <input type='submit' value='Send A/C State'>"
  489. "</form>"
  490. "<br><hr>"
  491. "<h3>Update IR Server firmware</h3><p>"
  492. "<b><mark>Warning:</mark></b><br> "
  493. "<i>Updating your firmware may screw up your access to the device. "
  494. "If you are going to use this, know what you are doing first "
  495. "(and you probably do).</i><br>"
  496. "<form method='POST' action='/update' enctype='multipart/form-data'>"
  497. "Firmware to upload: <input type='file' name='update'>"
  498. "<input type='submit' value='Update'>"
  499. "</form>"
  500. "</body></html>");
  501. }
  502. // Reset web page
  503. void handleReset() {
  504. server.send(200, "text/html",
  505. "<html><head><title>Reset Config</title></head>"
  506. "<body>"
  507. "<h1>Resetting the WiFiManager config back to defaults.</h1>"
  508. "<p>Device restarting. Try connecting in a few seconds.</p>"
  509. "</body></html>");
  510. // Do the reset.
  511. wifiManager.resetSettings();
  512. delay(10);
  513. ESP.restart();
  514. delay(1000);
  515. }
  516. // Parse an Air Conditioner A/C Hex String/code and send it.
  517. // Args:
  518. // irType: Nr. of the protocol we need to send.
  519. // str: A hexadecimal string containing the state to be sent.
  520. // Returns:
  521. // bool: Successfully sent or not.
  522. bool parseStringAndSendAirCon(const uint16_t irType, const String str) {
  523. uint8_t strOffset = 0;
  524. uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0.
  525. uint16_t stateSize = 0;
  526. if (str.startsWith("0x") || str.startsWith("0X"))
  527. strOffset = 2;
  528. // Calculate how many hexadecimal characters there are.
  529. uint16_t inputLength = str.length() - strOffset;
  530. if (inputLength == 0) {
  531. debug("Zero length AirCon code encountered. Ignored.");
  532. return false; // No input. Abort.
  533. }
  534. switch (irType) { // Get the correct state size for the protocol.
  535. case KELVINATOR:
  536. stateSize = kKelvinatorStateLength;
  537. break;
  538. case TOSHIBA_AC:
  539. stateSize = kToshibaACStateLength;
  540. break;
  541. case DAIKIN:
  542. stateSize = kDaikinStateLength;
  543. break;
  544. case ELECTRA_AC:
  545. stateSize = kElectraAcStateLength;
  546. break;
  547. case MITSUBISHI_AC:
  548. stateSize = kMitsubishiACStateLength;
  549. break;
  550. case PANASONIC_AC:
  551. stateSize = kPanasonicAcStateLength;
  552. break;
  553. case TROTEC:
  554. stateSize = kTrotecStateLength;
  555. break;
  556. case ARGO:
  557. stateSize = kArgoStateLength;
  558. break;
  559. case GREE:
  560. stateSize = kGreeStateLength;
  561. break;
  562. case FUJITSU_AC:
  563. // Fujitsu has four distinct & different size states, so make a best guess
  564. // which one we are being presented with based on the number of
  565. // hexadecimal digits provided. i.e. Zero-pad if you need to to get
  566. // the correct length/byte size.
  567. stateSize = inputLength / 2; // Every two hex chars is a byte.
  568. // Use at least the minimum size.
  569. stateSize = std::max(stateSize,
  570. (uint16_t) (kFujitsuAcStateLengthShort - 1));
  571. // If we think it isn't a "short" message.
  572. if (stateSize > kFujitsuAcStateLengthShort)
  573. // Then it has to be at least the smaller version of the "normal" size.
  574. stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1));
  575. // Lastly, it should never exceed the maximum "normal" size.
  576. stateSize = std::min(stateSize, kFujitsuAcStateLength);
  577. break;
  578. case HAIER_AC:
  579. stateSize = kHaierACStateLength;
  580. break;
  581. case HAIER_AC_YRW02:
  582. stateSize = kHaierACYRW02StateLength;
  583. break;
  584. case HITACHI_AC:
  585. stateSize = kHitachiAcStateLength;
  586. break;
  587. case HITACHI_AC1:
  588. stateSize = kHitachiAc1StateLength;
  589. break;
  590. case HITACHI_AC2:
  591. stateSize = kHitachiAc2StateLength;
  592. break;
  593. case WHIRLPOOL_AC:
  594. stateSize = kWhirlpoolAcStateLength;
  595. break;
  596. case SAMSUNG_AC:
  597. // Samsung has two distinct & different size states, so make a best guess
  598. // which one we are being presented with based on the number of
  599. // hexadecimal digits provided. i.e. Zero-pad if you need to to get
  600. // the correct length/byte size.
  601. stateSize = inputLength / 2; // Every two hex chars is a byte.
  602. // Use at least the minimum size.
  603. stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength));
  604. // If we think it isn't a "normal" message.
  605. if (stateSize > kSamsungAcStateLength)
  606. // Then it probably the extended size.
  607. stateSize = std::max(stateSize,
  608. (uint16_t) (kSamsungAcExtendedStateLength));
  609. // Lastly, it should never exceed the maximum "extended" size.
  610. stateSize = std::min(stateSize, kSamsungAcExtendedStateLength);
  611. break;
  612. case MWM:
  613. // MWM has variable size states, so make a best guess
  614. // which one we are being presented with based on the number of
  615. // hexadecimal digits provided. i.e. Zero-pad if you need to to get
  616. // the correct length/byte size.
  617. stateSize = inputLength / 2; // Every two hex chars is a byte.
  618. // Use at least the minimum size.
  619. stateSize = std::max(stateSize, (uint16_t) 3);
  620. // Cap the maximum size.
  621. stateSize = std::min(stateSize, kStateSizeMax);
  622. break;
  623. default: // Not a protocol we expected. Abort.
  624. debug("Unexpected AirCon protocol detected. Ignoring.");
  625. return false;
  626. }
  627. if (inputLength > stateSize * 2) {
  628. debug("AirCon code to large for the given protocol.");
  629. return false;
  630. }
  631. // Ptr to the least significant byte of the resulting state for this protocol.
  632. uint8_t *statePtr = &state[stateSize - 1];
  633. // Convert the string into a state array of the correct length.
  634. for (uint16_t i = 0; i < inputLength; i++) {
  635. // Grab the next least sigificant hexadecimal digit from the string.
  636. uint8_t c = tolower(str[inputLength + strOffset - i - 1]);
  637. if (isxdigit(c)) {
  638. if (isdigit(c))
  639. c -= '0';
  640. else
  641. c = c - 'a' + 10;
  642. } else {
  643. debug("Aborting! Non-hexadecimal char found in AirCon state: " + str);
  644. return false;
  645. }
  646. if (i % 2 == 1) { // Odd: Upper half of the byte.
  647. *statePtr += (c << 4);
  648. statePtr--; // Advance up to the next least significant byte of state.
  649. } else { // Even: Lower half of the byte.
  650. *statePtr = c;
  651. }
  652. }
  653. // Make the appropriate call for the protocol type.
  654. switch (irType) {
  655. #if SEND_KELVINATOR
  656. case KELVINATOR:
  657. irsend.sendKelvinator(reinterpret_cast<uint8_t *>(state));
  658. break;
  659. #endif
  660. #if SEND_TOSHIBA_AC
  661. case TOSHIBA_AC:
  662. irsend.sendToshibaAC(reinterpret_cast<uint8_t *>(state));
  663. break;
  664. #endif
  665. #if SEND_DAIKIN
  666. case DAIKIN:
  667. irsend.sendDaikin(reinterpret_cast<uint8_t *>(state));
  668. break;
  669. #endif
  670. #if MITSUBISHI_AC
  671. case MITSUBISHI_AC:
  672. irsend.sendMitsubishiAC(reinterpret_cast<uint8_t *>(state));
  673. break;
  674. #endif
  675. #if SEND_TROTEC
  676. case TROTEC:
  677. irsend.sendTrotec(reinterpret_cast<uint8_t *>(state));
  678. break;
  679. #endif
  680. #if SEND_ARGO
  681. case ARGO:
  682. irsend.sendArgo(reinterpret_cast<uint8_t *>(state));
  683. break;
  684. #endif
  685. #if SEND_GREE
  686. case GREE:
  687. irsend.sendGree(reinterpret_cast<uint8_t *>(state));
  688. break;
  689. #endif
  690. #if SEND_FUJITSU_AC
  691. case FUJITSU_AC:
  692. irsend.sendFujitsuAC(reinterpret_cast<uint8_t *>(state), stateSize);
  693. break;
  694. #endif
  695. #if SEND_HAIER_AC
  696. case HAIER_AC:
  697. irsend.sendHaierAC(reinterpret_cast<uint8_t *>(state));
  698. break;
  699. #endif
  700. #if SEND_HAIER_AC_YRW02
  701. case HAIER_AC_YRW02:
  702. irsend.sendHaierACYRW02(reinterpret_cast<uint8_t *>(state));
  703. break;
  704. #endif
  705. #if SEND_HITACHI_AC
  706. case HITACHI_AC:
  707. irsend.sendHitachiAC(reinterpret_cast<uint8_t *>(state));
  708. break;
  709. #endif
  710. #if SEND_HITACHI_AC1
  711. case HITACHI_AC1:
  712. irsend.sendHitachiAC1(reinterpret_cast<uint8_t *>(state));
  713. break;
  714. #endif
  715. #if SEND_HITACHI_AC2
  716. case HITACHI_AC2:
  717. irsend.sendHitachiAC2(reinterpret_cast<uint8_t *>(state));
  718. break;
  719. #endif
  720. #if SEND_WHIRLPOOL_AC
  721. case WHIRLPOOL_AC:
  722. irsend.sendWhirlpoolAC(reinterpret_cast<uint8_t *>(state));
  723. break;
  724. #endif
  725. #if SEND_SAMSUNG_AC
  726. case SAMSUNG_AC:
  727. irsend.sendSamsungAC(reinterpret_cast<uint8_t *>(state), stateSize);
  728. break;
  729. #endif
  730. #if SEND_ELECTRA_AC
  731. case ELECTRA_AC:
  732. irsend.sendElectraAC(reinterpret_cast<uint8_t *>(state));
  733. break;
  734. #endif
  735. #if SEND_PANASONIC_AC
  736. case PANASONIC_AC:
  737. irsend.sendPanasonicAC(reinterpret_cast<uint8_t *>(state));
  738. break;
  739. #endif
  740. #if SEND_MWM_
  741. case MWM:
  742. irsend.sendMWM(reinterpret_cast<uint8_t *>(state), stateSize);
  743. break;
  744. #endif
  745. default:
  746. debug("Unexpected AirCon type in send request. Not sent.");
  747. return false;
  748. }
  749. return true; // We were successful as far as we can tell.
  750. }
  751. // Count how many values are in the String.
  752. // Args:
  753. // str: String containing the values.
  754. // sep: Character that separates the values.
  755. // Returns:
  756. // The number of values found in the String.
  757. uint16_t countValuesInStr(const String str, char sep) {
  758. int16_t index = -1;
  759. uint16_t count = 1;
  760. do {
  761. index = str.indexOf(sep, index + 1);
  762. count++;
  763. } while (index != -1);
  764. return count;
  765. }
  766. // Dynamically allocate an array of uint16_t's.
  767. // Args:
  768. // size: Nr. of uint16_t's need to be in the new array.
  769. // Returns:
  770. // A Ptr to the new array. Restarts the ESP8266 if it fails.
  771. uint16_t * newCodeArray(const uint16_t size) {
  772. uint16_t *result;
  773. result = reinterpret_cast<uint16_t*>(malloc(size * sizeof(uint16_t)));
  774. // Check we malloc'ed successfully.
  775. if (result == NULL) { // malloc failed, so give up.
  776. Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n",
  777. size * sizeof(uint16_t), ESP.getFreeHeap());
  778. Serial.println("Giving up & forcing a reboot.");
  779. ESP.restart(); // Reboot.
  780. delay(500); // Wait for the restart to happen.
  781. return result; // Should never get here, but just in case.
  782. }
  783. return result;
  784. }
  785. #if SEND_GLOBALCACHE
  786. // Parse a GlobalCache String/code and send it.
  787. // Args:
  788. // str: A GlobalCache formatted String of comma separated numbers.
  789. // e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,
  790. // 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,
  791. // 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,
  792. // 63,20,63,20,63,20,63,20,1798"
  793. // Note: The leading "1:1,1," of normal GC codes should be removed.
  794. // Returns:
  795. // bool: Successfully sent or not.
  796. bool parseStringAndSendGC(const String str) {
  797. uint16_t count;
  798. uint16_t *code_array;
  799. String tmp_str;
  800. // Remove the leading "1:1,1," if present.
  801. if (str.startsWith("1:1,1,"))
  802. tmp_str = str.substring(6);
  803. else
  804. tmp_str = str;
  805. // Find out how many items there are in the string.
  806. count = countValuesInStr(tmp_str, ',');
  807. // Now we know how many there are, allocate the memory to store them all.
  808. code_array = newCodeArray(count);
  809. // Now convert the strings to integers and place them in code_array.
  810. count = 0;
  811. uint16_t start_from = 0;
  812. int16_t index = -1;
  813. do {
  814. index = tmp_str.indexOf(',', start_from);
  815. code_array[count] = tmp_str.substring(start_from, index).toInt();
  816. start_from = index + 1;
  817. count++;
  818. } while (index != -1);
  819. irsend.sendGC(code_array, count); // All done. Send it.
  820. free(code_array); // Free up the memory allocated.
  821. if (count > 0)
  822. return true; // We sent something.
  823. return false; // We probably didn't.
  824. }
  825. #endif // SEND_GLOBALCACHE
  826. #if SEND_PRONTO
  827. // Parse a Pronto Hex String/code and send it.
  828. // Args:
  829. // str: A comma-separated String of nr. of repeats, then hexadecimal numbers.
  830. // e.g. "R1,0000,0067,0000,0015,0060,0018,0018,0018,0030,0018,0030,0018,
  831. // 0030,0018,0018,0018,0030,0018,0018,0018,0018,0018,0030,0018,
  832. // 0018,0018,0030,0018,0030,0018,0030,0018,0018,0018,0018,0018,
  833. // 0030,0018,0018,0018,0018,0018,0030,0018,0018,03f6"
  834. // or
  835. // "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value
  836. // Requires at least kProntoMinLength comma-separated values.
  837. // sendPronto() only supports raw pronto code types, thus so does this.
  838. // repeats: Nr. of times the message is to be repeated.
  839. // This value is ignored if an embeddd repeat is found in str.
  840. // Returns:
  841. // bool: Successfully sent or not.
  842. bool parseStringAndSendPronto(const String str, uint16_t repeats) {
  843. uint16_t count;
  844. uint16_t *code_array;
  845. int16_t index = -1;
  846. uint16_t start_from = 0;
  847. // Find out how many items there are in the string.
  848. count = countValuesInStr(str, ',');
  849. // Check if we have the optional embedded repeats value in the code string.
  850. if (str.startsWith("R") || str.startsWith("r")) {
  851. // Grab the first value from the string, as it is the nr. of repeats.
  852. index = str.indexOf(',', start_from);
  853. repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'.
  854. start_from = index + 1;
  855. count--; // We don't count the repeats value as part of the code array.
  856. }
  857. // We need at least kProntoMinLength values for the code part.
  858. if (count < kProntoMinLength) return false;
  859. // Now we know how many there are, allocate the memory to store them all.
  860. code_array = newCodeArray(count);
  861. // Rest of the string are values for the code array.
  862. // Now convert the hex strings to integers and place them in code_array.
  863. count = 0;
  864. do {
  865. index = str.indexOf(',', start_from);
  866. // Convert the hexadecimal value string to an unsigned integer.
  867. code_array[count] = strtoul(str.substring(start_from, index).c_str(),
  868. NULL, 16);
  869. start_from = index + 1;
  870. count++;
  871. } while (index != -1);
  872. irsend.sendPronto(code_array, count, repeats); // All done. Send it.
  873. free(code_array); // Free up the memory allocated.
  874. if (count > 0)
  875. return true; // We sent something.
  876. return false; // We probably didn't.
  877. }
  878. #endif // SEND_PRONTO
  879. #if SEND_RAW
  880. // Parse an IRremote Raw Hex String/code and send it.
  881. // Args:
  882. // str: A comma-separated String containing the freq and raw IR data.
  883. // e.g. "38000,9000,4500,600,1450,600,900,650,1500,..."
  884. // Requires at least two comma-separated values.
  885. // First value is the transmission frequency in Hz or kHz.
  886. // Returns:
  887. // bool: Successfully sent or not.
  888. bool parseStringAndSendRaw(const String str) {
  889. uint16_t count;
  890. uint16_t freq = 38000; // Default to 38kHz.
  891. uint16_t *raw_array;
  892. // Find out how many items there are in the string.
  893. count = countValuesInStr(str, ',');
  894. // We expect the frequency as the first comma separated value, so we need at
  895. // least two values. If not, bail out.
  896. if (count < 2) return false;
  897. count--; // We don't count the frequency value as part of the raw array.
  898. // Now we know how many there are, allocate the memory to store them all.
  899. raw_array = newCodeArray(count);
  900. // Grab the first value from the string, as it is the frequency.
  901. int16_t index = str.indexOf(',', 0);
  902. freq = str.substring(0, index).toInt();
  903. uint16_t start_from = index + 1;
  904. // Rest of the string are values for the raw array.
  905. // Now convert the strings to integers and place them in raw_array.
  906. count = 0;
  907. do {
  908. index = str.indexOf(',', start_from);
  909. raw_array[count] = str.substring(start_from, index).toInt();
  910. start_from = index + 1;
  911. count++;
  912. } while (index != -1);
  913. irsend.sendRaw(raw_array, count, freq); // All done. Send it.
  914. free(raw_array); // Free up the memory allocated.
  915. if (count > 0)
  916. return true; // We sent something.
  917. return false; // We probably didn't.
  918. }
  919. #endif // SEND_RAW
  920. // Parse the URL args to find the IR code.
  921. void handleIr() {
  922. uint64_t data = 0;
  923. String data_str = "";
  924. int ir_type = 3; // Default to NEC codes.
  925. uint16_t nbits = 0;
  926. uint16_t repeat = 0;
  927. for (uint16_t i = 0; i < server.args(); i++) {
  928. if (server.argName(i) == argType)
  929. ir_type = atoi(server.arg(i).c_str());
  930. if (server.argName(i) == argData) {
  931. data = getUInt64fromHex(server.arg(i).c_str());
  932. data_str = server.arg(i);
  933. }
  934. if (server.argName(i) == argBits)
  935. nbits = atoi(server.arg(i).c_str());
  936. if (server.argName(i) == argRepeat)
  937. repeat = atoi(server.arg(i).c_str());
  938. }
  939. debug("New code received via HTTP");
  940. lastSendSucceeded = sendIRCode(ir_type, data, data_str.c_str(), nbits,
  941. repeat);
  942. handleRoot();
  943. }
  944. void handleNotFound() {
  945. String message = "File Not Found\n\n";
  946. message += "URI: ";
  947. message += server.uri();
  948. message += "\nMethod: ";
  949. message += (server.method() == HTTP_GET)?"GET":"POST";
  950. message += "\nArguments: ";
  951. message += server.args();
  952. message += "\n";
  953. for (uint8_t i=0; i < server.args(); i++)
  954. message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  955. server.send(404, "text/plain", message);
  956. }
  957. void setup_wifi() {
  958. delay(10);
  959. // We start by connecting to a WiFi network
  960. wifiManager.setTimeout(300); // Time out after 5 mins.
  961. #if USE_STATIC_IP
  962. // Use a static IP config rather than the one supplied via DHCP.
  963. wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask);
  964. #endif // USE_STATIC_IP
  965. if (!wifiManager.autoConnect()) {
  966. debug("Wifi failed to connect and hit timeout.");
  967. delay(3000);
  968. // Reboot. A.k.a. "Have you tried turning it Off and On again?"
  969. ESP.reset();
  970. delay(5000);
  971. }
  972. debug("WiFi connected. IP address: " + WiFi.localIP().toString());
  973. }
  974. void setup(void) {
  975. irsend.begin();
  976. offset = irsend.calibrate();
  977. #if IR_RX
  978. #if DECODE_HASH
  979. // Ignore messages with less than minimum on or off pulses.
  980. irrecv.setUnknownThreshold(kMinUnknownSize);
  981. #endif // DECODE_HASH
  982. irrecv.enableIRIn(); // Start the receiver
  983. #endif // IR_RX
  984. #ifdef DEBUG
  985. // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use.
  986. Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY);
  987. while (!Serial) // Wait for the serial connection to be establised.
  988. delay(50);
  989. Serial.println();
  990. debug("IRMQTTServer " _MY_VERSION_" has booted.");
  991. #endif // DEBUG
  992. setup_wifi();
  993. // Wait a bit for things to settle.
  994. delay(1500);
  995. lastReconnectAttempt = 0;
  996. if (mdns.begin(HOSTNAME, WiFi.localIP())) {
  997. debug("MDNS responder started");
  998. }
  999. // Setup the root web page.
  1000. server.on("/", handleRoot);
  1001. // Setup the page to handle web-based IR codes.
  1002. server.on("/ir", handleIr);
  1003. // Setup a reset page to cause WiFiManager information to be reset.
  1004. server.on("/reset", handleReset);
  1005. // Setup the URL to allow Over-The-Air (OTA) firmware updates.
  1006. server.on("/update", HTTP_POST, [](){
  1007. server.sendHeader("Connection", "close");
  1008. server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
  1009. ESP.restart();
  1010. }, [](){
  1011. HTTPUpload& upload = server.upload();
  1012. if (upload.status == UPLOAD_FILE_START) {
  1013. WiFiUDP::stopAll();
  1014. debug("Update: " + upload.filename);
  1015. uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) &
  1016. 0xFFFFF000;
  1017. if (!Update.begin(maxSketchSpace)) { // start with max available size
  1018. #ifdef DEBUG
  1019. Update.printError(Serial);
  1020. #endif // DEBUG
  1021. }
  1022. } else if (upload.status == UPLOAD_FILE_WRITE) {
  1023. if (Update.write(upload.buf, upload.currentSize) !=
  1024. upload.currentSize) {
  1025. #ifdef DEBUG
  1026. Update.printError(Serial);
  1027. #endif // DEBUG
  1028. }
  1029. } else if (upload.status == UPLOAD_FILE_END) {
  1030. if (Update.end(true)) { // true to set the size to the current progress
  1031. debug("Update Success: " + (String) upload.totalSize +
  1032. "\nRebooting...");
  1033. }
  1034. }
  1035. yield();
  1036. });
  1037. // Set up an error page.
  1038. server.onNotFound(handleNotFound);
  1039. server.begin();
  1040. debug("HTTP server started");
  1041. }
  1042. #ifdef MQTT_ENABLE
  1043. // MQTT subscribing to topic
  1044. void subscribing(const String topic_name) {
  1045. // subscription to topic for receiving data
  1046. if (mqtt_client.subscribe(topic_name.c_str())) {
  1047. debug("Subscription OK to " + topic_name);
  1048. }
  1049. }
  1050. bool reconnect() {
  1051. // Loop a few times or until we're reconnected
  1052. uint16_t tries = 1;
  1053. while (!mqtt_client.connected() && tries <= 3) {
  1054. int connected = false;
  1055. // Attempt to connect
  1056. debug("Attempting MQTT connection to " MQTT_SERVER ":" + String(kMqttPort) +
  1057. "... ");
  1058. if (mqtt_user && mqtt_password)
  1059. connected = mqtt_client.connect(mqtt_clientid.c_str(), mqtt_user,
  1060. mqtt_password);
  1061. else
  1062. connected = mqtt_client.connect(mqtt_clientid.c_str());
  1063. if (connected) {
  1064. // Once connected, publish an announcement...
  1065. mqtt_client.publish(MQTTack, "Connected");
  1066. debug("connected.");
  1067. // Subscribing to topic(s)
  1068. subscribing(MQTTcommand);
  1069. } else {
  1070. debug("failed, rc=" + String(mqtt_client.state()) +
  1071. " Try again in a bit.");
  1072. // Wait for a bit before retrying
  1073. delay(tries << 7); // Linear increasing back-off (x128)
  1074. }
  1075. tries++;
  1076. }
  1077. return mqtt_client.connected();
  1078. }
  1079. #endif // MQTT_ENABLE
  1080. void loop(void) {
  1081. server.handleClient(); // Handle any web activity
  1082. #ifdef MQTT_ENABLE
  1083. uint32_t now = millis();
  1084. // MQTT client connection management
  1085. if (!mqtt_client.connected()) {
  1086. if (wasConnected) {
  1087. lastDisconnectedTime = now;
  1088. wasConnected = false;
  1089. mqttDisconnectCounter++;
  1090. }
  1091. // Reconnect if it's longer than kMqttReconnectTime since we last tried.
  1092. if (now - lastReconnectAttempt > kMqttReconnectTime) {
  1093. lastReconnectAttempt = now;
  1094. debug("client mqtt not connected, trying to connect");
  1095. // Attempt to reconnect
  1096. if (reconnect()) {
  1097. lastReconnectAttempt = 0;
  1098. wasConnected = true;
  1099. if (boot) {
  1100. mqtt_client.publish(MQTTack, "IR Server just booted");
  1101. boot = false;
  1102. } else {
  1103. String text = "IR Server just (re)connected to MQTT. "
  1104. "Lost connection about " + timeSince(lastConnectedTime);
  1105. mqtt_client.publish(MQTTack, text.c_str());
  1106. }
  1107. lastConnectedTime = now;
  1108. debug("successful client mqtt connection");
  1109. }
  1110. }
  1111. } else {
  1112. lastConnectedTime = now;
  1113. // MQTT loop
  1114. mqtt_client.loop();
  1115. }
  1116. #endif // MQTT_ENABLE
  1117. #ifdef IR_RX
  1118. // Check if an IR code has been received via the IR RX module.
  1119. if (irrecv.decode(&capture)) {
  1120. lastIrReceivedTime = millis();
  1121. lastIrReceived = String(capture.decode_type) + "," +
  1122. resultToHexidecimal(&capture);
  1123. // If it isn't an AC code, add the bits.
  1124. if (!hasACState(capture.decode_type))
  1125. lastIrReceived += "," + String(capture.bits);
  1126. mqtt_client.publish(MQTTrecv, lastIrReceived.c_str());
  1127. irRecvCounter++;
  1128. debug("Incoming IR message sent to MQTT: " + lastIrReceived);
  1129. }
  1130. #endif // IR_RX
  1131. delay(100);
  1132. }
  1133. // Arduino framework doesn't support strtoull(), so make our own one.
  1134. uint64_t getUInt64fromHex(char const *str) {
  1135. uint64_t result = 0;
  1136. uint16_t offset = 0;
  1137. // Skip any leading '0x' or '0X' prefix.
  1138. if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
  1139. offset = 2;
  1140. for (; isxdigit((unsigned char)str[offset]); offset++) {
  1141. char c = str[offset];
  1142. result *= 16;
  1143. if (isdigit(c)) /* '0' .. '9' */
  1144. result += c - '0';
  1145. else if (isupper(c)) /* 'A' .. 'F' */
  1146. result += c - 'A' + 10;
  1147. else /* 'a' .. 'f'*/
  1148. result += c - 'a' + 10;
  1149. }
  1150. return result;
  1151. }
  1152. // Transmit the given IR message.
  1153. //
  1154. // Args:
  1155. // ir_type: enum of the protocol to be sent.
  1156. // code: Numeric payload of the IR message. Most protocols use this.
  1157. // code_str: The unparsed code to be sent. Used by complex protocol encodings.
  1158. // bits: Nr. of bits in the protocol. 0 means use the protocol's default.
  1159. // repeat: Nr. of times the message is to be repeated. (Not all protcols.)
  1160. // Returns:
  1161. // bool: Successfully sent or not.
  1162. bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
  1163. uint16_t bits, uint16_t repeat) {
  1164. // Create a pseudo-lock so we don't try to send two codes at the same time.
  1165. while (ir_lock)
  1166. delay(20);
  1167. ir_lock = true;
  1168. bool success = true; // Assume success.
  1169. // send the IR message.
  1170. switch (ir_type) {
  1171. #if SEND_RC5
  1172. case RC5: // 1
  1173. if (bits == 0)
  1174. bits = kRC5Bits;
  1175. irsend.sendRC5(code, bits, repeat);
  1176. break;
  1177. #endif
  1178. #if SEND_RC6
  1179. case RC6: // 2
  1180. if (bits == 0)
  1181. bits = kRC6Mode0Bits;
  1182. irsend.sendRC6(code, bits, repeat);
  1183. break;
  1184. #endif
  1185. #if SEND_NEC
  1186. case NEC: // 3
  1187. if (bits == 0)
  1188. bits = kNECBits;
  1189. irsend.sendNEC(code, bits, repeat);
  1190. break;
  1191. #endif
  1192. #if SEND_SONY
  1193. case SONY: // 4
  1194. if (bits == 0)
  1195. bits = kSony12Bits;
  1196. repeat = std::max(repeat, kSonyMinRepeat);
  1197. irsend.sendSony(code, bits, repeat);
  1198. break;
  1199. #endif
  1200. #if SEND_PANASONIC
  1201. case PANASONIC: // 5
  1202. if (bits == 0)
  1203. bits = kPanasonicBits;
  1204. irsend.sendPanasonic64(code, bits, repeat);
  1205. break;
  1206. #endif
  1207. #if SEND_JVC
  1208. case JVC: // 6
  1209. if (bits == 0)
  1210. bits = kJvcBits;
  1211. irsend.sendJVC(code, bits, repeat);
  1212. break;
  1213. #endif
  1214. #if SEND_SAMSUNG
  1215. case SAMSUNG: // 7
  1216. if (bits == 0)
  1217. bits = kSamsungBits;
  1218. irsend.sendSAMSUNG(code, bits, repeat);
  1219. break;
  1220. #endif
  1221. #if SEND_WHYNTER
  1222. case WHYNTER: // 8
  1223. if (bits == 0)
  1224. bits = kWhynterBits;
  1225. irsend.sendWhynter(code, bits, repeat);
  1226. break;
  1227. #endif
  1228. #if SEND_AIWA_RC_T501
  1229. case AIWA_RC_T501: // 9
  1230. if (bits == 0)
  1231. bits = kAiwaRcT501Bits;
  1232. repeat = std::max(repeat, kAiwaRcT501MinRepeats);
  1233. irsend.sendAiwaRCT501(code, bits, repeat);
  1234. break;
  1235. #endif
  1236. #if SEND_LG
  1237. case LG: // 10
  1238. if (bits == 0)
  1239. bits = kLgBits;
  1240. irsend.sendLG(code, bits, repeat);
  1241. break;
  1242. #endif
  1243. #if SEND_MITSUBISHI
  1244. case MITSUBISHI: // 12
  1245. if (bits == 0)
  1246. bits = kMitsubishiBits;
  1247. repeat = std::max(repeat, kMitsubishiMinRepeat);
  1248. irsend.sendMitsubishi(code, bits, repeat);
  1249. break;
  1250. #endif
  1251. #if SEND_DISH
  1252. case DISH: // 13
  1253. if (bits == 0)
  1254. bits = kDishBits;
  1255. repeat = std::max(repeat, kDishMinRepeat);
  1256. irsend.sendDISH(code, bits, repeat);
  1257. break;
  1258. #endif
  1259. #if SEND_SHARP
  1260. case SHARP: // 14
  1261. if (bits == 0)
  1262. bits = kSharpBits;
  1263. irsend.sendSharpRaw(code, bits, repeat);
  1264. break;
  1265. #endif
  1266. #if SEND_COOLIX
  1267. case COOLIX: // 15
  1268. if (bits == 0)
  1269. bits = kCoolixBits;
  1270. irsend.sendCOOLIX(code, bits, repeat);
  1271. break;
  1272. #endif
  1273. case DAIKIN: // 16
  1274. case KELVINATOR: // 18
  1275. case MITSUBISHI_AC: // 20
  1276. case GREE: // 24
  1277. case ARGO: // 27
  1278. case TROTEC: // 28
  1279. case TOSHIBA_AC: // 32
  1280. case FUJITSU_AC: // 33
  1281. case HAIER_AC: // 38
  1282. case HAIER_AC_YRW02: // 44
  1283. case HITACHI_AC: // 40
  1284. case HITACHI_AC1: // 41
  1285. case HITACHI_AC2: // 42
  1286. case WHIRLPOOL_AC: // 45
  1287. case SAMSUNG_AC: // 46
  1288. case ELECTRA_AC: // 48
  1289. case PANASONIC_AC: // 49
  1290. case MWM: // 52
  1291. success = parseStringAndSendAirCon(ir_type, code_str);
  1292. break;
  1293. #if SEND_DENON
  1294. case DENON: // 17
  1295. if (bits == 0)
  1296. bits = DENON_BITS;
  1297. irsend.sendDenon(code, bits, repeat);
  1298. break;
  1299. #endif
  1300. #if SEND_SHERWOOD
  1301. case SHERWOOD: // 19
  1302. if (bits == 0)
  1303. bits = kSherwoodBits;
  1304. repeat = std::max(repeat, kSherwoodMinRepeat);
  1305. irsend.sendSherwood(code, bits, repeat);
  1306. break;
  1307. #endif
  1308. #if SEND_RCMM
  1309. case RCMM: // 21
  1310. if (bits == 0)
  1311. bits = kRCMMBits;
  1312. irsend.sendRCMM(code, bits, repeat);
  1313. break;
  1314. #endif
  1315. #if SEND_SANYO
  1316. case SANYO_LC7461: // 22
  1317. if (bits == 0)
  1318. bits = kSanyoLC7461Bits;
  1319. irsend.sendSanyoLC7461(code, bits, repeat);
  1320. break;
  1321. #endif
  1322. #if SEND_RC5
  1323. case RC5X: // 23
  1324. if (bits == 0)
  1325. bits = kRC5XBits;
  1326. irsend.sendRC5(code, bits, repeat);
  1327. break;
  1328. #endif
  1329. #if SEND_PRONTO
  1330. case PRONTO: // 25
  1331. success = parseStringAndSendPronto(code_str, repeat);
  1332. break;
  1333. #endif
  1334. #if SEND_NIKAI
  1335. case NIKAI: // 29
  1336. if (bits == 0)
  1337. bits = kNikaiBits;
  1338. irsend.sendNikai(code, bits, repeat);
  1339. break;
  1340. #endif
  1341. #if SEND_RAW
  1342. case RAW: // 30
  1343. success = parseStringAndSendRaw(code_str);
  1344. break;
  1345. #endif
  1346. #if SEND_GLOBALCACHE
  1347. case GLOBALCACHE: // 31
  1348. success = parseStringAndSendGC(code_str);
  1349. break;
  1350. #endif
  1351. #if SEND_MIDEA
  1352. case MIDEA: // 34
  1353. if (bits == 0)
  1354. bits = kMideaBits;
  1355. irsend.sendMidea(code, bits, repeat);
  1356. break;
  1357. #endif
  1358. #if SEND_MAGIQUEST
  1359. case MAGIQUEST: // 35
  1360. if (bits == 0)
  1361. bits = kMagiquestBits;
  1362. irsend.sendMagiQuest(code, bits, repeat);
  1363. break;
  1364. #endif
  1365. #if SEND_LASERTAG
  1366. case LASERTAG: // 36
  1367. if (bits == 0)
  1368. bits = kLasertagBits;
  1369. irsend.sendLasertag(code, bits, repeat);
  1370. break;
  1371. #endif
  1372. #if SEND_CARRIER_AC
  1373. case CARRIER_AC: // 37
  1374. if (bits == 0)
  1375. bits = kCarrierAcBits;
  1376. irsend.sendCarrierAC(code, bits, repeat);
  1377. break;
  1378. #endif
  1379. #if SEND_MITSUBISHI2
  1380. case MITSUBISHI2: // 39
  1381. if (bits == 0)
  1382. bits = kMitsubishiBits;
  1383. repeat = std::max(repeat, kMitsubishiMinRepeat);
  1384. irsend.sendMitsubishi2(code, bits, repeat);
  1385. break;
  1386. #endif
  1387. #if SEND_GICABLE
  1388. case GICABLE: // 43
  1389. if (bits == 0)
  1390. bits = kGicableBits;
  1391. repeat = std::max(repeat, kGicableMinRepeat);
  1392. irsend.sendGICable(code, bits, repeat);
  1393. break;
  1394. #endif
  1395. #if SEND_LUTRON
  1396. case LUTRON: // 47
  1397. if (bits == 0)
  1398. bits = kLutronBits;
  1399. irsend.sendLutron(code, bits, repeat);
  1400. break;
  1401. #endif
  1402. #if SEND_PIONEER
  1403. case PIONEER: // 50
  1404. if (bits == 0)
  1405. bits = kPioneerBits;
  1406. irsend.sendPioneer(code, bits, repeat);
  1407. break;
  1408. #endif
  1409. #if SEND_LG
  1410. case LG2: // 51
  1411. if (bits == 0)
  1412. bits = kLgBits;
  1413. irsend.sendLG2(code, bits, repeat);
  1414. break;
  1415. #endif
  1416. default:
  1417. // If we got here, we didn't know how to send it.
  1418. success = false;
  1419. }
  1420. lastSendTime = millis();
  1421. // Release the lock.
  1422. ir_lock = false;
  1423. // Indicate that we sent the message or not.
  1424. if (success) {
  1425. sendReqCounter++;
  1426. debug("Sent the IR message:");
  1427. } else {
  1428. debug("Failed to send IR Message:");
  1429. }
  1430. debug("Type: " + String(ir_type));
  1431. // For "long" codes we basically repeat what we got.
  1432. if (hasACState((decode_type_t) ir_type) ||
  1433. ir_type == PRONTO ||
  1434. ir_type == RAW ||
  1435. ir_type == GLOBALCACHE) {
  1436. debug("Code: ");
  1437. debug(code_str);
  1438. // Confirm what we were asked to send was sent.
  1439. #ifdef MQTT_ENABLE
  1440. if (success) {
  1441. if (ir_type == PRONTO && repeat > 0)
  1442. mqtt_client.publish(MQTTack, (String(ir_type) + ",R" +
  1443. String(repeat) + "," +
  1444. String(code_str)).c_str());
  1445. else
  1446. mqtt_client.publish(MQTTack, (String(ir_type) + "," +
  1447. String(code_str)).c_str());
  1448. }
  1449. #endif // MQTT_ENABLE
  1450. } else { // For "short" codes, we break it down a bit more before we report.
  1451. debug("Code: 0x" + uint64ToString(code, 16));
  1452. debug("Bits: " + String(bits));
  1453. debug("Repeats: " + String(repeat));
  1454. #ifdef MQTT_ENABLE
  1455. if (success)
  1456. mqtt_client.publish(MQTTack, (String(ir_type) + "," +
  1457. uint64ToString(code, 16)
  1458. + "," + String(bits) + "," +
  1459. String(repeat)).c_str());
  1460. #endif // MQTT_ENABLE
  1461. }
  1462. return success;
  1463. }
  1464. #ifdef MQTT_ENABLE
  1465. void receivingMQTT(String const topic_name, String const callback_str) {
  1466. char* tok_ptr;
  1467. uint64_t code = 0;
  1468. uint16_t nbits = 0;
  1469. uint16_t repeat = 0;
  1470. debug("Receiving data by MQTT topic " + topic_name);
  1471. // Make a copy of the callback string as strtok destroys it.
  1472. char* callback_c_str = strdup(callback_str.c_str());
  1473. debug("MQTT Payload (raw): " + callback_str);
  1474. // Save the message as the last command seen (global).
  1475. lastMqttCmd = callback_str;
  1476. lastMqttCmdTime = millis();
  1477. // Get the numeric protocol type.
  1478. int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10);
  1479. char* next = strtok_r(NULL, ",", &tok_ptr);
  1480. // If there is unparsed string left, try to convert it assuming it's hex.
  1481. if (next != NULL) {
  1482. code = getUInt64fromHex(next);
  1483. next = strtok_r(NULL, ",", &tok_ptr);
  1484. } else {
  1485. // We require at least two value in the string. Give up.
  1486. return;
  1487. }
  1488. // If there is still string left, assume it is the bit size.
  1489. if (next != NULL) {
  1490. nbits = atoi(next);
  1491. next = strtok_r(NULL, ",", &tok_ptr);
  1492. }
  1493. // If there is still string left, assume it is the repeat count.
  1494. if (next != NULL)
  1495. repeat = atoi(next);
  1496. free(callback_c_str);
  1497. // send received MQTT value by IR signal
  1498. lastSendSucceeded = sendIRCode(
  1499. ir_type, code,
  1500. callback_str.substring(callback_str.indexOf(",") + 1).c_str(),
  1501. nbits, repeat);
  1502. }
  1503. // Callback function, when the gateway receive an MQTT value on the topics
  1504. // subscribed this function is called
  1505. void callback(char* topic, byte* payload, unsigned int length) {
  1506. // In order to republish this payload, a copy must be made
  1507. // as the orignal payload buffer will be overwritten whilst
  1508. // constructing the PUBLISH packet.
  1509. // Allocate the correct amount of memory for the payload copy
  1510. byte* payload_copy = reinterpret_cast<byte*>(malloc(length + 1));
  1511. // Copy the payload to the new buffer
  1512. memcpy(payload_copy, payload, length);
  1513. // Conversion to a printable string
  1514. payload_copy[length] = '\0';
  1515. String callback_string = String(reinterpret_cast<char*>(payload_copy));
  1516. String topic_name = String(reinterpret_cast<char*>(topic));
  1517. // launch the function to treat received data
  1518. receivingMQTT(topic_name, callback_string);
  1519. // Free the memory
  1520. free(payload_copy);
  1521. }
  1522. #endif // MQTT_ENABLE