mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
289
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1c777e6fd | ||
|
|
651353cc99 | ||
|
|
85f97551c5 | ||
|
|
6a158f68f5 | ||
|
|
1933714de5 | ||
|
|
545117ddf7 | ||
|
|
8eb479c411 | ||
|
|
b39f51ca3c | ||
|
|
4511e08d3c | ||
|
|
086e836956 | ||
|
|
3e20f617ea | ||
|
|
8d9aacb6a4 | ||
|
|
0670fa98a6 | ||
|
|
2b3d1517c9 | ||
|
|
0be79759d1 | ||
|
|
d47bede215 | ||
|
|
2988a244b2 | ||
|
|
8d87dc0175 | ||
|
|
21c86e666a | ||
|
|
e04f71ab47 | ||
|
|
90ae0720cf | ||
|
|
c8ecd8d374 | ||
|
|
7a451dd672 | ||
|
|
b06f6aa360 | ||
|
|
6b67415c71 | ||
|
|
b095465d3a | ||
|
|
81a5d911ba | ||
|
|
48060d8d99 | ||
|
|
d303fa023f | ||
|
|
29b0cc1b32 | ||
|
|
584abd1e75 | ||
|
|
b4d8be0e59 | ||
|
|
30f5c5cb7d | ||
|
|
6e7286c6fb | ||
|
|
ae1db543ce | ||
|
|
947e1e0343 | ||
|
|
0578d720f4 | ||
|
|
211d79bbd5 | ||
|
|
91c5d60af9 | ||
|
|
0d9bc8027d | ||
|
|
bd2bdab95c | ||
|
|
c4f98fbc8e | ||
|
|
b332a513b5 | ||
|
|
e36bb714f1 | ||
|
|
6e1cde343f | ||
|
|
cce9ddd2dc | ||
|
|
51be17a436 | ||
|
|
66d27a6697 | ||
|
|
a033e458fb | ||
|
|
494436bbff | ||
|
|
23bacd0762 | ||
|
|
f131254853 | ||
|
|
657d15d962 | ||
|
|
60910da48d | ||
|
|
6cee2d0f32 | ||
|
|
533cb88a64 | ||
|
|
ffa32fe851 | ||
|
|
b900cc9389 | ||
|
|
8e11c19af6 | ||
|
|
f33ca6df78 | ||
|
|
194a230d20 | ||
|
|
7a5a432016 | ||
|
|
07511fe144 | ||
|
|
9a390136f9 | ||
|
|
27a03f166a | ||
|
|
0a63aeb8f0 | ||
|
|
22c10ac615 | ||
|
|
ac83cfa249 | ||
|
|
6ef0c08bc4 | ||
|
|
1ee965067d | ||
|
|
e46a873fce | ||
|
|
784e956c96 | ||
|
|
b6eebb330a | ||
|
|
a4fe8d42d2 | ||
|
|
fa42cadfa1 | ||
|
|
17dc6fa859 | ||
|
|
1432726dec | ||
|
|
7ba6ffa407 | ||
|
|
ab555e4d6b | ||
|
|
d4259f4e23 | ||
|
|
aef45c06f6 | ||
|
|
828f9e6a30 | ||
|
|
e79d778cf4 | ||
|
|
965eb2d994 | ||
|
|
657fccf49f | ||
|
|
228b41a1ac | ||
|
|
3bba294eb7 | ||
|
|
0254181b3e | ||
|
|
3b67282abb | ||
|
|
38af765935 | ||
|
|
ff180b085c | ||
|
|
e0785eb796 | ||
|
|
75a83787a4 | ||
|
|
65bea6343c | ||
|
|
6fe61411a0 | ||
|
|
4552e91022 | ||
|
|
cc94d70e6c | ||
|
|
2435078f76 | ||
|
|
dd2cd50d4b | ||
|
|
fe38eb629d | ||
|
|
e961507456 | ||
|
|
e5ee5644bf | ||
|
|
ac6d85e214 | ||
|
|
39c2d54aee | ||
|
|
68efd5ff9e | ||
|
|
627f516f85 | ||
|
|
a45fadf42e | ||
|
|
2ae52b849c | ||
|
|
1d8b6d346b | ||
|
|
2a5ac327d3 | ||
|
|
886101d4d4 | ||
|
|
8fc6486855 | ||
|
|
eeb1cc71b3 | ||
|
|
13837830e8 | ||
|
|
f86ff46a82 | ||
|
|
e901bc957a | ||
|
|
e7e10c5305 | ||
|
|
466ddfbe88 | ||
|
|
c8f02d2a4c | ||
|
|
af1d9b38c9 | ||
|
|
7294217489 | ||
|
|
208fa8a3aa | ||
|
|
b3b8eff127 | ||
|
|
c6a90ab2d0 | ||
|
|
e7d28e8576 | ||
|
|
efa27dc01d | ||
|
|
7c7b81911d | ||
|
|
c2962d7337 | ||
|
|
e2e2059ff0 | ||
|
|
4e95bbef7f | ||
|
|
b0a90b5b14 | ||
|
|
c8ac1bbd59 | ||
|
|
e6105bfe71 | ||
|
|
1bb8669ed2 | ||
|
|
34f4a9a8e0 | ||
|
|
a517f3471c | ||
|
|
9c706b7b0e | ||
|
|
f2b4e17e2a | ||
|
|
6f62ba0f9b | ||
|
|
36c6fd4bf7 | ||
|
|
4446d76c83 | ||
|
|
f354b05b07 | ||
|
|
467df9dde2 | ||
|
|
dac243cc82 | ||
|
|
3d1be96f8a | ||
|
|
0dd53e38d7 | ||
|
|
df6338dfee | ||
|
|
1bab99de40 | ||
|
|
ec44efb59f | ||
|
|
202a6fd1f7 | ||
|
|
dc7828d343 | ||
|
|
5c92731b41 | ||
|
|
8c34067d9c | ||
|
|
92025249fc | ||
|
|
dd5b5bb7c3 | ||
|
|
03bc2f34f6 | ||
|
|
a19b59e184 | ||
|
|
861bc64ce3 | ||
|
|
7428b03e18 | ||
|
|
54a6ecac7d | ||
|
|
ec4aa627bb | ||
|
|
0dc47bc04c | ||
|
|
5f029c59d2 | ||
|
|
8beeeaf7fa | ||
|
|
293b63e654 | ||
|
|
734150e87b | ||
|
|
6bb1b4086c | ||
|
|
9cd2d2d843 | ||
|
|
02c022d7d5 | ||
|
|
b1c8346700 | ||
|
|
272ed8c578 | ||
|
|
a9e473dbc6 | ||
|
|
4baccf5863 | ||
|
|
ad84eb8b8a | ||
|
|
23f2bfc6d8 | ||
|
|
34d2f25652 | ||
|
|
294683ece1 | ||
|
|
56bd1cb19c | ||
|
|
812f530c4b | ||
|
|
97258a531d | ||
|
|
7d2ef727a7 | ||
|
|
9e83213a13 | ||
|
|
ecb5ca9306 | ||
|
|
edb6178ae2 | ||
|
|
b8894efc57 | ||
|
|
970181d8c7 | ||
|
|
9d2fad783f | ||
|
|
9d42e83314 | ||
|
|
0005915c5c | ||
|
|
eb78bc3e03 | ||
|
|
17777698cd | ||
|
|
d091148dfc | ||
|
|
fcb25a2aec | ||
|
|
a7d90a33a0 | ||
|
|
3a68ff8f9d | ||
|
|
3b45e200a7 | ||
|
|
b724c22c80 | ||
|
|
b810e34b74 | ||
|
|
c70ffd735c | ||
|
|
604c58a290 | ||
|
|
1d1b69b1ce | ||
|
|
bd2cc4138b | ||
|
|
ca5f2815bc | ||
|
|
f67bf51606 | ||
|
|
e1f0badd92 | ||
|
|
b9ea66037c | ||
|
|
62a196d398 | ||
|
|
d4ee74297b | ||
|
|
d0d95e221b | ||
|
|
7ad8880651 | ||
|
|
f46a6c3414 | ||
|
|
ab0ed4b5ca | ||
|
|
b1fb308646 | ||
|
|
79ee397e8e | ||
|
|
56569374f3 | ||
|
|
41b13f6797 | ||
|
|
8f23048f18 | ||
|
|
a123dbf37f | ||
|
|
6b65e90ddf | ||
|
|
890db55197 | ||
|
|
514800433f | ||
|
|
deb18d2391 | ||
|
|
e18d069171 | ||
|
|
b63c375793 | ||
|
|
f906e9173c | ||
|
|
c444022f5f | ||
|
|
5511ee9f38 | ||
|
|
427a22d23b | ||
|
|
306cbf3107 | ||
|
|
458f672744 | ||
|
|
dee1ee6f32 | ||
|
|
8930e331d3 | ||
|
|
6033990767 | ||
|
|
d271898968 | ||
|
|
f261e6a5ea | ||
|
|
ff160f386e | ||
|
|
ef92e7ae4f | ||
|
|
4051f0bd2e | ||
|
|
93105200d7 | ||
|
|
f48ede5d7e | ||
|
|
b73181487c | ||
|
|
caee2892c4 | ||
|
|
432bd32967 | ||
|
|
235bc0a598 | ||
|
|
f0cf26baec | ||
|
|
fffccd3787 | ||
|
|
5cfbac5eba | ||
|
|
2bce3f8d02 | ||
|
|
e210722e0e | ||
|
|
909d9a4fd1 | ||
|
|
c8b57cf9e0 | ||
|
|
d74493ced0 | ||
|
|
7783cf4ce5 | ||
|
|
27e07d1f67 | ||
|
|
40857cc57c | ||
|
|
4f764d6101 | ||
|
|
49e98f2f8b | ||
|
|
ddad026a2b | ||
|
|
b8df55ef36 | ||
|
|
be7724d8c7 | ||
|
|
c5c1c92cfb | ||
|
|
e6b219a182 | ||
|
|
d2d03358ef | ||
|
|
a4d751b40f | ||
|
|
1c73cacc82 | ||
|
|
23bfa54ec4 | ||
|
|
95138489cd | ||
|
|
c9ec69d30c | ||
|
|
0e4cabeb17 | ||
|
|
19961198fb | ||
|
|
c705c570db | ||
|
|
34d48adb4a | ||
|
|
fda973bb78 | ||
|
|
c5bfd67d6d | ||
|
|
402de69fd5 | ||
|
|
41341b46de | ||
|
|
3574517014 | ||
|
|
cca785e1f0 | ||
|
|
4215e058d0 | ||
|
|
2f91fbe931 | ||
|
|
90febfd46f | ||
|
|
ccc5b68111 | ||
|
|
07f6b86138 | ||
|
|
7b2667feaa | ||
|
|
48bd7b334d | ||
|
|
9dd4242590 | ||
|
|
ac8c29e9c5 | ||
|
|
387b0f68a8 | ||
|
|
fa130bf649 |
@@ -1,3 +1,4 @@
|
||||
### If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! ###
|
||||
<!--- If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! --->
|
||||
<!--- NOTE: This is not a support forum! For questions and support go here: --->
|
||||
<!--- https://www.letscontrolit.com/forum/viewforum.php?f=1 --->
|
||||
|
||||
@@ -18,3 +18,7 @@ lib/readme.txt
|
||||
src/Custom.h
|
||||
/ESPEasy
|
||||
test/output_export.cpp
|
||||
.vscode
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
|Latest Nightly | Build Status | Patreon | Ko-Fi | PayPal |
|
||||
|-------|-------|-------|-------|-------|
|
||||
| [](https://github.com/letscontrolit/ESPEasy/releases/latest) | [](https://travis-ci.org/letscontrolit/ESPEasy) | [](https://www.patreon.com/GrovkillenTDer) | [](https://ko-fi.com/grovkillentder) | [](https://www.paypal.me/espeasy) |
|
||||
|
||||
For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above.
|
||||
|
||||
# ESPEasy (development branch)
|
||||
|
||||
Badge | Info |
|
||||
-------|-------|
|
||||
[](https://travis-ci.org/letscontrolit/ESPEasy) | build status |
|
||||
[](https://github.com/letscontrolit/ESPEasy/releases/latest) | latest nightly |
|
||||
|
||||
Introduction and wiki: https://www.letscontrolit.com/wiki/index.php/ESPEasy#Introduction
|
||||
|
||||
|
||||
+4
-1
@@ -10,6 +10,7 @@ for ENV in \
|
||||
normal_ESP8266_1024 \
|
||||
normal_ESP8266_4096 \
|
||||
normal_ESP8285_1024 \
|
||||
normal_IR_ESP8266_4096 \
|
||||
test_ESP8266_1024\
|
||||
test_ESP8266_4096\
|
||||
test_ESP8266_4096_VCC\
|
||||
@@ -20,7 +21,9 @@ for ENV in \
|
||||
dev_ESP8266PUYA_1024\
|
||||
dev_ESP8266PUYA_1024_VCC\
|
||||
hard_SONOFF_POW\
|
||||
esp32dev;\
|
||||
hard_Shelly_1\
|
||||
esp32dev\
|
||||
esp32test_1M8_partition;\
|
||||
do
|
||||
echo
|
||||
echo "### Deploying environment $ENV for version $VERSION"
|
||||
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
_____ ____ ____
|
||||
| ____/ ___|| _ \ ___ __ _ ___ _ _
|
||||
| _| \___ \| |_) / _ \/ _` / __| | | |
|
||||
| |___ ___) | __/ __/ (_| \__ \ |_| |
|
||||
|_____|____/|_| \___|\__,_|___/\__, |
|
||||
|___/
|
||||
|
||||
|
||||
This is the distribution folder for ESPeasy.
|
||||
In here you will find a flash tool to program the ESP module.
|
||||
|
||||
Also a number of binary images is included.
|
||||
|
||||
The filename is quite descriptive:
|
||||
ESP_Easy_mega-<date>_<buildType>_<chip>_<memorySize>.bin
|
||||
|
||||
Build type can be: (differ in included plugins)
|
||||
- normal => Only Stable plugins and controllers
|
||||
- test => Stable + Testing
|
||||
- dev => Stable + Testing + Development
|
||||
|
||||
There is also a number of special builds:
|
||||
- normal_IR => "Normal" + IR receiver/transmitter plugins and library
|
||||
- hard_xxxxx => Special builds for some off-the-shelf hardware.
|
||||
|
||||
Chip can be:
|
||||
- ESP8266 => Most likely option
|
||||
- ESP8285 => Used in some Sonoff modules
|
||||
- ESP32 => Experimental support at this moment
|
||||
- ESP8266PUYA => For ESP modules with a flash chip labeled "PUYA" (often 1 MB)
|
||||
|
||||
The "PUYA" version deserves a bit more attention.
|
||||
If you cannot save settings, better try to see if the flash chip has "PUYA" written on it.
|
||||
If so, try the "PUYA" build.
|
||||
|
||||
MemorySize can be:
|
||||
- 1024 => 1 MB flash modules (e.g. almost all Sonoff modules)
|
||||
- 2048 => 2 MB flash modules (e.g. Shelly1/WROOM02)
|
||||
- 4096 => 4 MB flash modules (e.g. NodeMCU/ESP32)
|
||||
|
||||
ESP32 now has 2 builds:
|
||||
- esp32dev Using the default partition layout (1.4 MB for the sketch)
|
||||
- esp32test_1M8_partition Larger sketch partition (1.8MB) smaller SPIFFS (316 kB)
|
||||
|
||||
Please note that changing between those versions will destroy the settings!
|
||||
The SPIFFS partition will be lost and that contains all settings.
|
||||
|
||||
|
||||
To help recover from a bad flash, there are also blank images included.
|
||||
- blank_1MB.bin
|
||||
- blank_2MB.bin
|
||||
- blank_4MB.bin
|
||||
|
||||
When the wrong image is flashed, or the module behaves unstable, or is in a reboot loop,
|
||||
flash these images first and then the right image for the module.
|
||||
|
||||
Another great flash tool can be found here: (Windows only)
|
||||
https://github.com/Grovkillen/ESP_Easy_Flasher
|
||||
|
||||
For more information, see: https://github.com/letscontrolit/ESPEasy
|
||||
Or our forum: https://www.letscontrolit.com/forum/
|
||||
Vendored
+477
@@ -1,3 +1,480 @@
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181003 (since mega-20181002)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Oct 3 04:00:18 CEST 2018
|
||||
|
||||
David Conran (2):
|
||||
Acknowledge credit & origin of code used in IRTX
|
||||
Acknowledge credit & origin of code used in _P016IR.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181002 (since mega-20181001)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Oct 2 04:00:29 CEST 2018
|
||||
|
||||
Plebs (1):
|
||||
split parseTemplate function
|
||||
|
||||
TD-er (4):
|
||||
[#1798] Bugfix Sonoff Si7021 stability
|
||||
[ESP32] Add default partition table for reference
|
||||
[ESP32] Update to PIO espressif32@1.4.0
|
||||
[Stack] Report stack usage on ESP32 and increase stack for ESP8266normal
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181001 (since mega-20180930)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 1 13:13:13 CEST 2018
|
||||
|
||||
TD-er (4):
|
||||
[#1824] Show stack size on main page
|
||||
[Heap] Use NO_EXTRA_4K_HEAP to get old stack address space
|
||||
[Stack] Check if a block sent to save on SPIFFS is stack allocated
|
||||
[Stack] Revert move Settings from stack to heap
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180930 (since mega-20180927)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 30 04:00:24 CEST 2018
|
||||
|
||||
JojoS (5):
|
||||
fixed distance reading
|
||||
on INIT, delete sensordef before adding new
|
||||
removed sensordefs helper structure
|
||||
changed implementation to use shared_ptr instead of helper structure
|
||||
minor change: unit was missing for threshold in 'state' mode
|
||||
|
||||
TD-er (2):
|
||||
[Default Settings] Properly clear settings when reset to default
|
||||
[Defaults] Other default initializations
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180927 (since mega-20180924)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Sep 27 04:00:17 CEST 2018
|
||||
|
||||
TD-er (7):
|
||||
[ESP32] Change partition size (will clear all settings) and add plugins
|
||||
[Travis] Uninitialized variable in P073_7DGT
|
||||
[Stack] Move Settings from stack to heap
|
||||
[ESP32] RTOS move handle_schedule() to core1 and increase stack
|
||||
[Info] Add board name to the sysinfo page.
|
||||
[ESP32] Split new partition layout to separate build
|
||||
[Build] Add esp32test_1M8_partition build and warning on ESP32 partition
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180924 (since mega-20180923)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Sep 24 04:00:20 CEST 2018
|
||||
|
||||
Benno Eigenmann (1):
|
||||
Fix platformio
|
||||
|
||||
TD-er (2):
|
||||
[MQTT] Work around for lost MQTT connection
|
||||
[ESP32] Fix I2C handling to allow I2C devices to work
|
||||
|
||||
sakinit (1):
|
||||
change to platform version core_esp32_1_3_0
|
||||
|
||||
stefan (1):
|
||||
Change type of sensor for MCP and PCF devices
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180923 (since mega-20180922)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 23 04:00:12 CEST 2018
|
||||
|
||||
TD-er (2):
|
||||
[MQTT import] Fix crash at boot when plugin is enabled
|
||||
[BME280] Fix slow reading of BME280 causing watchdog resets
|
||||
|
||||
sakinit (5):
|
||||
ESP32 build issues fixed
|
||||
Fix ESP32 stack overflow in addPinSelect when called from P1WifiGateway
|
||||
ESP32: Split webserver spiffs filelist into pages
|
||||
Reverted Webserver change
|
||||
ESP32 platform updated to 1.2.0; Webserver replaced by platform one
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180922 (since mega-20180916)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Sep 22 04:00:22 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Revert "Revert "Added 2 new operators in Calculate() function""
|
||||
|
||||
TD-er (2):
|
||||
Revert "[Scheduler] Disable command scheduler"
|
||||
Revert "Revert "Fix elseif & Add if-elseif-else nesting levels""
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180916 (since mega-20180915)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 16 04:00:11 CEST 2018
|
||||
|
||||
Gijs Noorlander (3):
|
||||
Revert "Added 2 new operators in Calculate() function"
|
||||
Revert "Fix elseif & Add if-elseif-else nesting levels"
|
||||
Added warning to release notes 20180915
|
||||
|
||||
TD-er (1):
|
||||
[HW Watchdog] Backgroundtasks instead of yield during rules handling
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180915 (since mega-20180914)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Sep 15 04:00:15 CEST 2018
|
||||
Known issue:
|
||||
Causes reboots. (see [#1748](https://github.com/letscontrolit/ESPEasy/issues/1748) )
|
||||
|
||||
|
||||
Plebs (1):
|
||||
Added 2 new operators in Calculate
|
||||
|
||||
TD-er (4):
|
||||
[Scheduler] Disable command scheduler
|
||||
[Controller settings] Validate settings on load/save
|
||||
[#1713] Make hostname-unitnumber optional & validate settings
|
||||
[#1698] Incorrect sunrise value southern hemisphere
|
||||
|
||||
svmac (4):
|
||||
Fix elseif & Add if-elseif-else nesting levels
|
||||
reenabled code about !
|
||||
some code optimization
|
||||
fix mistake
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180914 (since mega-20180910)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Fri Sep 14 04:00:17 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Added PayPal link
|
||||
|
||||
giddyhup (1):
|
||||
Fix IR plugin passing received code
|
||||
|
||||
Plebs (7):
|
||||
Use parameters inside rules when calling commands
|
||||
few changes
|
||||
fixed ESP32 compatibility issue
|
||||
fixed calls to F-macro
|
||||
adding more parameters to EVENT
|
||||
final version with '=' sign as a prefix to identify the formula in rules
|
||||
added %eventvalue4%
|
||||
|
||||
TD-er (2):
|
||||
[Flash tools] Add blank files and README.txt
|
||||
[CrashRecover] Disable failing step based on failed boot count
|
||||
|
||||
sakinit (1):
|
||||
Feature: Split webserver spiffs filelist into pages
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180910 (since mega-20180909)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Sep 10 04:00:22 CEST 2018
|
||||
|
||||
TD-er (2):
|
||||
[HTTP] Command SendToHttp cannot connect
|
||||
[Domoticz IDX] Allow for IDX of > 9999
|
||||
|
||||
sakinit (1):
|
||||
Reduced buffer size to prevent stack overflow
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180909 (since mega-20180908)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 9 04:00:14 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Revert [WDT] Add watchdog feed to backgroundtasks()
|
||||
|
||||
TD-er (5):
|
||||
[#1720] WiFi read all lines from slow servers
|
||||
Add forward declaration for void backgroundtasks()
|
||||
[Serial Log] Limit serial log to prevent buffer overflow
|
||||
[ESP32] Fix Build, ESP32 doesn't have the availableForWrite
|
||||
[Serial log] Use buffer to prevent Serial buffer to overflow
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180908 (since mega-20180904)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Sep 8 04:00:20 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Update README.md
|
||||
|
||||
TD-er (37):
|
||||
[controller delay] Let controllers handle their own delay and buffer
|
||||
[MQTT] Add controller delay queue for MQTT
|
||||
[Controller] Make ControllerDelayHandler templated and comment
|
||||
[C001] Let C001 controller use queue
|
||||
[C003] Let C003 controller use queue
|
||||
[C004] Let C004 controller use queue and simplify using macros
|
||||
Added `USES_Cxxx` checks
|
||||
[C007] Let C007 controller use queue
|
||||
[MQTT] Use set controller settings on MQTT controllers
|
||||
[Webserver] Add fields for new Controller settings
|
||||
[C008] Let C008 use queue and some bugfixes
|
||||
[#1666] Moved code duplication for HTTP requests to special functions
|
||||
[C009] Removed port number from HTTP host header
|
||||
[C009] Controller C009 now also uses Delay Queue
|
||||
[Client Connect] Check for success on connecting with client.
|
||||
[C009] Remove 'no port' option in HTTP packet
|
||||
[HTTP Auth] Now using proper index for retrieving credentials
|
||||
[HTTP] Add user agent in request header
|
||||
[C010] Controller C010 now also uses Delay Queue
|
||||
[Cleanup] Merge code duplication between C008 and C010
|
||||
[C011] Controller C011 now also uses Delay Queue
|
||||
[Controller] Moved the 'magic' default and max values to defines
|
||||
[C012] Controller C012 now also uses Delay Queue
|
||||
[C013] Remove some obsolete function
|
||||
[Nodes] Make Nodes list a map with dynamic size
|
||||
[Nodes] Highlight current node in the list of nodes
|
||||
[Cleanup] Memory optimisation in available controllers and plugins
|
||||
[Nodes] Fix String copy to properly read node names
|
||||
[MQTT] Give MQTT state in log when connect state changes.
|
||||
[SPIFFS] Add SPIFFS size + usage to sysinfo page
|
||||
[Watchdog] Add watchdog feed to backgroundtasks() function
|
||||
[MQTT] Force MQTT client reconnect when wifi reconnects
|
||||
[WDT] Add yield() and runbackgroundtasks() when parsing rules
|
||||
[#1707] Getting logs from the /log even when password is not entered
|
||||
[#1714] Parse notification sender to allow some sender formatting
|
||||
[Scheduler] Do not schedule task device timer for tasks without interval
|
||||
[Queue] Add memory check to determine full queue
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180904 (since mega-20180903)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Sep 4 04:00:34 CEST 2018
|
||||
|
||||
TD-er (1):
|
||||
[#1702] Check for duplicate names bug
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180903 (since mega-20180826)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Sep 3 04:01:13 CEST 2018
|
||||
|
||||
TD-er (7):
|
||||
[Pio] Build issues
|
||||
[#1692] Add 2nd Receiver for Email notification
|
||||
Forgot to save before commit
|
||||
[#1549] Add page showing all system variables
|
||||
[sysvar] rename vars with leading zero to %..._0%
|
||||
[#1688] Add dew point computation as standard conversion
|
||||
[Shelly1] Add first test build for Shelly1 nodes
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180826 (since mega-20180822)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Aug 26 04:00:20 CEST 2018
|
||||
|
||||
Gerald Hollweger (1):
|
||||
Optimisations - Save RAM, move constants to flash (CharTableTM1637, CharTableMAX7219) - Remove unnecessary parameter passing (the interface pins do not change during operation for one instance)
|
||||
|
||||
TD-er (16):
|
||||
[MQTT] PubSubClient Add checks for connectivity
|
||||
[Client Connect] Check for success on connecting with client.
|
||||
[Eastron] Add P078 plugin from the Playground (was P150)
|
||||
[SDM] Update library to latest version
|
||||
[Eastron] Make P078 Eastron compile, serial settings do not yet work
|
||||
[Eastron] Add missing SDM_Config_User.h
|
||||
[IR/IRTX] For PullReq #1645 split IR/IRTX to a separate build
|
||||
[JSON] Fix issue when UDP port not set
|
||||
[JSON] Only show node information when generic info is requested
|
||||
[MotorShield] Rename P078 to P079
|
||||
[MotorShield] Cleanup code layout
|
||||
[MotorShield] Added comments in the code and fixes
|
||||
[P080] Dallas iButton Rename P078 to P80
|
||||
[Dallas iButton] Change VType to SENSOR_TYPE_LONG
|
||||
[Build] Add "[TESTING]" labels to P078 and P080
|
||||
[Build Travis] "Scratchpad was set but never used"
|
||||
|
||||
TungstenE2 (2):
|
||||
Added Wemos Motorshield Plugin (No. 78)
|
||||
Added Wemos Motorshield Plugin (No. 78)
|
||||
|
||||
feuerreiter (1):
|
||||
Adds a overview of known espeasy nodes to the json response. I think this can be useful for own webinterfaces etc.
|
||||
|
||||
henfri (2):
|
||||
Create _P078_DallasIButton.ino
|
||||
Update _P078_DallasIButton.ino
|
||||
|
||||
jimmys01 (6):
|
||||
IR library added
|
||||
Travis build complaining about unused vars
|
||||
commented out more unused code
|
||||
Travis please be happy :-)
|
||||
IR RX updated
|
||||
Small correction
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180822 (since mega-20180818)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Aug 22 04:00:15 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Changed INPUT_COMMAND_SIZE from 80 to 240
|
||||
|
||||
TD-er (1):
|
||||
[UDP] Use random outgoing port for NTP and check return values
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180818 (since mega-20180815)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Aug 18 04:00:17 CEST 2018
|
||||
|
||||
Edwin Eefting (1):
|
||||
fixed network config for newer versions
|
||||
|
||||
TD-er (1):
|
||||
[#1650] Use icons in tabs on web page on small screens
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180815 (since mega-20180809)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Aug 15 04:00:13 CEST 2018
|
||||
|
||||
TD-er (1):
|
||||
[core 2.4.2] Update to PlatformIO espressif8266@1.8.0
|
||||
|
||||
krikk (1):
|
||||
Update _P047_i2c-soil-moisture-sensor.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180809 (since mega-20180808)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Aug 9 04:00:06 CEST 2018
|
||||
|
||||
Bartlomiej Zimon (1):
|
||||
fix for #702, setup propper gpio state before send into PCF8574 for pcfpulse,pcflongpulse
|
||||
|
||||
Gijs Noorlander (2):
|
||||
[Blynk] Command handling was not active
|
||||
[Blynk] Change define to allow blynkget command
|
||||
|
||||
TD-er (4):
|
||||
[#1621] Timing interval of BME280 incorrect
|
||||
[C011] Make sure the loaded extra controller settings end with 0
|
||||
[Scheduler] Remove scheduled commands from scheduler to run in idle time
|
||||
[deepsleep] Send values immediately after connections are made
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180808 (since mega-20180804)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Aug 8 04:00:13 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
[#1612] Make P008_RFID using new functions
|
||||
|
||||
TD-er (8):
|
||||
[Scheduler] Rename some functions and clean-up
|
||||
[Notifications] Schedule NPlugin_ptr calls async
|
||||
[MQTT] Schedule from callback functions
|
||||
[Rules] Split parsing single line from rulesProcessingFile
|
||||
[Rules] Split processing matched rule in a separate function
|
||||
[Commands] Return String output per command
|
||||
[Schedule] Scheduled command timer
|
||||
[Schedule] Run command from webpage immediately
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180804 (since mega-20180802)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Aug 4 04:00:23 CEST 2018
|
||||
|
||||
TD-er (5):
|
||||
[#1574] Set event->TaskIndex
|
||||
[Settings File] Show settings file layout
|
||||
[ESP32] Small fix building ESP32
|
||||
[Settings file] Include SettingsStruct in the view
|
||||
[Cleanup] Replace a lot of TR TD with function
|
||||
|
||||
Thomas (1):
|
||||
Update _P075_Nextion.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180802 (since mega-20180801)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Aug 2 04:00:14 CEST 2018
|
||||
|
||||
Grovkillen (1):
|
||||
Added extra info to self compilers
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180801 (since mega-20180723)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Aug 1 04:00:18 CEST 2018
|
||||
|
||||
Edwin Eefting (1):
|
||||
Power down PN532 after reading. Saves power and reduces interference with Wifi.
|
||||
|
||||
TD-er (11):
|
||||
[ExtraTaskSettings] Added some comments & clarifications
|
||||
[ExtraTaskSettings] Added extra clear functions and checks
|
||||
[ExtraTaskSettings] Keep ExtraTaskSettings actualized for plugin calls
|
||||
[meminfodetail] Add command `meminfodetail`
|
||||
[FileHandling] Write some log indicating file operation
|
||||
[compiler fix] Forgot to declare String
|
||||
[Storage] Split storage related functions to new file
|
||||
[Storage] Simplify settings I/O error handling
|
||||
[#882] Add checks on names in task setup
|
||||
[#1379] Check for "forbidden" characters in names.
|
||||
[#1306] Added %R% and %N% for literals \r and \n
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180723 (since mega-20180722)
|
||||
-------------------------------------------------
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
eeprom, data, 0x99, 0x290000,0x1000,
|
||||
spiffs, data, spiffs, 0x291000,0x16F000,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||
eeprom, data, 0x99, 0x3B0000,0x1000,
|
||||
spiffs, data, spiffs, 0x3B1000,0x4F000,
|
||||
|
@@ -70,6 +70,7 @@ ESPeasySoftwareSerial::ESPeasySoftwareSerial(uint8_t receivePin, uint8_t transmi
|
||||
m_rxValid = m_txValid = m_txEnableValid = false;
|
||||
m_buffer = NULL;
|
||||
m_invert = inverse_logic;
|
||||
m_rxEnabled = false;
|
||||
if (isValidGPIOpin(receivePin)) {
|
||||
m_rxPin = receivePin;
|
||||
m_buffSize = buffSize;
|
||||
@@ -140,6 +141,8 @@ uint8_t ESPeasySoftwareSerial::pinToIndex(uint8_t pin) {
|
||||
void ESPeasySoftwareSerial::begin(long speed) {
|
||||
// Use getCycleCount() loop to get as exact timing as possible
|
||||
m_bitTime = ESP.getCpuFreqMHz()*1000000/speed;
|
||||
if (!m_rxEnabled)
|
||||
enableRx(true);
|
||||
}
|
||||
|
||||
void ESPeasySoftwareSerial::setTransmitEnablePin(uint8_t transmitEnablePin) {
|
||||
@@ -160,6 +163,7 @@ void ESPeasySoftwareSerial::enableRx(bool on) {
|
||||
} else {
|
||||
detachInterrupt(m_rxPin);
|
||||
}
|
||||
m_rxEnabled = on;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,12 @@ public:
|
||||
|
||||
void rxRead();
|
||||
|
||||
// AVR compatibility methods
|
||||
bool listen() { enableRx(true); return true; }
|
||||
void end() { stopListening(); }
|
||||
bool isListening() { return m_rxEnabled; }
|
||||
bool stopListening() { enableRx(false); return true; }
|
||||
|
||||
using Print::write;
|
||||
|
||||
private:
|
||||
@@ -62,7 +68,8 @@ private:
|
||||
|
||||
// Member variables
|
||||
uint8_t m_rxPin, m_txPin, m_txEnablePin;
|
||||
bool m_rxValid, m_txValid, m_txEnableValid;
|
||||
bool m_rxValid, m_rxEnabled;
|
||||
bool m_txValid, m_txEnableValid;
|
||||
bool m_invert;
|
||||
unsigned long m_bitTime;
|
||||
uint16_t m_inPos, m_outPos;
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
# Contributing to the IRremoteESP8266 library
|
||||
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
The following is a set of guidelines for contributing to the IRremoteESP8266 library, hosted on GitHub. These are guidelines, [not rules](http://imgur.com/mSHi8). Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||
|
||||
#### Table Of Contents
|
||||
|
||||
[Code of Conduct](#code-of-conduct)
|
||||
|
||||
[How Can I Contribute?](#how-can-i-contribute)
|
||||
* [Reporting Bugs](#reporting-bugs)
|
||||
* [Pull Requests](#pull-requests)
|
||||
|
||||
[Styleguides](#styleguides)
|
||||
* [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
|
||||
* [Git Commit Messages](#git-commit-messages)
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and everyone participating in it is governed by the principle of ["Be excellent to each other"](http://www.imdb.com/title/tt0096928/quotes). That's it. TL;DR: _Don't be a jerk._
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
This section guides you through submitting a bug report for the library. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:.
|
||||
|
||||
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as much detail as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](issue_template.md), the information it asks for helps us resolve issues faster.
|
||||
|
||||
> **Note:** If you find a **Closed** issue that seems like it's the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
|
||||
|
||||
#### Before Submitting A Bug Report
|
||||
|
||||
* **Check the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library.
|
||||
* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Amarkszabo/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
|
||||
|
||||
#### How Do I Submit A (Good) Bug Report?
|
||||
|
||||
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue and provide the following information by filling in [the template](issue_template.md).
|
||||
|
||||
Explain the problem and include any additional details to help maintainers reproduce the problem:
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the problem.
|
||||
* **Describe the exact steps which reproduce the problem** in as much detail as possible.
|
||||
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
|
||||
|
||||
Provide more context by answering these questions:
|
||||
|
||||
* **Can you reproduce the problem in one of the code examples?**
|
||||
* **Did the problem start happening recently** (e.g. after updating to a new version of Arduino or the library) or was this always a problem?
|
||||
* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/markszabo/IRremoteESP8266/releases).
|
||||
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
|
||||
|
||||
Include details about your configuration, circuit and environment:
|
||||
|
||||
* **Which version of the library are you using?** You can get the exact version by inspecting the `library.json` file in the root directory of the library.
|
||||
* **What board are you running this on?**
|
||||
|
||||
### Pull Requests
|
||||
|
||||
* Do not include issue numbers in the PR title
|
||||
* Include as much data and comments as practicle.
|
||||
* Follow the [C++ style guide](https://google.github.io/styleguide/cppguide.html).
|
||||
* Please write or ensure Unit Tests cover the change you are making, if you can.
|
||||
* End all files with a newline
|
||||
* Avoid platform-dependent code.
|
||||
* Use c98 types where possible for better portablity.
|
||||
* In almost all cases, code & documentation should be peer-reviewed by at least one other contributor.
|
||||
* The code should pass all the existing testing infrastructure in Travis. e.g. Unit tests, cpplint, and basic compilation.
|
||||
* State if you have tested this under real conditions if you have, and what other tests you may have carried out.
|
||||
|
||||
### Git Commit Messages
|
||||
|
||||
* Limit the first line to 72 characters or less
|
||||
* Reference issues and pull requests liberally after the first line
|
||||
* Humour is always acceptable. Be liberal with it. ;-)
|
||||
* While not required, a comprehensive description of all the changes in the PR is best.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
## Contributors of this project
|
||||
### Main contributors & maintainers
|
||||
- [Mark Szabo](https://github.com/markszabo/) : Initial IR sending on ESP8266
|
||||
- [Sébastien Warin](https://github.com/sebastienwarin/) (http://sebastien.warin.fr) : Initial IR receiving on ESP8266
|
||||
- [David Conran](https://github.com/crankyoldgit/)
|
||||
- [Roi Dayan](https://github.com/roidayan/)
|
||||
- [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/)
|
||||
- [Massimiliano Pinto](https://github.com/pintomax/)
|
||||
- [Darsh Patel](https://github.com/darshkpatel/)
|
||||
- [Jonny Graham](https://github.com/jonnygraham/)
|
||||
- [Stu Fisher](https://github.com/stufisher/)
|
||||
- [Jorge Cisneros](https://github.com/jorgecis/)
|
||||
|
||||
All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors).
|
||||
|
||||
### Contributors of the [original project](https://github.com/z3t0/Arduino-IRremote) can be found on the [original project's contributors page](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_
|
||||
|
||||
### Version/revison of the library used
|
||||
_Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library.
|
||||
e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._
|
||||
|
||||
### Expected behavior
|
||||
_What steps did you do and what should it have done?_
|
||||
|
||||
e.g.
|
||||
1. Initialise the IRsend class.
|
||||
2. IRsend.sendFoobar(0xdeadbeef);
|
||||
3. Foobar branded BBQ turns on and cooks me some ribs.
|
||||
|
||||
### Actual behavior
|
||||
_What steps did you do, and what did or didn't actually happen?_
|
||||
|
||||
e.g.
|
||||
1. Initialise the IRsend class.
|
||||
2. IRsend.sendFoobar(0xdeadbeef);
|
||||
3. Foobar BBQ went into Cow(er)-saving mode and fried me a couple of eggs instead.
|
||||
|
||||
#### Output of raw data from IRrecvDumpV2.ino (if applicable)
|
||||
_Include some raw dumps of what the device saw._
|
||||
|
||||
### Steps to reproduce the behavior
|
||||
_What can we do to (pref. reliably) repeat what is happening?_
|
||||
|
||||
#### Example code used
|
||||
_Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._
|
||||
|
||||
#### Circuit diagram and hardware used (if applicable)
|
||||
_Link to an image of the circuit diagram used. Part number of the IR receiver module etc._
|
||||
|
||||
### I have followed the steps in the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions)
|
||||
_Yes/No._
|
||||
|
||||
### Has this library/code previously worked as expected for you?
|
||||
_Yes/No. If "Yes", which version last worked for you?_
|
||||
|
||||
### Other useful information
|
||||
_More information is always welcome. Be verbose._
|
||||
@@ -0,0 +1,42 @@
|
||||
#----------------------------------------#
|
||||
# .gitingore for IRremoteESP8266 library #
|
||||
#----------------------------------------#
|
||||
|
||||
### Files to ignore.
|
||||
|
||||
## Editors
|
||||
# vi/vim
|
||||
**/*.swp
|
||||
|
||||
## Build environments
|
||||
# Platformio
|
||||
**/.pioenvs/
|
||||
**/.piolibdeps/
|
||||
**/.clang_complete
|
||||
**/.gcc-flags.json
|
||||
examples/**/lib
|
||||
examples/**/.travis.yml
|
||||
examples/**/.gitignore
|
||||
lib/readme.txt
|
||||
lib/googletest/**/*
|
||||
|
||||
# GCC pre-compiled headers.
|
||||
**/*.gch
|
||||
|
||||
# Python compiled files
|
||||
**/*.pyc
|
||||
|
||||
# Unit Test builds
|
||||
test/*.o
|
||||
test/*.a
|
||||
test/*_test
|
||||
|
||||
# Tools builds
|
||||
tools/*.o
|
||||
tools/*.a
|
||||
tools/gc_decode
|
||||
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "lib/googletest"]
|
||||
path = lib/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
@@ -0,0 +1,3 @@
|
||||
[style]
|
||||
based_on_style: google
|
||||
indent_width: 2
|
||||
@@ -10,13 +10,19 @@ before_install:
|
||||
- tar xf arduino-1.8.2-linux64.tar.xz
|
||||
- sudo mv arduino-1.8.2 /usr/local/share/arduino
|
||||
- sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino
|
||||
- wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py
|
||||
install:
|
||||
- ln -s $PWD /usr/local/share/arduino/libraries/
|
||||
- git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager
|
||||
- git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient
|
||||
- arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs
|
||||
- arduino --install-boards esp8266:esp8266
|
||||
- arduino --board $BD --save-prefs
|
||||
- arduino --pref "compiler.warning_level=all" --save-prefs
|
||||
- sudo apt-get install jq
|
||||
- sudo pip install pylint
|
||||
script:
|
||||
# Check that everything compiles.
|
||||
- arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino
|
||||
- arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino
|
||||
- arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino
|
||||
@@ -25,8 +31,29 @@ script:
|
||||
- arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino
|
||||
- arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino
|
||||
- arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino
|
||||
- arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino
|
||||
# Also check the tools programs compile.
|
||||
- (cd tools; make all)
|
||||
# Check for lint issues.
|
||||
- shopt -s nullglob
|
||||
- python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino}
|
||||
- pylint {src,test,tools}/*.py
|
||||
- shopt -u nullglob
|
||||
# Build and run the unit tests.
|
||||
- (cd test; make run)
|
||||
- (cd tools; make run_tests)
|
||||
# Check the version numbers match.
|
||||
- LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2)
|
||||
- test ${LIB_VERSION} == "$(jq -r .version library.json)"
|
||||
- grep -q "^version=${LIB_VERSION}$" library.properties
|
||||
|
||||
notifications:
|
||||
email:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
set noparent
|
||||
root=src
|
||||
linelength=80
|
||||
@@ -1,38 +1,63 @@
|
||||
# IRremote ESP8266 Library
|
||||
|
||||
[](https://travis-ci.org/markszabo/IRremoteESP8266)
|
||||
[](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue")
|
||||
[](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open")
|
||||
[](https://gitlicense.com/license/markszabo/IRremoteESP8266)
|
||||
|
||||
This library enables you to **send and receive** infra-red signals on an ESP8266 using Arduino framework (https://github.com/esp8266/Arduino)
|
||||
This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc.
|
||||
|
||||
This library is based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/)
|
||||
## v2.4.3 Now Available
|
||||
Version 2.4.3 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes.
|
||||
|
||||
[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class).
|
||||
#### Upgrading from pre-v2.0
|
||||
Usage of the library slight changed at v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.
|
||||
|
||||
Seb's notes : I also changed the pulse parameters for Samsung, update the Panasonic and Samsung decoders and remove the SANYO decoders. The IR decoder was successfully tested with Panasonic and Samsung remote controls.
|
||||
## Troubleshooting
|
||||
Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first.
|
||||
|
||||
## Frequently Asked Questions
|
||||
Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions).
|
||||
|
||||
## Installation
|
||||
1. Click "Download ZIP"
|
||||
2. Extract the downloaded zip file
|
||||
3. Rename the extracted folder to "IRremoteESP8266"
|
||||
4. Move this folder to your libraries directory (under windows: C:\Users\YOURNAME\Documents\Arduino\libraries\)
|
||||
5. Restart your Arduino ide
|
||||
6. Check out the examples
|
||||
##### Official releases via the Arduino IDE v1.8+ (Windows & Linux)
|
||||
1. Click the _"Sketch"_ -> _"Include Library"_ -> _"Manage Libraries..."_ Menu items.
|
||||
1. Enter `IRremoteESP8266` into the _"Filter your search..."_ top right search box.
|
||||
1. Click on the IRremoteESP8266 result of the search.
|
||||
1. Select the version you wish to install and click _"Install"_.
|
||||
|
||||
###### Using Git to install library ( Linux )
|
||||
##### Manual Installation for Windows
|
||||
1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/markszabo/IRremoteESP8266/archive->master.zip)"_ on the page.
|
||||
1. Extract the contents of the downloaded zip file.
|
||||
1. Rename the extracted folder to _"IRremoteESP8266"_.
|
||||
1. Move this folder to your libraries directory. (under windows: `C:\Users\YOURNAME\Documents\Arduino\libraries\`)
|
||||
1. Restart your Arduino IDE.
|
||||
1. Check out the examples.
|
||||
|
||||
##### Using Git to install library ( Linux )
|
||||
```
|
||||
cd ~/Arduino/libraries
|
||||
git clone https://github.com/markszabo/IRremoteESP8266.git
|
||||
```
|
||||
###### To Update to the latest version of the library
|
||||
`
|
||||
```
|
||||
cd ~/Arduino/libraries/IRremoteESP8266 && git pull
|
||||
`
|
||||
```
|
||||
|
||||
## Contributing
|
||||
If you want to contribute to this project:
|
||||
- Report bugs and errors
|
||||
If you want to [contribute](.github/CONTRIBUTING.md#how-can-i-contribute) to this project, consider:
|
||||
- [Report](.github/CONTRIBUTING.md#reporting-bugs) bugs and errors
|
||||
- Ask for enhancements
|
||||
- Create issues and pull requests
|
||||
- Improve our documentation
|
||||
- [Create issues](.github/CONTRIBUTING.md#reporting-bugs) and [pull requests](.github/CONTRIBUTING.md#pull-requests)
|
||||
- Tell other people about this library
|
||||
|
||||
## Contributors
|
||||
Check [here](Contributors.md)
|
||||
Available [here](.github/Contributors.md)
|
||||
|
||||
## Library History
|
||||
This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/)
|
||||
|
||||
[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class).
|
||||
|
||||
As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind.
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
# Release Notes
|
||||
|
||||
## _v2.4.3 (20180727)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Handle Space Gaps better in auto analyse tool. (#482)
|
||||
- Correct min repeat for GICABLE in IRMQTTServer. (#494)
|
||||
|
||||
**[Features]**
|
||||
- Add static IP config option to IRMQTTServer (#480)
|
||||
- Full decoding/encoding support for the Haier YRW02 A/C. (#485 #486 #487)
|
||||
|
||||
**[Misc]**
|
||||
- Update LG (28-bit) HDR mark and space timings. (#492)
|
||||
- Spelling and grammar fixes (#491)
|
||||
|
||||
|
||||
## _v2.4.2 (20180601)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Timing Fix: Update the period offset compensation.
|
||||
|
||||
**[Features]**
|
||||
- Improvements for IRMQTTServer example (#466)
|
||||
|
||||
|
||||
## _v2.4.1 (20180520)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Fix crash in IRMQTTServer when compiled under Arduino IDE. (#455)
|
||||
- Default bit length not set for RCMM in IRMQTTServer example. (#456)
|
||||
- Bad acknowledgements for some A/C protocols in IRMQTTServer example. (#460)
|
||||
|
||||
**[Features]**
|
||||
- Allow disabling the use of delay() calls. (#450)
|
||||
- Initial support for G.I. Cable protocol. (#458)
|
||||
- Support of Hitachi A/C 13 & 53 byte messages. (#461)
|
||||
|
||||
**[Misc]**
|
||||
- Auto Analyse Raw Data script converted to Python. (#454)
|
||||
|
||||
## _v2.4.0 (20180407)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Add missing WiFi.begin() call to IRGCTCPServer example. (#433)
|
||||
- Add missing sendHaierAC() to IRMQTTServer example. (#434 & #444)
|
||||
- Make mqtt clientid unique in IRMQTTServer example. (#444)
|
||||
|
||||
**[Features]**
|
||||
|
||||
- Initial Mitsubishi projector protocol support. (#442)
|
||||
- Experimental support of Hitachi A/C messages. (#445)
|
||||
- Improve transmission pulse modulation support.
|
||||
Allow disabling of transmission frequency modulation.(#439)
|
||||
|
||||
**[Misc]**
|
||||
- IRMQTTServer example improvements. (#444)
|
||||
|
||||
|
||||
## _v2.3.3 (20180302)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Ensure the IR LED is off before we start. (#405)
|
||||
|
||||
**[Features]**
|
||||
- Experimental decode support for Gree HVAC units (#397)
|
||||
- Initial support for Haier A/Cs. (#409)
|
||||
- Improve timing accuracy of unit tests. (#403)
|
||||
- Rework matchData() to handle equal total data bit time protocols. (#408)
|
||||
|
||||
**[Misc]**
|
||||
- Add startup text to IRrecvDumpV2 and IRrecvDemo (#412)
|
||||
- Tweak timings on Fujitsu A/C header (#418)
|
||||
- AutoAnalyseRawData.sh: Add some support for handling larger than 64 bit codes. (#419)
|
||||
- Use better comments for send GPIO in examples. (#425)
|
||||
|
||||
|
||||
## _v2.3.2 (20180126)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Integer underflow caused device not to respond in `sendJVC()` (#401)
|
||||
|
||||
**[Features]**
|
||||
- Initial support for sending & receiving Carrier HVAC codes. (#387)
|
||||
- Add Pronto HEX code support to _gc_decode_ tool. (#388)
|
||||
|
||||
**[Misc]**
|
||||
- Make mDNS independent of MQTT in IRMQTTServer example code. (#390 #391)
|
||||
|
||||
|
||||
## _v2.3.1 (20171229)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Setting `#define SEND_FUJITSU_AC false` caused a compilation error (#375)
|
||||
- Integer underflow caused huge `space()` in `sendGeneric()` (#381)
|
||||
|
||||
**[Features]**
|
||||
- Support sending & receiving Lasertag codes. (#374)
|
||||
- Reduce the library footprint by using a new `sendGeneric()` routine. (#373)
|
||||
|
||||
**[Misc]**
|
||||
- Lots of grammar & typo fixes. (#378)
|
||||
- Update keywords.txt for Arduino IDE users (#371)
|
||||
- Update pins in examples so they are compatible with Adafruit boards. (#383)
|
||||
|
||||
|
||||
## _v2.3.0 (20171208)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Panasonic-based protocols had incorrect message gap. (#358)
|
||||
- Formatting error for large rawData values in example code. (#355)
|
||||
- Off-by-one error in payload_copy malloc. (#337)
|
||||
- Off-by-one error in unit test helper routines (#363)
|
||||
|
||||
**[Features]**
|
||||
- Support sending and receiving Midea A/C codes.
|
||||
- Support for receiving Kelvinator A/C codes. (#332)
|
||||
- Support more operation features for Daikin A/Cs.
|
||||
- Support for decoding Daikin A/Cs.
|
||||
- Support sending and receiving Toshiba A/Cs. (#333)
|
||||
- Support sending and receiving AR-DB1 Fujitsu A/C codes. (#367)
|
||||
- Add new AutoAnalyseRawData.sh & RawToGlobalCache.sh tools (#345) (#343)
|
||||
- Support for MagiQuest wands. (#365)
|
||||
|
||||
**[Misc]**
|
||||
- Add checksum verification to Kelvinator A/C decodes. (#348)
|
||||
- Changes to the threshold reporting of UNKNOWN messages (#347)
|
||||
- Major re-work of Daikin A/C support.
|
||||
- Sending for all A/Cs added to MQTT example code.
|
||||
- MQTT example code improvements. (#334)
|
||||
- IRrecvDumpV2 significant output improvements. (#363)
|
||||
- Improved unit test coverage for the library.
|
||||
|
||||
|
||||
## _v2.2.1 (20171025)_
|
||||
|
||||
**[Features]**
|
||||
- Support for sending and decoding Nikai TV messages. (#311, #313)
|
||||
- gc_decode: External utility to decode Global Cache codes. (#308, #312)
|
||||
- IRMQTTServer: Example code to send IR messages via HTTP & MQTT. (#316, #323)
|
||||
- Improve converting 64bit values to hexadecimal. (#318)
|
||||
|
||||
**[Misc]**
|
||||
- IRrecvDump.ino code is now deprecated. Use IRrecvDumpV2.ino instead. (#314)
|
||||
|
||||
|
||||
## _v2.2.0 (20170922)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- Add printing output of RC-MM and RC-5X protocols in example code. (#284)
|
||||
- LG timing improvements based on observations (#291)
|
||||
|
||||
**[Features]**
|
||||
- Automatic capture timing calibration for some protocols. (#268)
|
||||
- Support for creating & sending Trotec AC codes. (#279)
|
||||
- Support for creating & sending Argo Ulisse 13 DCI codes. (#280 #300)
|
||||
- Move to 2 microsecond timing resolution for capture of codes. (#287)
|
||||
- Capture buffer changes:
|
||||
- Size at runtime. (#276)
|
||||
- Message timeout at runtime. (#294)
|
||||
- Simplify creating & using a second buffer (#303)
|
||||
- New example code:
|
||||
- Trotec A/C (#279)
|
||||
- LG A/C units (#289)
|
||||
- Argo Ulisse 13 DCI codes. (#300)
|
||||
|
||||
|
||||
## _v2.1.1 (20170711)_
|
||||
|
||||
**[Bug Fixes]**
|
||||
- GlobalCache incorrectly using hardware offset for period calc. (#267)
|
||||
|
||||
**[Features]**
|
||||
- Support reporting of 'NEC'-like 32-bit protocols. e.g. Apple TV remote (#265)
|
||||
- Add an example of sendRaw() to IRsendDemo.ino (#270)
|
||||
|
||||
|
||||
## _v2.1.0 (20170704)_
|
||||
|
||||
**[Features]**
|
||||
- Support for sending Pronto IR codes. (#248)
|
||||
- Support for sending Fujitsu A/C codes. (#88)
|
||||
- Minor improvements to examples.
|
||||
|
||||
|
||||
## _v2.0.3 (20170618)_
|
||||
|
||||
**[Bug fixes]**
|
||||
- Capture buffer could become corrupt after large message, breaking subsequent decodes. (#253)
|
||||
|
||||
|
||||
## _v2.0.2 (20170615)_
|
||||
|
||||
**[Bug fixes]**
|
||||
- Correct decode issue introduced in v2.0.1 affecting multiple protocol decoders (#243)
|
||||
- Correct post-message gap for the Panasonic protocol(s) (#245)
|
||||
- Incorrect display of the decoded uint64_t value in the example code. (#245)
|
||||
|
||||
|
||||
## _v2.0.1 (20170614)_
|
||||
|
||||
**[Bug fixes]**
|
||||
- Decoding protocols when it doesn't detect a post-command gap, and there is no more data. (#243)
|
||||
- Incorrect minimum size calculation when there is no post-command gap. (#243)
|
||||
|
||||
|
||||
## _v2.0.0 - 64 bit support and major improvements (20170612)_
|
||||
|
||||
**[Misc]**
|
||||
- This is almost a complete re-write of the library.
|
||||
|
||||
**[Features]**
|
||||
- All suitable protocols now handle 64-bit data messages and are repeatable via an optional argument.
|
||||
- Unit tests for all protocols.
|
||||
- Far better and stricter decoding for most protocols.
|
||||
- Address & command decoding for protocols where that information is available.
|
||||
- Much more precise timing for generation of signals sent.
|
||||
- Lower duty-cycles for some protocols.
|
||||
- Several new protocols added, and some new sending and decoding routines for existing ones.
|
||||
- Ability to optionally chose which protocols are included, enabling faster decoding and smaller code footprints if desired.
|
||||
- Support for far larger capture buffers. (e.g. RAWLEN > 256)
|
||||
|
||||
**[Bug fixes]**
|
||||
- Numerous bug fixes.
|
||||
|
||||
|
||||
## _v1.2.0 (20170429)_
|
||||
|
||||
**[Features]**
|
||||
- Add ability to copy IR capture buffer, and continue capturing. Means faster and better IR command decoding.
|
||||
- Reduce IRAM usage by 28 bytes.
|
||||
- Improve capture of RC-MM & Panasonic protocols.
|
||||
- Upgrade IRrecvDumpV2 to new IR capture buffer. Much fewer corrupted/truncated IR messages.
|
||||
|
||||
|
||||
## _v1.1.1 (20170413)_
|
||||
|
||||
**[Bug fixes]**
|
||||
- Fix a reported problem when sending the LG protocol. Preemptive fix for possible similar cases.
|
||||
- Fix minor issues in examples.
|
||||
|
||||
**[Features]**
|
||||
- Add documentation to some examples to aid new people.
|
||||
- Add ALPHA support for RC-MM protocol. (Known to be currently not 100% working.)
|
||||
@@ -1,26 +1,71 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRsendGCDemo - demonstrates sending Global Cache-formatted IR codes with IRsend
|
||||
* An IR LED must be connected to ESP8266 pin 0.
|
||||
* IRremoteESP8266: IRsendGCDemo
|
||||
* demonstrates sending Global Cache-formatted IR codes with IRsend
|
||||
* Copyright 2009 Ken Shirriff
|
||||
* http://arcfn.com
|
||||
*
|
||||
* Version 0.2 June, 2017
|
||||
* Added helpful comments
|
||||
* Better includes files.
|
||||
* Version 0.1 30 March, 2016
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* Based on Ken Shirriff's IrsendDemo
|
||||
* Version 0.1 July, 2009
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
|
||||
// Codes are in Global Cache format less the emitter ID and request ID. These codes can be found in GC's Control Tower database.
|
||||
// Codes are in Global Cache format less the emitter ID and request ID.
|
||||
// These codes can be found in GC's Control Tower database.
|
||||
|
||||
unsigned int Samsung_power_toggle[71] = {38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,63,20,63,20,63,20,63,20,1798};
|
||||
uint16_t Samsung_power_toggle[71] = {
|
||||
38000, 1, 1, 170, 170, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20,
|
||||
20, 20, 20, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
20, 20, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20,
|
||||
20, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 1798};
|
||||
|
||||
IRsend irsend(4); //an IR emitter led is connected to GPIO pin 4
|
||||
|
||||
void setup()
|
||||
{
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
void setup() {
|
||||
irsend.begin();
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Toggling power");
|
||||
#if SEND_GLOBALCACHE
|
||||
irsend.sendGC(Samsung_power_toggle, 71);
|
||||
#else // SEND_GLOBALCACHE
|
||||
Serial.println("Can't send because SEND_GLOBALCACHE has been disabled.");
|
||||
#endif // SEND_GLOBALCACHE
|
||||
delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,85 +1,133 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRGCTCPServer - send Global Cache-formatted codes via TCP.
|
||||
* An IR emitter must be connected to GPIO pin 4.
|
||||
* Version 0.1 1 April, 2016
|
||||
* Hisham Khalifa, http://www.hishamkhalifa.com
|
||||
* Version 0.2 May, 2017
|
||||
* Copyright 2016 Hisham Khalifa, http://www.hishamkhalifa.com
|
||||
* Copyright 2017 David Conran
|
||||
*
|
||||
* Example command - Samsung TV power toggle: 38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,63,20,63,20,63,20,63,20,1798\r\n
|
||||
* For more codes, visit: https://irdb.globalcache.com/
|
||||
*
|
||||
* How to use this program:
|
||||
* 1) Update "ssid" and "password" below for your WIFI network.
|
||||
* 2) Compile and upload the sketch to your ESP8266 module.
|
||||
* 3) (Optional) Use the serial connection to confirm it started and get the
|
||||
* IP address.
|
||||
* 4) From a client machine, connect to port 4998 on the ESP8266, using
|
||||
* 'telnet', 'nc' (netcat), 'putty' or similar command, etc.
|
||||
* You may need to install one.
|
||||
* Unix/OSX:
|
||||
* Start a shell. Then type:
|
||||
* telnet <esp8266deviceIPaddress> 4998
|
||||
* Windows:
|
||||
* Start a new CMD window, then type:
|
||||
* telnet <esp8266deviceIPaddress> 4998
|
||||
*
|
||||
* 5) Enter a Global Cache-formatted code, starting at the frequency,
|
||||
* and then a return/enter at the end. No spaces. e.g.:
|
||||
*
|
||||
* 38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,63,20,63,20,63,20,63,20,1798
|
||||
*
|
||||
* To exit the 'telnet' command:
|
||||
* press <control> + <]> at the same time, then press 'q', and then <return>.
|
||||
* or:
|
||||
* <control> + <d> might work.
|
||||
*
|
||||
* This program will display the ESP's IP address on the serial console, or you
|
||||
* can check your wifi router for it's address.
|
||||
*/
|
||||
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRremoteInt.h>
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
|
||||
const char* ssid = "...";
|
||||
const char* password = "...";
|
||||
const char* ssid = "..."; // Put your WIFI SSID here.
|
||||
const char* password = "..."; // Put your WIFI password here.
|
||||
|
||||
WiFiServer server(4998); // Uses port 4998.
|
||||
WiFiServer server(4998); // Uses port 4998.
|
||||
WiFiClient client;
|
||||
|
||||
unsigned int *codeArray;
|
||||
IRsend irsend(4); //an IR emitter led is connected to GPIO pin 4
|
||||
uint16_t *code_array;
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
void parseString(String str) {
|
||||
int nextIndex;
|
||||
int codeLength = 1;
|
||||
int currentIndex = 0;
|
||||
nextIndex = str.indexOf(',');
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
// change to do/until and remove superfluous repetition below...
|
||||
while (nextIndex != -1) {
|
||||
if (codeLength > 1) {
|
||||
codeArray = (unsigned int*) realloc(codeArray, codeLength * sizeof(unsigned int));
|
||||
} else {
|
||||
codeArray = (unsigned int*) malloc(codeLength * sizeof(unsigned int));
|
||||
}
|
||||
void sendGCString(String str) {
|
||||
int16_t index;
|
||||
uint16_t count;
|
||||
|
||||
codeArray[codeLength-1] = (unsigned int) (str.substring(currentIndex, nextIndex).toInt());
|
||||
|
||||
codeLength++;
|
||||
currentIndex = nextIndex + 1;
|
||||
nextIndex = str.indexOf(',', currentIndex);
|
||||
// Find out how many items there are in the string.
|
||||
index = -1;
|
||||
count = 1;
|
||||
do {
|
||||
index = str.indexOf(',', index + 1);
|
||||
count++;
|
||||
} while (index != -1);
|
||||
|
||||
// Now we know how many there are, allocate the memory to store them all.
|
||||
code_array = reinterpret_cast<uint16_t*>(malloc(count * sizeof(uint16_t)));
|
||||
// Check we malloc'ed successfully.
|
||||
if (code_array == NULL) { // malloc failed, so give up.
|
||||
Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n",
|
||||
count * sizeof(uint16_t), ESP.getFreeHeap());
|
||||
Serial.println("Giving up & forcing a reboot.");
|
||||
ESP.restart(); // Reboot.
|
||||
delay(500); // Wait for the restart to happen.
|
||||
return; // Should never get here, but just in case.
|
||||
}
|
||||
codeArray = (unsigned int*) realloc(codeArray, codeLength * sizeof(unsigned int));
|
||||
codeArray[codeLength-1] = (unsigned int) (str.substring(currentIndex, nextIndex).toInt());
|
||||
|
||||
irsend.sendGC(codeArray,codeLength);
|
||||
|
||||
// Now convert the strings to integers and place them in code_array.
|
||||
count = 0;
|
||||
uint16_t start_from = 0;
|
||||
do {
|
||||
index = str.indexOf(',', start_from);
|
||||
code_array[count] = str.substring(start_from, index).toInt();
|
||||
start_from = index + 1;
|
||||
count++;
|
||||
} while (index != -1);
|
||||
|
||||
#if SEND_GLOBALCACHE
|
||||
irsend.sendGC(code_array, count); // All done. Send it.
|
||||
#endif // SEND_GLOBALCACHE
|
||||
free(code_array); // Free up the memory allocated.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// initialize serial:
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
Serial.println(" ");
|
||||
Serial.println("IR TCP Server");
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(900);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
server.begin();
|
||||
IPAddress myAddress = WiFi.localIP();
|
||||
Serial.println(myAddress);
|
||||
irsend.begin();
|
||||
server.begin();
|
||||
IPAddress myAddress = WiFi.localIP();
|
||||
Serial.println(myAddress.toString());
|
||||
irsend.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
while(!client) {
|
||||
while (!client)
|
||||
client = server.available();
|
||||
}
|
||||
|
||||
while(!client.connected()){
|
||||
while (!client.connected()) {
|
||||
delay(900);
|
||||
client = server.available();
|
||||
}
|
||||
|
||||
if(client.available()){
|
||||
String irCode = client.readStringUntil('\r'); // Exclusive of \r
|
||||
client.readStringUntil('\n'); // Skip new line as well
|
||||
|
||||
if (client.available()) {
|
||||
String ir_code_str = client.readStringUntil('\r'); // Exclusive of \r
|
||||
client.readStringUntil('\n'); // Skip new line as well
|
||||
client.flush();
|
||||
parseString(irCode);
|
||||
}
|
||||
}
|
||||
sendGCString(ir_code_str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags = -DMQTT_MAX_PACKET_SIZE=512
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
PubSubClient
|
||||
WifiManager@0.12
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
|
||||
[env:d1_mini]
|
||||
platform=espressif8266
|
||||
framework=arduino
|
||||
board=d1_mini
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,39 +1,78 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver
|
||||
* An IR LED must be connected to ESP8266 pin 0.
|
||||
* Version 0.1 June, 2015
|
||||
* Version 0.2 June, 2017
|
||||
* Copyright 2015 Mark Szabo
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <IRremoteESP8266.h>
|
||||
|
||||
#include <IRsend.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
const char* ssid = ".....";
|
||||
const char* password = ".....";
|
||||
MDNSResponder mdns;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
IRsend irsend(0);
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
void handleRoot() {
|
||||
server.send(200, "text/html", "<html><head> <title>ESP8266 Demo</title></head><body><h1>Hello from ESP8266, you can send NEC encoded IR signals from here!</h1><p><a href=\"ir?code=16769055\">Send 0xFFE01F</a></p><p><a href=\"ir?code=16429347\">Send 0xFAB123</a></p><p><a href=\"ir?code=16771222\">Send 0xFFE896</a></p></body></html>");
|
||||
server.send(200, "text/html",
|
||||
"<html>" \
|
||||
"<head><title>ESP8266 Demo</title></head>" \
|
||||
"<body>" \
|
||||
"<h1>Hello from ESP8266, you can send NEC encoded IR" \
|
||||
"signals from here!</h1>" \
|
||||
"<p><a href=\"ir?code=16769055\">Send 0xFFE01F</a></p>" \
|
||||
"<p><a href=\"ir?code=16429347\">Send 0xFAB123</a></p>" \
|
||||
"<p><a href=\"ir?code=16771222\">Send 0xFFE896</a></p>" \
|
||||
"</body>" \
|
||||
"</html>");
|
||||
}
|
||||
|
||||
void handleIr(){
|
||||
for (uint8_t i=0; i<server.args(); i++){
|
||||
if(server.argName(i) == "code")
|
||||
{
|
||||
unsigned long code = strtoul(server.arg(i).c_str(), NULL, 10);
|
||||
void handleIr() {
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
if (server.argName(i) == "code") {
|
||||
uint32_t code = strtoul(server.arg(i).c_str(), NULL, 10);
|
||||
#if SEND_NEC
|
||||
irsend.sendNEC(code, 32);
|
||||
#endif // SEND_NEC
|
||||
}
|
||||
}
|
||||
handleRoot();
|
||||
}
|
||||
|
||||
void handleNotFound(){
|
||||
void handleNotFound() {
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
@@ -42,15 +81,14 @@ void handleNotFound(){
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i=0; i<server.args(); i++){
|
||||
for (uint8_t i = 0; i < server.args(); i++)
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
void setup(void){
|
||||
|
||||
void setup(void) {
|
||||
irsend.begin();
|
||||
|
||||
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
@@ -64,25 +102,25 @@ void setup(void){
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.println(WiFi.localIP().toString());
|
||||
|
||||
if (mdns.begin("esp8266", WiFi.localIP())) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
|
||||
server.on("/", handleRoot);
|
||||
server.on("/ir", handleIr);
|
||||
|
||||
server.on("/ir", handleIr);
|
||||
|
||||
server.on("/inline", [](){
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void){
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,28 +1,52 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRrecvDemo - demonstrates receiving IR codes with IRrecv
|
||||
* This is very simple teaching code to show you how to use the library.
|
||||
* If you are trying to decode your Infra-Red remote(s) for later replay,
|
||||
* use the IRrecvDumpV2.ino example code instead of this.
|
||||
* An IR detector/demodulator must be connected to the input RECV_PIN.
|
||||
* Version 0.1 Sept, 2015
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* Example circuit diagram:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
|
||||
* Changes:
|
||||
* Version 0.2 June, 2017
|
||||
* Changed GPIO pin to the same as other examples.
|
||||
* Used our own method for printing a uint64_t.
|
||||
* Changed the baud rate to 115200.
|
||||
* Version 0.1 Sept, 2015
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
|
||||
int RECV_PIN = 2; //an IR detector/demodulatord is connected to GPIO pin 2
|
||||
// An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU
|
||||
// board).
|
||||
uint16_t RECV_PIN = 14;
|
||||
|
||||
IRrecv irrecv(RECV_PIN);
|
||||
|
||||
decode_results results;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
irrecv.enableIRIn(); // Start the receiver
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
irrecv.enableIRIn(); // Start the receiver
|
||||
while (!Serial) // Wait for the serial connection to be establised.
|
||||
delay(50);
|
||||
Serial.println();
|
||||
Serial.print("IRrecvDemo is now running and waiting for IR message on Pin ");
|
||||
Serial.println(RECV_PIN);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (irrecv.decode(&results)) {
|
||||
Serial.println(results.value, HEX);
|
||||
irrecv.resume(); // Receive the next value
|
||||
// print() & println() can't handle printing long longs. (uint64_t)
|
||||
serialPrintUint64(results.value, HEX);
|
||||
Serial.println("");
|
||||
irrecv.resume(); // Receive the next value
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,89 +1,104 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRrecvDump - dump details of IR codes with IRrecv
|
||||
* Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
*
|
||||
***** DEPRECATED - DO NOT USE *****
|
||||
* Unless you know what you are doing, you should be using the
|
||||
* IRrecvDumpV2.ino sketch/example instead for capturing & decoding IR messages.
|
||||
* In almost ALL ways it is BETTER, FASTER, and MORE DETAILED.
|
||||
*
|
||||
* This code is left only for legacy reasons, and as another simple example of
|
||||
* how to use the IRremoteESP8266 library.
|
||||
*
|
||||
* As of November 2017 it will no longer be updated or supported.
|
||||
* You have been warned.
|
||||
***** DEPRECATED - DO NOT USE *****
|
||||
*
|
||||
* An IR detector/demodulator must be connected to the input RECV_PIN.
|
||||
* Version 0.1 Sept, 2015
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
|
||||
* Version 0.2 Oct 2017
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
|
||||
* JVC and Panasonic protocol added by Kristian Lauszus
|
||||
* (Thanks to zenwheel and other people at the original blog post)
|
||||
* LG added by Darryl Smith (based on the JVC protocol)
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
|
||||
int RECV_PIN = 2; //an IR detector/demodulatord is connected to GPIO pin 2
|
||||
// an IR detector/demodulator is connected to GPIO pin 2
|
||||
uint16_t RECV_PIN = 2;
|
||||
|
||||
IRrecv irrecv(RECV_PIN);
|
||||
|
||||
decode_results results;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
irrecv.enableIRIn(); // Start the receiver
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
irrecv.enableIRIn(); // Start the receiver
|
||||
}
|
||||
|
||||
|
||||
void dump(decode_results *results) {
|
||||
// Dumps out the decode_results structure.
|
||||
// Call this after IRrecv::decode()
|
||||
int count = results->rawlen;
|
||||
uint16_t count = results->rawlen;
|
||||
if (results->decode_type == UNKNOWN) {
|
||||
Serial.print("Unknown encoding: ");
|
||||
}
|
||||
else if (results->decode_type == NEC) {
|
||||
} else if (results->decode_type == NEC) {
|
||||
Serial.print("Decoded NEC: ");
|
||||
|
||||
}
|
||||
else if (results->decode_type == SONY) {
|
||||
} else if (results->decode_type == SONY) {
|
||||
Serial.print("Decoded SONY: ");
|
||||
}
|
||||
else if (results->decode_type == RC5) {
|
||||
} else if (results->decode_type == RC5) {
|
||||
Serial.print("Decoded RC5: ");
|
||||
}
|
||||
else if (results->decode_type == RC6) {
|
||||
} else if (results->decode_type == RC5X) {
|
||||
Serial.print("Decoded RC5X: ");
|
||||
} else if (results->decode_type == RC6) {
|
||||
Serial.print("Decoded RC6: ");
|
||||
}
|
||||
else if (results->decode_type == PANASONIC) {
|
||||
} else if (results->decode_type == RCMM) {
|
||||
Serial.print("Decoded RCMM: ");
|
||||
} else if (results->decode_type == PANASONIC) {
|
||||
Serial.print("Decoded PANASONIC - Address: ");
|
||||
Serial.print(results->panasonicAddress, HEX);
|
||||
Serial.print(results->address, HEX);
|
||||
Serial.print(" Value: ");
|
||||
}
|
||||
else if (results->decode_type == LG) {
|
||||
} else if (results->decode_type == LG) {
|
||||
Serial.print("Decoded LG: ");
|
||||
}
|
||||
else if (results->decode_type == JVC) {
|
||||
} else if (results->decode_type == JVC) {
|
||||
Serial.print("Decoded JVC: ");
|
||||
}
|
||||
else if (results->decode_type == AIWA_RC_T501) {
|
||||
} else if (results->decode_type == AIWA_RC_T501) {
|
||||
Serial.print("Decoded AIWA RC T501: ");
|
||||
}
|
||||
else if (results->decode_type == WHYNTER) {
|
||||
} else if (results->decode_type == WHYNTER) {
|
||||
Serial.print("Decoded Whynter: ");
|
||||
} else if (results->decode_type == NIKAI) {
|
||||
Serial.print("Decoded Nikai: ");
|
||||
}
|
||||
Serial.print(results->value, HEX);
|
||||
serialPrintUint64(results->value, 16);
|
||||
Serial.print(" (");
|
||||
Serial.print(results->bits, DEC);
|
||||
Serial.println(" bits)");
|
||||
Serial.print("Raw (");
|
||||
Serial.print(count, DEC);
|
||||
Serial.print("): ");
|
||||
Serial.print("): {");
|
||||
|
||||
for (int i = 1; i < count; i++) {
|
||||
for (uint16_t i = 1; i < count; i++) {
|
||||
if (i % 100 == 0)
|
||||
yield(); // Preemptive yield every 100th entry to feed the WDT.
|
||||
if (i & 1) {
|
||||
Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
|
||||
Serial.print(results->rawbuf[i] * RAWTICK, DEC);
|
||||
} else {
|
||||
Serial.print(", ");
|
||||
Serial.print((uint32_t) results->rawbuf[i] * RAWTICK, DEC);
|
||||
}
|
||||
else {
|
||||
Serial.write('-');
|
||||
Serial.print((unsigned long) results->rawbuf[i]*USECPERTICK, DEC);
|
||||
}
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("};");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (irrecv.decode(&results)) {
|
||||
Serial.println(results.value, HEX);
|
||||
dump(&results);
|
||||
irrecv.resume(); // Receive the next value
|
||||
Serial.println("DEPRECATED: Please use IRrecvDumpV2.ino instead!");
|
||||
irrecv.resume(); // Receive the next value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,173 +1,221 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRrecvDumpV2 - dump details of IR codes with IRrecv
|
||||
* An IR detector/demodulator must be connected to the input RECV_PIN.
|
||||
*
|
||||
* Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* Copyright 2017 David Conran
|
||||
*
|
||||
* Example circuit diagram:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
|
||||
*
|
||||
* Changes:
|
||||
* Version 0.4 July, 2018
|
||||
* - Minor improvements and more A/C unit support.
|
||||
* Version 0.3 November, 2017
|
||||
* - Support for A/C decoding for some protcols.
|
||||
* Version 0.2 April, 2017
|
||||
* - Decode from a copy of the data so we can start capturing faster thus
|
||||
* reduce the likelihood of miscaptures.
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
#if DECODE_AC
|
||||
#include <ir_Daikin.h>
|
||||
#include <ir_Fujitsu.h>
|
||||
#include <ir_Gree.h>
|
||||
#include <ir_Haier.h>
|
||||
#include <ir_Kelvinator.h>
|
||||
#include <ir_Midea.h>
|
||||
#include <ir_Toshiba.h>
|
||||
#endif // DECODE_AC
|
||||
|
||||
// An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU board).
|
||||
int RECV_PIN = 14;
|
||||
// ==================== start of TUNEABLE PARAMETERS ====================
|
||||
// An IR detector/demodulator is connected to GPIO pin 14
|
||||
// e.g. D5 on a NodeMCU board.
|
||||
#define RECV_PIN 14
|
||||
|
||||
IRrecv irrecv(RECV_PIN);
|
||||
// The Serial connection baud rate.
|
||||
// i.e. Status message will be sent to the PC at this baud rate.
|
||||
// Try to avoid slow speeds like 9600, as you will miss messages and
|
||||
// cause other problems. 115200 (or faster) is recommended.
|
||||
// NOTE: Make sure you set your Serial Monitor to the same speed.
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
decode_results results; // Somewhere to store the results
|
||||
irparams_t save; // A place to copy the interrupt state while decoding.
|
||||
// As this program is a special purpose capture/decoder, let us use a larger
|
||||
// than normal buffer so we can handle Air Conditioner remote codes.
|
||||
#define CAPTURE_BUFFER_SIZE 1024
|
||||
|
||||
// TIMEOUT is the Nr. of milli-Seconds of no-more-data before we consider a
|
||||
// message ended.
|
||||
// This parameter is an interesting trade-off. The longer the timeout, the more
|
||||
// complex a message it can capture. e.g. Some device protocols will send
|
||||
// multiple message packets in quick succession, like Air Conditioner remotes.
|
||||
// Air Coniditioner protocols often have a considerable gap (20-40+ms) between
|
||||
// packets.
|
||||
// The downside of a large timeout value is a lot of less complex protocols
|
||||
// send multiple messages when the remote's button is held down. The gap between
|
||||
// them is often also around 20+ms. This can result in the raw data be 2-3+
|
||||
// times larger than needed as it has captured 2-3+ messages in a single
|
||||
// capture. Setting a low timeout value can resolve this.
|
||||
// So, choosing the best TIMEOUT value for your use particular case is
|
||||
// quite nuanced. Good luck and happy hunting.
|
||||
// NOTE: Don't exceed MAX_TIMEOUT_MS. Typically 130ms.
|
||||
#if DECODE_AC
|
||||
#define TIMEOUT 50U // Some A/C units have gaps in their protocols of ~40ms.
|
||||
// e.g. Kelvinator
|
||||
// A value this large may swallow repeats of some protocols
|
||||
#else // DECODE_AC
|
||||
#define TIMEOUT 15U // Suits most messages, while not swallowing many repeats.
|
||||
#endif // DECODE_AC
|
||||
// Alternatives:
|
||||
// #define TIMEOUT 90U // Suits messages with big gaps like XMP-1 & some aircon
|
||||
// units, but can accidentally swallow repeated messages
|
||||
// in the rawData[] output.
|
||||
// #define TIMEOUT MAX_TIMEOUT_MS // This will set it to our currently allowed
|
||||
// maximum. Values this high are problematic
|
||||
// because it is roughly the typical boundary
|
||||
// where most messages repeat.
|
||||
// e.g. It will stop decoding a message and
|
||||
// start sending it to serial at precisely
|
||||
// the time when the next message is likely
|
||||
// to be transmitted, and may miss it.
|
||||
|
||||
// Set the smallest sized "UNKNOWN" message packets we actually care about.
|
||||
// This value helps reduce the false-positive detection rate of IR background
|
||||
// noise as real messages. The chances of background IR noise getting detected
|
||||
// as a message increases with the length of the TIMEOUT value. (See above)
|
||||
// The downside of setting this message too large is you can miss some valid
|
||||
// short messages for protocols that this library doesn't yet decode.
|
||||
//
|
||||
// Set higher if you get lots of random short UNKNOWN messages when nothing
|
||||
// should be sending a message.
|
||||
// Set lower if you are sure your setup is working, but it doesn't see messages
|
||||
// from your device. (e.g. Other IR remotes work.)
|
||||
// NOTE: Set this value very high to effectively turn off UNKNOWN detection.
|
||||
#define MIN_UNKNOWN_SIZE 12
|
||||
// ==================== end of TUNEABLE PARAMETERS ====================
|
||||
|
||||
|
||||
// Use turn on the save buffer feature for more complete capture coverage.
|
||||
IRrecv irrecv(RECV_PIN, CAPTURE_BUFFER_SIZE, TIMEOUT, true);
|
||||
|
||||
decode_results results; // Somewhere to store the results
|
||||
|
||||
// Display the human readable state of an A/C message if we can.
|
||||
void dumpACInfo(decode_results *results) {
|
||||
String description = "";
|
||||
#if DECODE_DAIKIN
|
||||
if (results->decode_type == DAIKIN) {
|
||||
IRDaikinESP ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_DAIKIN
|
||||
#if DECODE_FUJITSU_AC
|
||||
if (results->decode_type == FUJITSU_AC) {
|
||||
IRFujitsuAC ac(0);
|
||||
ac.setRaw(results->state, results->bits / 8);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_FUJITSU_AC
|
||||
#if DECODE_KELVINATOR
|
||||
if (results->decode_type == KELVINATOR) {
|
||||
IRKelvinatorAC ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_KELVINATOR
|
||||
#if DECODE_TOSHIBA_AC
|
||||
if (results->decode_type == TOSHIBA_AC) {
|
||||
IRToshibaAC ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_TOSHIBA_AC
|
||||
#if DECODE_GREE
|
||||
if (results->decode_type == GREE) {
|
||||
IRGreeAC ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_GREE
|
||||
#if DECODE_MIDEA
|
||||
if (results->decode_type == MIDEA) {
|
||||
IRMideaAC ac(0);
|
||||
ac.setRaw(results->value); // Midea uses value instead of state.
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_MIDEA
|
||||
#if DECODE_HAIER_AC
|
||||
if (results->decode_type == HAIER_AC) {
|
||||
IRHaierAC ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_HAIER_AC
|
||||
#if DECODE_HAIER_AC_YRW02
|
||||
if (results->decode_type == HAIER_AC_YRW02) {
|
||||
IRHaierACYRW02 ac(0);
|
||||
ac.setRaw(results->state);
|
||||
description = ac.toString();
|
||||
}
|
||||
#endif // DECODE_HAIER_AC_YRW02
|
||||
// If we got a human-readable description of the message, display it.
|
||||
if (description != "") Serial.println("Mesg Desc.: " + description);
|
||||
}
|
||||
|
||||
// The section of code run only once at start-up.
|
||||
void setup() {
|
||||
Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); // Status message will be sent to the PC at 115200 baud
|
||||
Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY);
|
||||
while (!Serial) // Wait for the serial connection to be establised.
|
||||
delay(50);
|
||||
Serial.println();
|
||||
Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin ");
|
||||
Serial.println(RECV_PIN);
|
||||
|
||||
#if DECODE_HASH
|
||||
// Ignore messages with less than minimum on or off pulses.
|
||||
irrecv.setUnknownThreshold(MIN_UNKNOWN_SIZE);
|
||||
#endif // DECODE_HASH
|
||||
irrecv.enableIRIn(); // Start the receiver
|
||||
}
|
||||
|
||||
//+=============================================================================
|
||||
// Display IR code
|
||||
//
|
||||
void ircode(decode_results *results) {
|
||||
// Panasonic has an Address
|
||||
if (results->decode_type == PANASONIC) {
|
||||
Serial.print(results->panasonicAddress, HEX);
|
||||
Serial.print(":");
|
||||
}
|
||||
// Print Code
|
||||
Serial.print(results->value, HEX);
|
||||
}
|
||||
|
||||
//+=============================================================================
|
||||
// Display encoding type
|
||||
//
|
||||
void encoding(decode_results *results) {
|
||||
switch (results->decode_type) {
|
||||
default:
|
||||
case UNKNOWN: Serial.print("UNKNOWN"); break ;
|
||||
case NEC: Serial.print("NEC"); break ;
|
||||
case SONY: Serial.print("SONY"); break ;
|
||||
case RC5: Serial.print("RC5"); break ;
|
||||
case RC6: Serial.print("RC6"); break ;
|
||||
case DISH: Serial.print("DISH"); break ;
|
||||
case SHARP: Serial.print("SHARP"); break ;
|
||||
case JVC: Serial.print("JVC"); break ;
|
||||
case SANYO: Serial.print("SANYO"); break ;
|
||||
case MITSUBISHI: Serial.print("MITSUBISHI"); break ;
|
||||
case SAMSUNG: Serial.print("SAMSUNG"); break ;
|
||||
case LG: Serial.print("LG"); break ;
|
||||
case WHYNTER: Serial.print("WHYNTER"); break ;
|
||||
case AIWA_RC_T501: Serial.print("AIWA_RC_T501"); break ;
|
||||
case PANASONIC: Serial.print("PANASONIC"); break ;
|
||||
case DENON: Serial.print("DENON"); break ;
|
||||
}
|
||||
}
|
||||
|
||||
//+=============================================================================
|
||||
// Dump out the decode_results structure.
|
||||
//
|
||||
void dumpInfo (decode_results *results) {
|
||||
if (results->overflow) {
|
||||
Serial.println("IR code too long. Edit IRremoteInt.h and increase RAWBUF");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show Encoding standard
|
||||
Serial.print("Encoding : ");
|
||||
encoding(results);
|
||||
Serial.println("");
|
||||
|
||||
// Show Code & length
|
||||
Serial.print("Code : ");
|
||||
ircode(results);
|
||||
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 (int i = 1; i < results->rawlen; i++) {
|
||||
unsigned long 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("unsigned int "); // variable type
|
||||
Serial.print("rawData["); // array name
|
||||
Serial.print(results->rawlen - 1, DEC); // array size
|
||||
Serial.print("] = {"); // Start declaration
|
||||
|
||||
// Dump data
|
||||
for (int 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(" // ");
|
||||
encoding(results);
|
||||
Serial.print(" ");
|
||||
ircode(results);
|
||||
|
||||
// Newline
|
||||
Serial.println("");
|
||||
|
||||
// Now dump "known" codes
|
||||
if (results->decode_type != UNKNOWN) {
|
||||
// Some protocols have an address
|
||||
if (results->decode_type == PANASONIC) {
|
||||
Serial.print("unsigned int addr = 0x");
|
||||
Serial.print(results->panasonicAddress, HEX);
|
||||
Serial.println(";");
|
||||
}
|
||||
|
||||
// All protocols have data
|
||||
Serial.print("unsigned int data = 0x");
|
||||
Serial.print(results->value, HEX);
|
||||
Serial.println(";");
|
||||
}
|
||||
}
|
||||
|
||||
//+=============================================================================
|
||||
// The repeating section of the code
|
||||
//
|
||||
void loop() {
|
||||
// Check if the IR code has been received.
|
||||
if (irrecv.decode(&results, &save)) {
|
||||
dumpInfo(&results); // Output the results
|
||||
dumpRaw(&results); // Output the results in RAW format
|
||||
dumpCode(&results); // Output the results as source code
|
||||
Serial.println(""); // Blank line between entries
|
||||
if (irrecv.decode(&results)) {
|
||||
// Display a crude timestamp.
|
||||
uint32_t now = millis();
|
||||
Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000);
|
||||
if (results.overflow)
|
||||
Serial.printf("WARNING: IR code is too big for buffer (>= %d). "
|
||||
"This result shouldn't be trusted until this is resolved. "
|
||||
"Edit & increase CAPTURE_BUFFER_SIZE.\n",
|
||||
CAPTURE_BUFFER_SIZE);
|
||||
// Display the basic output of what we found.
|
||||
Serial.print(resultToHumanReadableBasic(&results));
|
||||
dumpACInfo(&results); // Display any extra A/C info if we have it.
|
||||
yield(); // Feed the WDT as the text output can take a while to print.
|
||||
|
||||
// Display the library version the message was captured with.
|
||||
Serial.print("Library : v");
|
||||
Serial.println(_IRREMOTEESP8266_VERSION_);
|
||||
Serial.println();
|
||||
|
||||
// Output RAW timing info of the result.
|
||||
Serial.println(resultToTimingInfo(&results));
|
||||
yield(); // Feed the WDT (again)
|
||||
|
||||
// Output the results as source code
|
||||
Serial.println(resultToSourceCode(&results));
|
||||
Serial.println(""); // Blank line between entries
|
||||
yield(); // Feed the WDT (again)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,10 +1,14 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend.
|
||||
/* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend.
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2).
|
||||
* Version 1.0 April, 2017
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
|
||||
* Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
@@ -22,26 +26,46 @@
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*
|
||||
* Version 1.0 April, 2017
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
|
||||
IRsend irsend(4); //an IR led is connected to GPIO pin 4 (D2)
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
void setup()
|
||||
{
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
// Example of data captured by IRrecvDumpV2.ino
|
||||
uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550,
|
||||
600, 1650, 650, 550, 600, 1650, 650, 1650, 650, 1650,
|
||||
600, 550, 650, 1650, 650, 1650, 650, 550, 600, 1650,
|
||||
650, 1650, 650, 550, 650, 550, 650, 1650, 650, 550,
|
||||
650, 550, 650, 550, 600, 550, 650, 550, 650, 550,
|
||||
650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650,
|
||||
650, 1650, 650, 1650, 650, 1650, 600};
|
||||
|
||||
void setup() {
|
||||
irsend.begin();
|
||||
Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#if SEND_NEC
|
||||
Serial.println("NEC");
|
||||
irsend.sendNEC(0x00FFE01FUL, 32);
|
||||
#endif // SEND_NEC
|
||||
delay(2000);
|
||||
#if SEND_SONY
|
||||
Serial.println("Sony");
|
||||
irsend.sendSony(0xa90, 12, 2);
|
||||
#endif // SEND_SONY
|
||||
delay(2000);
|
||||
#if SEND_RAW
|
||||
Serial.println("a rawData capture from IRrecvDumpV2");
|
||||
irsend.sendRaw(rawData, 67, 38); // Send a raw data capture at 38kHz.
|
||||
#endif // SEND_RAW
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,112 @@
|
||||
/* IRremoteESP8266: IRsendProntoDemo
|
||||
* Copyright 2017 David Conran
|
||||
*
|
||||
* Demonstrates sending Pronto codes with IRsend.
|
||||
*
|
||||
* Version 1.0 June, 2017
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2), unless you
|
||||
* change the IR_LED value below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
// Panasonic Plasma TV Descrete code (Power On).
|
||||
// Acquired from:
|
||||
// https://irdb.globalcache.com/
|
||||
// e.g.
|
||||
// 0000 006D 0000 0022 00ac 00ac 0016 0040 0016 0040 0016 0040 0016 0015 0016
|
||||
// 0015 0016 0015 0016 0015 0016 0015 0016 0040 0016 0040 0016 0040 0016 0015
|
||||
// 0016 0015 0016 0015 0016 0015 0016 0015 0016 0040 0016 0015 0016 0015 0016
|
||||
// 0040 0016 0040 0016 0015 0016 0015 0016 0040 0016 0015 0016 0040 0016 0040
|
||||
// 0016 0015 0016 0015 0016 0040 0016 0040 0016 0015 0016 071c
|
||||
//
|
||||
// Or the equiv. of sendSamsung(0xE0E09966);
|
||||
uint16_t samsungProntoCode[72] = {
|
||||
0x0000, 0x006D, 0x0000, 0x0022,
|
||||
0x00ac, 0x00ac, 0x0016, 0x0040, 0x0016, 0x0040, 0x0016, 0x0040,
|
||||
0x0016, 0x0015, 0x0016, 0x0015, 0x0016, 0x0015, 0x0016, 0x0015,
|
||||
0x0016, 0x0015, 0x0016, 0x0040, 0x0016, 0x0040, 0x0016, 0x0040,
|
||||
0x0016, 0x0015, 0x0016, 0x0015, 0x0016, 0x0015, 0x0016, 0x0015,
|
||||
0x0016, 0x0015, 0x0016, 0x0040, 0x0016, 0x0015, 0x0016, 0x0015,
|
||||
0x0016, 0x0040, 0x0016, 0x0040, 0x0016, 0x0015, 0x0016, 0x0015,
|
||||
0x0016, 0x0040, 0x0016, 0x0015, 0x0016, 0x0040, 0x0016, 0x0040,
|
||||
0x0016, 0x0015, 0x0016, 0x0015, 0x0016, 0x0040, 0x0016, 0x0040,
|
||||
0x0016, 0x0015, 0x0016, 0x071c
|
||||
};
|
||||
|
||||
// Panasonic Plasma TV Descrete code (Power On).
|
||||
// Acquired from:
|
||||
// ftp://ftp.panasonic.com/pub/panasonic/drivers/monitors/Discrete-remote-control-codesProntoCCFformat.pdf
|
||||
// e.g.
|
||||
// 0000 0071 0000 0032 0080 003F 0010 0010 0010 0030 0010 0010 0010 0010 0010
|
||||
// 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010
|
||||
// 0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010
|
||||
// 0010 0010 0010 0010 0010 0010 0010 0010 0030 0010 0010 0010 0010 0010 0010
|
||||
// 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0030 0010
|
||||
// 0030 0010 0030 0010 0030 0010 0030 0010 0010 0010 0010 0010 0010 0010 0030
|
||||
// 0010 0030 0010 0030 0010 0030 0010 0030 0010 0010 0010 0030 0010 0A98
|
||||
//
|
||||
// Or the equiv. of sendPanasonic64(0x400401007C7D);
|
||||
uint16_t panasonicProntoCode[104] = {
|
||||
0x0000, 0x0071, 0x0000, 0x0032,
|
||||
0x0080, 0x003F, 0x0010, 0x0010, 0x0010, 0x0030, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0030, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0030, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0030, 0x0010, 0x0030,
|
||||
0x0010, 0x0030, 0x0010, 0x0030, 0x0010, 0x0030, 0x0010, 0x0010,
|
||||
0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0030, 0x0010, 0x0030,
|
||||
0x0010, 0x0030, 0x0010, 0x0030, 0x0010, 0x0030, 0x0010, 0x0010,
|
||||
0x0010, 0x0030, 0x0010, 0x0A98};
|
||||
|
||||
void setup() {
|
||||
irsend.begin();
|
||||
Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#if SEND_PRONTO
|
||||
Serial.println("Sending a Samsung TV 'on' command.");
|
||||
irsend.sendPronto(samsungProntoCode, 72);
|
||||
delay(2000);
|
||||
Serial.println("Sending a Panasonic Plasma TV 'on' command.");
|
||||
irsend.sendPronto(panasonicProntoCode, 104);
|
||||
delay(2000);
|
||||
#else // SEND_PRONTO
|
||||
Serial.println("Can't send because SEND_PRONTO has been disabled.");
|
||||
delay(10000);
|
||||
#endif // SEND_PRONTO
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,29 +1,67 @@
|
||||
/*
|
||||
* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend
|
||||
* An IR LED must be connected to ESP8266 pin 0.
|
||||
* Version 0.1 June, 2015
|
||||
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
|
||||
* JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
|
||||
#define PanasonicAddress 0x4004 // Panasonic address (Pre data)
|
||||
#include <IRsend.h>
|
||||
|
||||
#define PanasonicAddress 0x4004 // Panasonic address (Pre data)
|
||||
#define PanasonicPower 0x100BCBD // Panasonic Power button
|
||||
|
||||
#define JVCPower 0xC5E8
|
||||
|
||||
IRsend irsend(0); //an IR led is connected to GPIO pin 0
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
|
||||
void setup()
|
||||
{
|
||||
irsend.begin();
|
||||
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
void setup() {
|
||||
irsend.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
irsend.sendPanasonic(PanasonicAddress,PanasonicPower); // This should turn your TV on and off
|
||||
|
||||
irsend.sendJVC(JVCPower, 16,0); // hex value, 16 bits, no repeat
|
||||
delayMicroseconds(50); // see http://www.sbprojects.com/knowledge/ir/jvc.php for information
|
||||
irsend.sendJVC(JVCPower, 16,1); // hex value, 16 bits, repeat
|
||||
// This should turn your TV on and off
|
||||
#if SEND_PANASONIC
|
||||
irsend.sendPanasonic(PanasonicAddress, PanasonicPower);
|
||||
#else // SEND_PANASONIC
|
||||
Serial.println("Can't send because SEND_PANASONIC has been disabled.");
|
||||
#endif // SEND_PANASONIC
|
||||
|
||||
#if SEND_JVC
|
||||
irsend.sendJVC(JVCPower, 16, 0); // hex value, 16 bits, no repeat
|
||||
// see http://www.sbprojects.com/knowledge/ir/jvc.php for information
|
||||
delayMicroseconds(50);
|
||||
irsend.sendJVC(JVCPower, 16, 1); // hex value, 16 bits, repeat
|
||||
delayMicroseconds(50);
|
||||
#else // SEND_JVC
|
||||
Serial.println("Can't send because SEND_JVC has been disabled.");
|
||||
#endif // SEND_JVC
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright 2015 chaeplin
|
||||
// Copyright 2017 xpokor22
|
||||
// This is based on:
|
||||
// https://github.com/z3t0/Arduino-IRremote/blob/master/examples/LGACSendDemo/LGACSendDemo.ino
|
||||
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
|
||||
|
||||
IRsend irsend(14); // An IR LED is controlled by GPIO pin 14 (D5)
|
||||
|
||||
// 0 : TOWER
|
||||
// 1 : WALL
|
||||
const unsigned int kAc_Type = 1;
|
||||
|
||||
// 0 : cooling
|
||||
// 1 : heating
|
||||
unsigned int ac_heat = 1;
|
||||
|
||||
// 0 : off
|
||||
// 1 : on
|
||||
unsigned int ac_power_on = 0;
|
||||
|
||||
// 0 : off
|
||||
// 1 : on --> power on
|
||||
unsigned int ac_air_clean_state = 0;
|
||||
|
||||
// temperature : 18 ~ 30
|
||||
unsigned int ac_temperature = 24;
|
||||
|
||||
// 0 : low
|
||||
// 1 : mid
|
||||
// 2 : high
|
||||
// if kAc_Type = 1, 3 : change
|
||||
unsigned int ac_flow = 0;
|
||||
|
||||
const uint8_t kAc_Flow_Tower[3] = {0, 4, 6};
|
||||
const uint8_t kAc_Flow_Wall[4] = {0, 2, 4, 5};
|
||||
|
||||
uint32_t ac_code_to_sent;
|
||||
|
||||
void Ac_Send_Code(uint32_t code) {
|
||||
Serial.print("code to send : ");
|
||||
Serial.print(code, BIN);
|
||||
Serial.print(" : ");
|
||||
Serial.println(code, HEX);
|
||||
|
||||
#if SEND_LG
|
||||
irsend.sendLG(code, 28);
|
||||
#else // SEND_LG
|
||||
Serial.println("Can't send because SEND_LG has been disabled.");
|
||||
#endif // SEND_LG
|
||||
}
|
||||
|
||||
void Ac_Activate(unsigned int temperature, unsigned int air_flow,
|
||||
unsigned int heat) {
|
||||
ac_heat = heat;
|
||||
unsigned int ac_msbits1 = 8;
|
||||
unsigned int ac_msbits2 = 8;
|
||||
unsigned int ac_msbits3 = 0;
|
||||
unsigned int ac_msbits4;
|
||||
if (ac_heat == 1)
|
||||
ac_msbits4 = 4; // heating
|
||||
else
|
||||
ac_msbits4 = 0; // cooling
|
||||
unsigned int ac_msbits5 = (temperature < 15) ? 0 : temperature - 15;
|
||||
unsigned int ac_msbits6;
|
||||
|
||||
if (0 <= air_flow && air_flow <= 2) {
|
||||
if (kAc_Type == 0)
|
||||
ac_msbits6 = kAc_Flow_Tower[air_flow];
|
||||
else
|
||||
ac_msbits6 = kAc_Flow_Wall[air_flow];
|
||||
}
|
||||
|
||||
// calculating using other values
|
||||
unsigned int ac_msbits7 = (ac_msbits3 + ac_msbits4 + ac_msbits5 +
|
||||
ac_msbits6) & B00001111;
|
||||
ac_code_to_sent = ac_msbits1 << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits2) << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits3) << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits4) << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits5) << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits6) << 4;
|
||||
ac_code_to_sent = (ac_code_to_sent + ac_msbits7);
|
||||
|
||||
Ac_Send_Code(ac_code_to_sent);
|
||||
|
||||
ac_power_on = 1;
|
||||
ac_temperature = temperature;
|
||||
ac_flow = air_flow;
|
||||
}
|
||||
|
||||
void Ac_Change_Air_Swing(int air_swing) {
|
||||
if (kAc_Type == 0) {
|
||||
if (air_swing == 1)
|
||||
ac_code_to_sent = 0x881316B;
|
||||
else
|
||||
ac_code_to_sent = 0x881317C;
|
||||
} else {
|
||||
if (air_swing == 1)
|
||||
ac_code_to_sent = 0x8813149;
|
||||
else
|
||||
ac_code_to_sent = 0x881315A;
|
||||
}
|
||||
Ac_Send_Code(ac_code_to_sent);
|
||||
}
|
||||
|
||||
void Ac_Power_Down() {
|
||||
ac_code_to_sent = 0x88C0051;
|
||||
|
||||
Ac_Send_Code(ac_code_to_sent);
|
||||
|
||||
ac_power_on = 0;
|
||||
}
|
||||
|
||||
void Ac_Air_Clean(int air_clean) {
|
||||
if (air_clean == '1')
|
||||
ac_code_to_sent = 0x88C000C;
|
||||
else
|
||||
ac_code_to_sent = 0x88C0084;
|
||||
|
||||
Ac_Send_Code(ac_code_to_sent);
|
||||
|
||||
ac_air_clean_state = air_clean;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
irsend.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char b;
|
||||
Serial.println("# a : mode or temp b : air_flow, temp, swing, clean,"
|
||||
" cooling/heating");
|
||||
Serial.println("# 0 : off 0");
|
||||
Serial.println("# 1 : on 0");
|
||||
Serial.println("# 2 : air_swing 0 or 1");
|
||||
Serial.println("# 3 : air_clean 0 or 1");
|
||||
Serial.println("# 4 : air_flow 0 ~ 2 : flow");
|
||||
Serial.println("# + : temp + 1");
|
||||
Serial.println("# - : temp - 1");
|
||||
Serial.println("# c : cooling");
|
||||
Serial.println("# h : heating");
|
||||
Serial.println("# m : change cooling to air clean, air clean to cooling");
|
||||
|
||||
Serial.println("a="); // Prompt User for input
|
||||
while (Serial.available() == 0) { // Wait for user input
|
||||
}
|
||||
char a = Serial.read(); // Read user input into a
|
||||
switch (a) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '+':
|
||||
case '-':
|
||||
case 'c':
|
||||
case 'h':
|
||||
case 'm':
|
||||
break;
|
||||
default:
|
||||
Serial.println("b="); // Prompt User for input
|
||||
while (Serial.available() == 0) {}
|
||||
char b = Serial.read();
|
||||
}
|
||||
|
||||
/*
|
||||
# a : mode or temp b : air_flow, temp, swing, clean, cooling/heating
|
||||
# 18 ~ 30 : temp 0 ~ 2 : flow // on
|
||||
# 0 : off 0
|
||||
# 1 : on 0
|
||||
# 2 : air_swing 0 or 1
|
||||
# 3 : air_clean 0 or 1
|
||||
# 4 : air_flow 0 ~ 3 : flow
|
||||
# + : temp + 1
|
||||
# - : temp - 1
|
||||
# c : cooling
|
||||
# h : heating
|
||||
# m : change cooling to air clean, air clean to cooling
|
||||
*/
|
||||
Serial.print("a : ");
|
||||
Serial.print(a);
|
||||
Serial.print(" b : ");
|
||||
Serial.println(b);
|
||||
|
||||
switch (a) {
|
||||
case '0': // off
|
||||
Ac_Power_Down();
|
||||
break;
|
||||
case '1': // on
|
||||
Ac_Activate(ac_temperature, ac_flow, ac_heat);
|
||||
break;
|
||||
case '2':
|
||||
if (b == '0')
|
||||
Ac_Change_Air_Swing(0);
|
||||
else
|
||||
Ac_Change_Air_Swing(1);
|
||||
break;
|
||||
case '3': // 1 : clean on, power on
|
||||
if (b == '0' | b == '1')
|
||||
Ac_Air_Clean(b);
|
||||
break;
|
||||
case '4':
|
||||
switch (b) {
|
||||
case '1':
|
||||
Ac_Activate(ac_temperature, 1, ac_heat);
|
||||
break;
|
||||
case '2':
|
||||
Ac_Activate(ac_temperature, 2, ac_heat);
|
||||
break;
|
||||
case '3':
|
||||
Ac_Activate(ac_temperature, 3, ac_heat);
|
||||
break;
|
||||
default:
|
||||
Ac_Activate(ac_temperature, 0, ac_heat);
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
if (18 <= ac_temperature && ac_temperature <= 29)
|
||||
Ac_Activate((ac_temperature + 1), ac_flow, ac_heat);
|
||||
break;
|
||||
case '-':
|
||||
if (19 <= ac_temperature && ac_temperature <= 30)
|
||||
Ac_Activate((ac_temperature - 1), ac_flow, ac_heat);
|
||||
break;
|
||||
case 'c':
|
||||
ac_heat = 0;
|
||||
Ac_Activate(ac_temperature, ac_flow, ac_heat);
|
||||
break;
|
||||
case 'h':
|
||||
ac_heat = 1;
|
||||
Ac_Activate(ac_temperature, ac_flow, ac_heat);
|
||||
break;
|
||||
case 'm':
|
||||
/*
|
||||
if ac is on, 1) turn off, 2) turn on Ac_Air_Clean(1)
|
||||
if ac is off, 1) turn on, 2) turn off Ac_Air_Clean(0)
|
||||
*/
|
||||
if (ac_power_on == 1) {
|
||||
Ac_Power_Down();
|
||||
delay(100);
|
||||
Ac_Air_Clean(1);
|
||||
} else {
|
||||
if (ac_air_clean_state == 1) {
|
||||
Ac_Air_Clean(0);
|
||||
delay(100);
|
||||
}
|
||||
Ac_Activate(ac_temperature, ac_flow, ac_heat);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
delay(100);
|
||||
Serial.println("ac_temperature");
|
||||
Serial.println(ac_temperature);
|
||||
Serial.println("ac_flow");
|
||||
Serial.println(ac_flow);
|
||||
Serial.println("ac_heat");
|
||||
Serial.println(ac_heat);
|
||||
Serial.println("ac_power_on");
|
||||
Serial.println(ac_power_on);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Copyright 2017 crankyoldgit
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Argo.h>
|
||||
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRArgoAC argoir(IR_LED); // Set the GPIO to be used to sending the message.
|
||||
|
||||
void setup() {
|
||||
argoir.begin();
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Sending...");
|
||||
|
||||
// Set up what we want to send. See ir_Argo.cpp for all the options.
|
||||
argoir.setPower(true);
|
||||
argoir.setFan(ARGO_FAN_1);
|
||||
argoir.setCoolMode(ARGO_COOL_AUTO);
|
||||
argoir.setTemp(25);
|
||||
|
||||
#if SEND_ARGO
|
||||
// Now send the IR signal.
|
||||
argoir.send();
|
||||
#else // SEND_ARGO
|
||||
Serial.println("Can't send because SEND_ARGO has been disabled.");
|
||||
#endif // SEND_ARGO
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,27 +1,69 @@
|
||||
/* Copyright 2017 sillyfrog
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#include <IRDaikinESP.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Daikin.h>
|
||||
|
||||
IRDaikinESP dakinir(D1);
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRDaikinESP daikinir(IR_LED); // Set the GPIO to be used to sending the message
|
||||
|
||||
void setup(){
|
||||
dakinir.begin();
|
||||
void setup() {
|
||||
daikinir.begin();
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
|
||||
void loop(){
|
||||
void loop() {
|
||||
Serial.println("Sending...");
|
||||
|
||||
// Set up what we want to send. See IRDaikinESP.cpp for all the options.
|
||||
dakinir.on();
|
||||
dakinir.setFan(1);
|
||||
dakinir.setMode(DAIKIN_COOL);
|
||||
dakinir.setTemp(25);
|
||||
dakinir.setSwingVertical(0);
|
||||
dakinir.setSwingHorizontal(0);
|
||||
// Set up what we want to send. See ir_Daikin.cpp for all the options.
|
||||
daikinir.on();
|
||||
daikinir.setFan(1);
|
||||
daikinir.setMode(DAIKIN_COOL);
|
||||
daikinir.setTemp(25);
|
||||
daikinir.setSwingVertical(false);
|
||||
daikinir.setSwingHorizontal(false);
|
||||
|
||||
// Set the current time to 1:33PM (13:33)
|
||||
// Time works in minutes past midnight
|
||||
daikinir.setCurrentTime((13*60) + 33);
|
||||
// Turn off about 1 hour later at 2:30PM (15:30)
|
||||
daikinir.enableOffTimer((14*60) + 30);
|
||||
|
||||
// Display what we are going to send.
|
||||
Serial.println(daikinir.toString());
|
||||
|
||||
// Now send the IR signal.
|
||||
dakinir.send();
|
||||
#if SEND_DAIKIN
|
||||
daikinir.send();
|
||||
#endif // SEND_DAIKIN
|
||||
|
||||
delay(5000);
|
||||
delay(15000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017 Jonny Graham
|
||||
#include <IRsend.h>
|
||||
#include <ir_Fujitsu.h>
|
||||
|
||||
IRFujitsuAC fujitsu(5); // IR led controlled by Pin D1.
|
||||
|
||||
void printState() {
|
||||
// Display the settings.
|
||||
Serial.println("Fujitsu A/C remote is in the following state:");
|
||||
Serial.printf(" Command:%d, Mode: %d, Temp: %dC, Fan Speed: %d," \
|
||||
" Swing Mode: %d\n",
|
||||
fujitsu.getCmd(), fujitsu.getMode(), fujitsu.getTemp(),
|
||||
fujitsu.getFanSpeed(), fujitsu.getSwing());
|
||||
// Display the encoded IR sequence.
|
||||
unsigned char* ir_code = fujitsu.getRaw();
|
||||
Serial.print("IR Code: 0x");
|
||||
for (uint8_t i = 0; i < fujitsu.getStateLength(); i++)
|
||||
Serial.printf("%02X", ir_code[i]);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
fujitsu.begin();
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
|
||||
// Set up what we want to send. See ir_Fujitsu.cpp for all the options.
|
||||
Serial.println("Default state of the remote.");
|
||||
printState();
|
||||
Serial.println("Setting desired state for A/C.");
|
||||
fujitsu.setCmd(FUJITSU_AC_CMD_TURN_ON);
|
||||
fujitsu.setSwing(FUJITSU_AC_SWING_BOTH);
|
||||
fujitsu.setMode(FUJITSU_AC_MODE_COOL);
|
||||
fujitsu.setFanSpeed(FUJITSU_AC_FAN_HIGH);
|
||||
fujitsu.setTemp(24);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Now send the IR signal.
|
||||
Serial.println("Sending IR command to A/C ...");
|
||||
#if SEND_FUJITSU_AC
|
||||
fujitsu.send();
|
||||
#else // SEND_FUJITSU_AC
|
||||
Serial.println("Can't send because SEND_FUJITSU_AC has been disabled.");
|
||||
#endif // SEND_FUJITSU_AC
|
||||
printState();
|
||||
delay(5000);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,7 +1,37 @@
|
||||
/* Copyright 2016 David Conran
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Kelvinator.h>
|
||||
|
||||
#include <IRKelvinator.h>
|
||||
|
||||
IRKelvinatorAC kelvir(D1); // IR led controlled by Pin D1.
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRKelvinatorAC kelvir(IR_LED); // Set the GPIO to be used for sending messages.
|
||||
|
||||
void printState() {
|
||||
// Display the settings.
|
||||
@@ -17,17 +47,17 @@ void printState() {
|
||||
// Display the encoded IR sequence.
|
||||
unsigned char* ir_code = kelvir.getRaw();
|
||||
Serial.print("IR Code: 0x");
|
||||
for (int i = 0; i < KELVINATOR_STATE_LENGTH; i++)
|
||||
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++)
|
||||
Serial.printf("%02X", ir_code[i]);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
void setup() {
|
||||
kelvir.begin();
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
|
||||
// Set up what we want to send. See IRKelvinator.cpp for all the options.
|
||||
// Set up what we want to send. See ir_Kelvinator.cpp for all the options.
|
||||
// Most things default to off.
|
||||
Serial.println("Default state of the remote.");
|
||||
printState();
|
||||
@@ -45,8 +75,10 @@ void setup(){
|
||||
|
||||
void loop() {
|
||||
// Now send the IR signal.
|
||||
#if SEND_KELVINATOR
|
||||
Serial.println("Sending IR command to A/C ...");
|
||||
kelvir.send();
|
||||
#endif // SEND_KELVINATOR
|
||||
printState();
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -1,28 +1,59 @@
|
||||
/* Copyright 2017 David Conran
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Mitsubishi.h>
|
||||
|
||||
#include <IRMitsubishiAC.h>
|
||||
|
||||
IRMitsubishiAC mitsubir(D1); // IR led controlled by Pin D1.
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRMitsubishiAC mitsubir(IR_LED); // Set the GPIO used for sending messages.
|
||||
|
||||
void printState() {
|
||||
// Display the settings.
|
||||
Serial.println("Mitsubishi A/C remote is in the following state:");
|
||||
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d, Vane Mode: %d\n",
|
||||
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d," \
|
||||
" Vane Mode: %d\n",
|
||||
mitsubir.getPower(), mitsubir.getMode(), mitsubir.getTemp(),
|
||||
mitsubir.getFan(), mitsubir.getVane());
|
||||
// Display the encoded IR sequence.
|
||||
unsigned char* ir_code = mitsubir.getRaw();
|
||||
Serial.print("IR Code: 0x");
|
||||
for (int i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
|
||||
for (uint8_t i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
|
||||
Serial.printf("%02X", ir_code[i]);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
void setup() {
|
||||
mitsubir.begin();
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
|
||||
// Set up what we want to send. See IRMitsubishiAC.cpp for all the options.
|
||||
// Set up what we want to send. See ir_Mitsubishi.cpp for all the options.
|
||||
Serial.println("Default state of the remote.");
|
||||
printState();
|
||||
Serial.println("Setting desired state for A/C.");
|
||||
@@ -35,8 +66,10 @@ void setup(){
|
||||
|
||||
void loop() {
|
||||
// Now send the IR signal.
|
||||
#if SEND_MITSUBISHI_AC
|
||||
Serial.println("Sending IR command to A/C ...");
|
||||
mitsubir.send();
|
||||
#endif // SEND_MITSUBISHI_AC
|
||||
printState();
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Copyright 2017 David Conran
|
||||
*
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Toshiba.h>
|
||||
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRToshibaAC toshibair(IR_LED); // Set the GPIO to be used for sending messages.
|
||||
|
||||
void printState() {
|
||||
// Display the settings.
|
||||
Serial.println("Toshiba A/C remote is in the following state:");
|
||||
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d\n",
|
||||
toshibair.getPower(), toshibair.getMode(), toshibair.getTemp(),
|
||||
toshibair.getFan());
|
||||
// Display the encoded IR sequence.
|
||||
unsigned char* ir_code = toshibair.getRaw();
|
||||
Serial.print("IR Code: 0x");
|
||||
for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++)
|
||||
Serial.printf("%02X", ir_code[i]);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
toshibair.begin();
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
|
||||
// Set up what we want to send. See ir_Toshiba.cpp for all the options.
|
||||
Serial.println("Default state of the remote.");
|
||||
printState();
|
||||
Serial.println("Setting desired state for A/C.");
|
||||
toshibair.on();
|
||||
toshibair.setFan(1);
|
||||
toshibair.setMode(TOSHIBA_AC_COOL);
|
||||
toshibair.setTemp(26);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Now send the IR signal.
|
||||
#if SEND_TOSHIBA_AC
|
||||
Serial.println("Sending IR command to A/C ...");
|
||||
toshibair.send();
|
||||
#endif // SEND_TOSHIBA_AC
|
||||
printState();
|
||||
delay(5000);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Copyright 2017 stufisher
|
||||
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
|
||||
* as specified by IR_LED below.
|
||||
*
|
||||
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
|
||||
*
|
||||
* Suggested circuit:
|
||||
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
|
||||
*
|
||||
* Common mistakes & tips:
|
||||
* * Don't just connect the IR LED directly to the pin, it won't
|
||||
* have enough current to drive the IR LED effectively.
|
||||
* * Make sure you have the IR LED polarity correct.
|
||||
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
|
||||
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
|
||||
* Replace the IR LED with a normal LED if you don't have a digital camera
|
||||
* when debugging.
|
||||
* * Avoid using the following pins unless you really know what you are doing:
|
||||
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
|
||||
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
|
||||
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
|
||||
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
|
||||
* for your first time. e.g. ESP-12 etc.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <ir_Trotec.h>
|
||||
|
||||
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
|
||||
IRTrotecESP trotecir(IR_LED); // Set the GPIO to be used for sending messages.
|
||||
|
||||
void setup() {
|
||||
trotecir.begin();
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("Sending...");
|
||||
|
||||
// Set up what we want to send. See ir_Trotec.cpp for all the options.
|
||||
trotecir.setPower(true);
|
||||
trotecir.setSpeed(TROTEC_FAN_LOW);
|
||||
trotecir.setMode(TROTEC_COOL);
|
||||
trotecir.setTemp(25);
|
||||
|
||||
// Now send the IR signal.
|
||||
#if SEND_TROTEC
|
||||
trotecir.send();
|
||||
#else // SEND_TROTEC
|
||||
Serial.println("Can't send because SEND_TROTEC has been disabled.");
|
||||
#endif // SEND_TROTEC
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = ../../
|
||||
src_dir=.
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
+1121
-39
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "IRremoteESP8266",
|
||||
"version": "1.2.0",
|
||||
"version": "2.4.3",
|
||||
"keywords": "infrared, ir, remote, esp8266",
|
||||
"description": "Send and receive infrared signals with multiple protocols (ESP8266)",
|
||||
"repository":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name=IRremoteESP8266
|
||||
version=1.2.0
|
||||
version=2.4.3
|
||||
author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran
|
||||
maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto
|
||||
sentence=Send and receive infrared signals with multiple protocols (ESP8266)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[platformio]
|
||||
lib_extra_dirs = .
|
||||
src_dir = examples/IRrecvDumpV2
|
||||
|
||||
[common]
|
||||
build_flags =
|
||||
lib_deps_builtin =
|
||||
lib_deps_external =
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = nodemcuv2
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
|
||||
[env:d1_mini]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = d1_mini
|
||||
build_flags = ${common.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_builtin}
|
||||
${common.lib_deps_external}
|
||||
@@ -0,0 +1,12 @@
|
||||
[REPORTS]
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# String used as indentation unit.
|
||||
indent-string=' '
|
||||
@@ -0,0 +1 @@
|
||||
filter=-build/include,+build/include_alpha,+build/include_order,+build/include_what_you_use
|
||||
@@ -0,0 +1,732 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2015 Sebastien Warin
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include <stddef.h>
|
||||
#ifndef UNIT_TEST
|
||||
extern "C" {
|
||||
#include <gpio.h>
|
||||
#include <user_interface.h>
|
||||
}
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#undef ICACHE_RAM_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#endif
|
||||
// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code
|
||||
// on ESP8266
|
||||
// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
|
||||
// sending IR code on ESP8266
|
||||
|
||||
// Globals
|
||||
#ifndef UNIT_TEST
|
||||
static ETSTimer timer;
|
||||
#endif
|
||||
volatile irparams_t irparams;
|
||||
irparams_t *irparams_save; // A copy of the interrupt state while decoding.
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) {
|
||||
os_intr_lock();
|
||||
if (irparams.rawlen)
|
||||
irparams.rcvstate = STATE_STOP;
|
||||
os_intr_unlock();
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR gpio_intr() {
|
||||
uint32_t now = system_get_time();
|
||||
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
|
||||
static uint32_t start = 0;
|
||||
|
||||
os_timer_disarm(&timer);
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
|
||||
|
||||
// Grab a local copy of rawlen to reduce instructions used in IRAM.
|
||||
// This is an ugly premature optimisation code-wise, but we do everything we
|
||||
// can to save IRAM.
|
||||
// It seems referencing the value via the structure uses more instructions.
|
||||
// Less instructions means faster and less IRAM used.
|
||||
// N.B. It saves about 13 bytes of IRAM.
|
||||
uint16_t rawlen = irparams.rawlen;
|
||||
|
||||
if (rawlen >= irparams.bufsize) {
|
||||
irparams.overflow = true;
|
||||
irparams.rcvstate = STATE_STOP;
|
||||
}
|
||||
|
||||
if (irparams.rcvstate == STATE_STOP)
|
||||
return;
|
||||
|
||||
if (irparams.rcvstate == STATE_IDLE) {
|
||||
irparams.rcvstate = STATE_MARK;
|
||||
irparams.rawbuf[rawlen] = 1;
|
||||
} else {
|
||||
if (now < start)
|
||||
irparams.rawbuf[rawlen] = (UINT32_MAX - start + now) / RAWTICK;
|
||||
else
|
||||
irparams.rawbuf[rawlen] = (now - start) / RAWTICK;
|
||||
}
|
||||
irparams.rawlen++;
|
||||
|
||||
start = now;
|
||||
#define ONCE 0
|
||||
os_timer_arm(&timer, irparams.timeout, ONCE);
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
// Start of IRrecv class -------------------
|
||||
|
||||
// Class constructor
|
||||
// Args:
|
||||
// recvpin: GPIO pin the IR receiver module's data pin is connected to.
|
||||
// bufsize: Nr. of entries to have in the capture buffer. (Default: RAWBUF)
|
||||
// timeout: Nr. of milli-Seconds of no signal before we stop capturing data.
|
||||
// (Default: TIMEOUT_MS)
|
||||
// save_buffer: Use a second (save) buffer to decode from. (Def: false)
|
||||
// Returns:
|
||||
// An IRrecv class object.
|
||||
IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
|
||||
bool save_buffer) {
|
||||
irparams.recvpin = recvpin;
|
||||
irparams.bufsize = bufsize;
|
||||
// Ensure we are going to be able to store all possible values in the
|
||||
// capture buffer.
|
||||
irparams.timeout = std::min(timeout, (uint8_t) MAX_TIMEOUT_MS);
|
||||
irparams.rawbuf = new uint16_t[bufsize];
|
||||
if (irparams.rawbuf == NULL) {
|
||||
DPRINTLN("Could not allocate memory for the primary IR buffer.\n"
|
||||
"Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!");
|
||||
#ifndef UNIT_TEST
|
||||
ESP.restart(); // Mem alloc failure. Reboot.
|
||||
#endif
|
||||
}
|
||||
// If we have been asked to use a save buffer (for decoding), then create one.
|
||||
if (save_buffer) {
|
||||
irparams_save = new irparams_t;
|
||||
irparams_save->rawbuf = new uint16_t[bufsize];
|
||||
// Check we allocated the memory successfully.
|
||||
if (irparams_save->rawbuf == NULL) {
|
||||
DPRINTLN("Could not allocate memory for the second IR buffer.\n"
|
||||
"Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!");
|
||||
#ifndef UNIT_TEST
|
||||
ESP.restart(); // Mem alloc failure. Reboot.
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
irparams_save = NULL;
|
||||
}
|
||||
#if DECODE_HASH
|
||||
unknown_threshold = UNKNOWN_THRESHOLD;
|
||||
#endif // DECODE_HASH
|
||||
}
|
||||
|
||||
// Class destructor
|
||||
IRrecv::~IRrecv(void) {
|
||||
delete [] irparams.rawbuf;
|
||||
if (irparams_save != NULL) {
|
||||
delete [] irparams_save->rawbuf;
|
||||
delete irparams_save;
|
||||
}
|
||||
}
|
||||
|
||||
// initialization
|
||||
void IRrecv::enableIRIn() {
|
||||
// initialize state machine variables
|
||||
resume();
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
// Initialize timer
|
||||
os_timer_disarm(&timer);
|
||||
os_timer_setfn(&timer, reinterpret_cast<os_timer_func_t *>(read_timeout),
|
||||
NULL);
|
||||
|
||||
// Attach Interrupt
|
||||
attachInterrupt(irparams.recvpin, gpio_intr, CHANGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void IRrecv::disableIRIn() {
|
||||
#ifndef UNIT_TEST
|
||||
os_timer_disarm(&timer);
|
||||
detachInterrupt(irparams.recvpin);
|
||||
#endif
|
||||
}
|
||||
|
||||
void IRrecv::resume() {
|
||||
irparams.rcvstate = STATE_IDLE;
|
||||
irparams.rawlen = 0;
|
||||
irparams.overflow = false;
|
||||
}
|
||||
|
||||
// Make a copy of the interrupt state & buffer data.
|
||||
// Needed because irparams is marked as volatile, thus memcpy() isn't allowed.
|
||||
// Only call this when you know the interrupt handlers won't modify anything.
|
||||
// i.e. In STATE_STOP.
|
||||
//
|
||||
// Args:
|
||||
// src: Pointer to an irparams_t structure to copy from.
|
||||
// dst: Pointer to an irparams_t structure to copy to.
|
||||
void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) {
|
||||
// Typecast src and dst addresses to (char *)
|
||||
char *csrc = (char *) src; // NOLINT(readability/casting)
|
||||
char *cdst = (char *) dst; // NOLINT(readability/casting)
|
||||
|
||||
// Save the pointer to the destination's rawbuf so we don't lose it as
|
||||
// the for-loop/copy after this will overwrite it with src's rawbuf pointer.
|
||||
// This isn't immediately obvious due to typecasting/different variable names.
|
||||
uint16_t *dst_rawbuf_ptr;
|
||||
dst_rawbuf_ptr = dst->rawbuf;
|
||||
|
||||
// Copy contents of src[] to dst[]
|
||||
for (uint16_t i = 0; i < sizeof(irparams_t); i++)
|
||||
cdst[i] = csrc[i];
|
||||
|
||||
// Restore the buffer pointer
|
||||
dst->rawbuf = dst_rawbuf_ptr;
|
||||
|
||||
// Copy the rawbuf
|
||||
for (uint16_t i = 0; i < dst->bufsize; i++)
|
||||
dst->rawbuf[i] = src->rawbuf[i];
|
||||
}
|
||||
|
||||
// Obtain the maximum number of entries possible in the capture buffer.
|
||||
// i.e. It's size.
|
||||
uint16_t IRrecv::getBufSize() {
|
||||
return irparams.bufsize;
|
||||
}
|
||||
|
||||
#if DECODE_HASH
|
||||
// Set the minimum length we will consider for reporting UNKNOWN message types.
|
||||
void IRrecv::setUnknownThreshold(uint16_t length) {
|
||||
unknown_threshold = length;
|
||||
}
|
||||
#endif // DECODE_HASH
|
||||
|
||||
// Decodes the received IR message.
|
||||
// If the interrupt state is saved, we will immediately resume waiting
|
||||
// for the next IR message to avoid missing messages.
|
||||
// Note: There is a trade-off here. Saving the state means less time lost until
|
||||
// we can receiving the next message vs. using more RAM. Choose appropriately.
|
||||
//
|
||||
// Args:
|
||||
// results: A pointer to where the decoded IR message will be stored.
|
||||
// save: A pointer to an irparams_t instance in which to save
|
||||
// the interrupt's memory/state. NULL means don't save it.
|
||||
// Returns:
|
||||
// A boolean indicating if an IR message is ready or not.
|
||||
bool IRrecv::decode(decode_results *results, irparams_t *save) {
|
||||
// Proceed only if an IR message been received.
|
||||
#ifndef UNIT_TEST
|
||||
if (irparams.rcvstate != STATE_STOP)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Clear the entry we are currently pointing to when we got the timeout.
|
||||
// i.e. Stopped collecting IR data.
|
||||
// It's junk as we never wrote an entry to it and can only confuse decoding.
|
||||
// This is done here rather than logically the best place in read_timeout()
|
||||
// as it saves a few bytes of ICACHE_RAM as that routine is bound to an
|
||||
// interrupt. decode() is not stored in ICACHE_RAM.
|
||||
// Another better option would be to zero the entire irparams.rawbuf[] on
|
||||
// resume() but that is a much more expensive operation compare to this.
|
||||
irparams.rawbuf[irparams.rawlen] = 0;
|
||||
|
||||
bool resumed = false; // Flag indicating if we have resumed.
|
||||
|
||||
// If we were requested to use a save buffer previously, do so.
|
||||
if (save == NULL)
|
||||
save = irparams_save;
|
||||
|
||||
if (save == NULL) {
|
||||
// We haven't been asked to copy it so use the existing memory.
|
||||
#ifndef UNIT_TEST
|
||||
results->rawbuf = irparams.rawbuf;
|
||||
results->rawlen = irparams.rawlen;
|
||||
results->overflow = irparams.overflow;
|
||||
#endif
|
||||
} else {
|
||||
copyIrParams(&irparams, save); // Duplicate the interrupt's memory.
|
||||
resume(); // It's now safe to rearm. The IR message won't be overridden.
|
||||
resumed = true;
|
||||
// Point the results at the saved copy.
|
||||
results->rawbuf = save->rawbuf;
|
||||
results->rawlen = save->rawlen;
|
||||
results->overflow = save->overflow;
|
||||
}
|
||||
|
||||
// Reset any previously partially processed results.
|
||||
results->decode_type = UNKNOWN;
|
||||
results->bits = 0;
|
||||
results->value = 0;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
results->repeat = false;
|
||||
|
||||
#if DECODE_AIWA_RC_T501
|
||||
DPRINTLN("Attempting Aiwa RC T501 decode");
|
||||
// Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC()
|
||||
// because the protocols are similar. This protocol is more specific than
|
||||
// those ones, so should got before them.
|
||||
if (decodeAiwaRCT501(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_SANYO
|
||||
DPRINTLN("Attempting Sanyo LC7461 decode");
|
||||
// Try decodeSanyoLC7461() before decodeNEC() because the protocols are
|
||||
// similar in timings & structure, but the Sanyo one is much longer than the
|
||||
// NEC protocol (42 vs 32 bits) so this one should be tried first to try to
|
||||
// reduce false detection as a NEC packet.
|
||||
if (decodeSanyoLC7461(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_CARRIER_AC
|
||||
DPRINTLN("Attempting Carrier AC decode");
|
||||
// Try decodeCarrierAC() before decodeNEC() because the protocols are
|
||||
// similar in timings & structure, but the Carrier one is much longer than the
|
||||
// NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried first to
|
||||
// try to reduce false detection as a NEC packet.
|
||||
if (decodeCarrierAC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_NEC
|
||||
DPRINTLN("Attempting NEC decode");
|
||||
if (decodeNEC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_SONY
|
||||
DPRINTLN("Attempting Sony decode");
|
||||
if (decodeSony(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI
|
||||
DPRINTLN("Attempting Mitsubishi decode");
|
||||
if (decodeMitsubishi(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI2
|
||||
DPRINTLN("Attempting Mitsubishi2 decode");
|
||||
if (decodeMitsubishi2(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_RC5
|
||||
DPRINTLN("Attempting RC5 decode");
|
||||
if (decodeRC5(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_RC6
|
||||
DPRINTLN("Attempting RC6 decode");
|
||||
if (decodeRC6(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_RCMM
|
||||
DPRINTLN("Attempting RC-MM decode");
|
||||
if (decodeRCMM(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_FUJITSU_AC
|
||||
// Fujitsu A/C needs to precede Panasonic and Denon as it has a short
|
||||
// message which looks exactly the same as a Panasonic/Denon message.
|
||||
DPRINTLN("Attempting Fujitsu A/C decode");
|
||||
if (decodeFujitsuAC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_DENON
|
||||
// Denon needs to precede Panasonic as it is a special case of Panasonic.
|
||||
DPRINTLN("Attempting Denon decode");
|
||||
if (decodeDenon(results, DENON_48_BITS) ||
|
||||
decodeDenon(results, DENON_BITS) ||
|
||||
decodeDenon(results, DENON_LEGACY_BITS))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_PANASONIC
|
||||
DPRINTLN("Attempting Panasonic decode");
|
||||
if (decodePanasonic(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_LG
|
||||
DPRINTLN("Attempting LG (28-bit) decode");
|
||||
if (decodeLG(results, LG_BITS, true))
|
||||
return true;
|
||||
DPRINTLN("Attempting LG (32-bit) decode");
|
||||
// LG32 should be tried before Samsung
|
||||
if (decodeLG(results, LG32_BITS, true))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_GICABLE
|
||||
// Note: Needs to happen before JVC decode, because it looks similar except
|
||||
// with a required NEC-like repeat code.
|
||||
DPRINTLN("Attempting GICable decode");
|
||||
if (decodeGICable(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_JVC
|
||||
DPRINTLN("Attempting JVC decode");
|
||||
if (decodeJVC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_SAMSUNG
|
||||
DPRINTLN("Attempting SAMSUNG decode");
|
||||
if (decodeSAMSUNG(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_WHYNTER
|
||||
DPRINTLN("Attempting Whynter decode");
|
||||
if (decodeWhynter(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_DISH
|
||||
DPRINTLN("Attempting DISH decode");
|
||||
if (decodeDISH(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_SHARP
|
||||
DPRINTLN("Attempting Sharp decode");
|
||||
if (decodeSharp(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_COOLIX
|
||||
DPRINTLN("Attempting Coolix decode");
|
||||
if (decodeCOOLIX(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_NIKAI
|
||||
DPRINTLN("Attempting Nikai decode");
|
||||
if (decodeNikai(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_KELVINATOR
|
||||
// Kelvinator based-devices use a similar code to Gree ones, to avoid false
|
||||
// matches this needs to happen before decodeGree().
|
||||
DPRINTLN("Attempting Kelvinator decode");
|
||||
if (decodeKelvinator(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_DAIKIN
|
||||
DPRINTLN("Attempting Daikin decode");
|
||||
if (decodeDaikin(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_TOSHIBA_AC
|
||||
DPRINTLN("Attempting Toshiba AC decode");
|
||||
if (decodeToshibaAC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_MIDEA
|
||||
DPRINTLN("Attempting Midea decode");
|
||||
if (decodeMidea(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_MAGIQUEST
|
||||
DPRINTLN("Attempting Magiquest decode");
|
||||
if (decodeMagiQuest(results))
|
||||
return true;
|
||||
#endif
|
||||
/* NOTE: Disabled due to poor quality.
|
||||
#if DECODE_SANYO
|
||||
// The Sanyo S866500B decoder is very poor quality & depricated.
|
||||
// *IF* you are going to enable it, do it near last to avoid false positive
|
||||
// matches.
|
||||
DPRINTLN("Attempting Sanyo SA8650B decode");
|
||||
if (decodeSanyo(results))
|
||||
return true;
|
||||
#endif
|
||||
*/
|
||||
#if DECODE_NEC
|
||||
// Some devices send NEC-like codes that don't follow the true NEC spec.
|
||||
// This should detect those. e.g. Apple TV remote etc.
|
||||
// This needs to be done after all other codes that use strict and some
|
||||
// other protocols that are NEC-like as well, as turning off strict may
|
||||
// cause this to match other valid protocols.
|
||||
DPRINTLN("Attempting NEC (non-strict) decode");
|
||||
if (decodeNEC(results, NEC_BITS, false)) {
|
||||
results->decode_type = NEC_LIKE;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if DECODE_LASERTAG
|
||||
DPRINTLN("Attempting Lasertag decode");
|
||||
if (decodeLasertag(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_GREE
|
||||
// Gree based-devices use a similar code to Kelvinator ones, to avoid false
|
||||
// matches this needs to happen after decodeKelvinator().
|
||||
DPRINTLN("Attempting Gree decode");
|
||||
if (decodeGree(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HAIER_AC
|
||||
DPRINTLN("Attempting Haier AC decode");
|
||||
if (decodeHaierAC(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HAIER_AC_YRW02
|
||||
DPRINTLN("Attempting Haier AC YR-W02 decode");
|
||||
if (decodeHaierACYRW02(results))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HITACHI_AC2
|
||||
// HitachiAC2 should be checked before HitachiAC
|
||||
DPRINTLN("Attempting Hitachi AC2 decode");
|
||||
if (decodeHitachiAC(results, HITACHI_AC2_BITS))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HITACHI_AC
|
||||
DPRINTLN("Attempting Hitachi AC decode");
|
||||
if (decodeHitachiAC(results, HITACHI_AC_BITS))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HITACHI_AC1
|
||||
DPRINTLN("Attempting Hitachi AC1 decode");
|
||||
if (decodeHitachiAC(results, HITACHI_AC1_BITS))
|
||||
return true;
|
||||
#endif
|
||||
#if DECODE_HASH
|
||||
// decodeHash returns a hash on any input.
|
||||
// Thus, it needs to be last in the list.
|
||||
// If you add any decodes, add them before this.
|
||||
if (decodeHash(results)) {
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_HASH
|
||||
// Throw away and start over
|
||||
if (!resumed) // Check if we have already resumed.
|
||||
resume();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the lower bound of the nr. of ticks.
|
||||
//
|
||||
// Args:
|
||||
// usecs: Nr. of uSeconds.
|
||||
// tolerance: Percent as an integer. e.g. 10 is 10%
|
||||
// delta: A non-scaling amount to reduce usecs by.
|
||||
// Returns:
|
||||
// Nr. of ticks.
|
||||
uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
|
||||
// max() used to ensure the result can't drop below 0 before the cast.
|
||||
return((uint32_t) std::max(
|
||||
(int32_t) (usecs * (1.0 - tolerance / 100.0) - delta), 0));
|
||||
}
|
||||
|
||||
// Calculate the upper bound of the nr. of ticks.
|
||||
//
|
||||
// Args:
|
||||
// usecs: Nr. of uSeconds.
|
||||
// tolerance: Percent as an integer. e.g. 10 is 10%
|
||||
// delta: A non-scaling amount to increase usecs by.
|
||||
// Returns:
|
||||
// Nr. of ticks.
|
||||
uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
|
||||
return((uint32_t) (usecs * (1.0 + tolerance / 100.0)) + 1 + delta);
|
||||
}
|
||||
|
||||
// Check if we match a pulse(measured) with the desired within
|
||||
// +/-tolerance percent and/or +/- a fixed delta range.
|
||||
//
|
||||
// Args:
|
||||
// measured: The recorded period of the signal pulse.
|
||||
// desired: The expected period (in useconds) we are matching against.
|
||||
// tolerance: A percentage expressed as an integer. e.g. 10 is 10%.
|
||||
// delta: A non-scaling (+/-) error margin (in useconds).
|
||||
//
|
||||
// Returns:
|
||||
// Boolean: true if it matches, false if it doesn't.
|
||||
bool IRrecv::match(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance, uint16_t delta) {
|
||||
measured *= RAWTICK; // Convert to uSecs.
|
||||
DPRINT("Matching: ");
|
||||
DPRINT(ticksLow(desired, tolerance, delta));
|
||||
DPRINT(" <= ");
|
||||
DPRINT(measured);
|
||||
DPRINT(" <= ");
|
||||
DPRINTLN(ticksHigh(desired, tolerance, delta));
|
||||
return (measured >= ticksLow(desired, tolerance, delta) &&
|
||||
measured <= ticksHigh(desired, tolerance, delta));
|
||||
}
|
||||
|
||||
|
||||
// Check if we match a pulse(measured) of at least desired within
|
||||
// tolerance percent and/or a fixed delta margin.
|
||||
//
|
||||
// Args:
|
||||
// measured: The recorded period of the signal pulse.
|
||||
// desired: The expected period (in useconds) we are matching against.
|
||||
// tolerance: A percentage expressed as an integer. e.g. 10 is 10%.
|
||||
// delta: A non-scaling amount to reduce usecs by.
|
||||
|
||||
//
|
||||
// Returns:
|
||||
// Boolean: true if it matches, false if it doesn't.
|
||||
bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance, uint16_t delta) {
|
||||
measured *= RAWTICK; // Convert to uSecs.
|
||||
DPRINT("Matching ATLEAST ");
|
||||
DPRINT(measured);
|
||||
DPRINT(" vs ");
|
||||
DPRINT(desired);
|
||||
DPRINT(". Matching: ");
|
||||
DPRINT(measured);
|
||||
DPRINT(" >= ");
|
||||
DPRINT(ticksLow(std::min(desired, MS_TO_USEC(irparams.timeout)), tolerance,
|
||||
delta));
|
||||
DPRINT(" [min(");
|
||||
DPRINT(ticksLow(desired, tolerance, delta));
|
||||
DPRINT(", ");
|
||||
DPRINT(ticksLow(MS_TO_USEC(irparams.timeout), tolerance, delta));
|
||||
DPRINTLN(")]");
|
||||
// We really should never get a value of 0, except as the last value
|
||||
// in the buffer. If that is the case, then assume infinity and return true.
|
||||
if (measured == 0) return true;
|
||||
return measured >= ticksLow(std::min(desired, MS_TO_USEC(irparams.timeout)),
|
||||
tolerance, delta);
|
||||
}
|
||||
|
||||
// Check if we match a mark signal(measured) with the desired within
|
||||
// +/-tolerance percent, after an expected is excess is added.
|
||||
//
|
||||
// Args:
|
||||
// measured: The recorded period of the signal pulse.
|
||||
// desired: The expected period (in useconds) we are matching against.
|
||||
// tolerance: A percentage expressed as an integer. e.g. 10 is 10%.
|
||||
// excess: Nr. of useconds.
|
||||
//
|
||||
// Returns:
|
||||
// Boolean: true if it matches, false if it doesn't.
|
||||
bool IRrecv::matchMark(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance, int16_t excess) {
|
||||
DPRINT("Matching MARK ");
|
||||
DPRINT(measured * RAWTICK);
|
||||
DPRINT(" vs ");
|
||||
DPRINT(desired);
|
||||
DPRINT(" + ");
|
||||
DPRINT(excess);
|
||||
DPRINT(". ");
|
||||
return match(measured, desired + excess, tolerance);
|
||||
}
|
||||
|
||||
// Check if we match a space signal(measured) with the desired within
|
||||
// +/-tolerance percent, after an expected is excess is removed.
|
||||
//
|
||||
// Args:
|
||||
// measured: The recorded period of the signal pulse.
|
||||
// desired: The expected period (in useconds) we are matching against.
|
||||
// tolerance: A percentage expressed as an integer. e.g. 10 is 10%.
|
||||
// excess: Nr. of useconds.
|
||||
//
|
||||
// Returns:
|
||||
// Boolean: true if it matches, false if it doesn't.
|
||||
bool IRrecv::matchSpace(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance, int16_t excess) {
|
||||
DPRINT("Matching SPACE ");
|
||||
DPRINT(measured * RAWTICK);
|
||||
DPRINT(" vs ");
|
||||
DPRINT(desired);
|
||||
DPRINT(" - ");
|
||||
DPRINT(excess);
|
||||
DPRINT(". ");
|
||||
return match(measured, desired - excess, tolerance);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* hashdecode - decode an arbitrary IR code.
|
||||
* Instead of decoding using a standard encoding scheme
|
||||
* (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value.
|
||||
*
|
||||
* The algorithm: look at the sequence of MARK signals, and see if each one
|
||||
* is shorter (0), the same length (1), or longer (2) than the previous.
|
||||
* Do the same with the SPACE signals. Hash the resulting sequence of 0's,
|
||||
* 1's, and 2's to a 32-bit value. This will give a unique value for each
|
||||
* different code (probably), for most code systems.
|
||||
*
|
||||
* http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html
|
||||
*/
|
||||
|
||||
// Compare two tick values, returning 0 if newval is shorter,
|
||||
// 1 if newval is equal, and 2 if newval is longer
|
||||
// Use a tolerance of 20%
|
||||
int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) {
|
||||
if (newval < oldval * 0.8)
|
||||
return 0;
|
||||
else if (oldval < newval * 0.8)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if DECODE_HASH
|
||||
/* Converts the raw code values into a 32-bit hash code.
|
||||
* Hopefully this code is unique for each button.
|
||||
* This isn't a "real" decoding, just an arbitrary value.
|
||||
*/
|
||||
bool IRrecv::decodeHash(decode_results *results) {
|
||||
// Require at least some samples to prevent triggering on noise
|
||||
if (results->rawlen < unknown_threshold)
|
||||
return false;
|
||||
int32_t hash = FNV_BASIS_32;
|
||||
// 'rawlen - 2' to avoid the look ahead from going out of bounds.
|
||||
// Should probably be -3 to avoid comparing the trailing space entry,
|
||||
// however it is left this way for compatibility with previously captured
|
||||
// values.
|
||||
for (uint16_t i = 1; i < results->rawlen - 2; i++) {
|
||||
int16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]);
|
||||
// Add value into the hash
|
||||
hash = (hash * FNV_PRIME_32) ^ value;
|
||||
}
|
||||
results->value = hash & 0xFFFFFFFF;
|
||||
results->bits = results->rawlen / 2;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
results->decode_type = UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_HASH
|
||||
|
||||
// Match & decode the typical data section of an IR message.
|
||||
// The data value constructed as the Most Significant Bit first.
|
||||
//
|
||||
// Args:
|
||||
// data_ptr: A pointer to where we are at in the capture buffer.
|
||||
// nbits: Nr. of data bits we expect.
|
||||
// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit.
|
||||
// onespace: Nr. of uSeconds in an expected space signal for a '1' bit.
|
||||
// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit.
|
||||
// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit.
|
||||
// tolerance: Percentage error margin to allow.
|
||||
// Returns:
|
||||
// A match_result_t structure containing the success (or not), the data value,
|
||||
// and how many buffer entries were used.
|
||||
match_result_t IRrecv::matchData(volatile uint16_t *data_ptr,
|
||||
const uint16_t nbits, const uint16_t onemark,
|
||||
const uint32_t onespace,
|
||||
const uint16_t zeromark,
|
||||
const uint32_t zerospace,
|
||||
const uint8_t tolerance) {
|
||||
match_result_t result;
|
||||
result.success = false; // Fail by default.
|
||||
result.data = 0;
|
||||
for (result.used = 0;
|
||||
result.used < nbits * 2;
|
||||
result.used += 2, data_ptr += 2) {
|
||||
// Is the bit a '1'?
|
||||
if (matchMark(*data_ptr, onemark, tolerance) &&
|
||||
matchSpace(*(data_ptr + 1), onespace, tolerance))
|
||||
result.data = (result.data << 1) | 1;
|
||||
// or is the bit a '0'?
|
||||
else if (matchMark(*data_ptr, zeromark, tolerance) &&
|
||||
matchSpace(*(data_ptr + 1), zerospace, tolerance))
|
||||
result.data <<= 1;
|
||||
else
|
||||
return result; // It's neither, so fail.
|
||||
}
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// End of IRrecv class -------------------
|
||||
@@ -0,0 +1,298 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2015 Sebastien Warin
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef IRRECV_H_
|
||||
#define IRRECV_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Constants
|
||||
#define HEADER 2U // Usual nr. of header entries.
|
||||
#define FOOTER 2U // Usual nr. of footer (stop bits) entries.
|
||||
#define OFFSET_START 1U // Usual rawbuf entry to start processing from.
|
||||
#define MS_TO_USEC(x) (x * 1000U) // Convert milli-Seconds to micro-Seconds.
|
||||
// Marks tend to be 100us too long, and spaces 100us too short
|
||||
// when received due to sensor lag.
|
||||
#define MARK_EXCESS 50U
|
||||
#define RAWBUF 100U // Default length of raw capture buffer
|
||||
#define REPEAT UINT64_MAX
|
||||
#define UNKNOWN_THRESHOLD 6U // Default min size of reported UNKNOWN messages.
|
||||
// receiver states
|
||||
#define STATE_IDLE 2U
|
||||
#define STATE_MARK 3U
|
||||
#define STATE_SPACE 4U
|
||||
#define STATE_STOP 5U
|
||||
#define TOLERANCE 25U // default percent tolerance in measurements
|
||||
#define RAWTICK 2U // Capture tick to uSec factor.
|
||||
// How long (ms) before we give up wait for more data?
|
||||
// Don't exceed MAX_TIMEOUT_MS without a good reason.
|
||||
// That is the capture buffers maximum value size. (UINT16_MAX / RAWTICK)
|
||||
// Typically messages/protocols tend to repeat around the 100ms timeframe,
|
||||
// thus we should timeout before that to give us some time to try to decode
|
||||
// before we need to start capturing a possible new message.
|
||||
// Typically 15ms suits most applications. However, some protocols demand a
|
||||
// higher value. e.g. 90ms for XMP-1 and some aircon units.
|
||||
#define TIMEOUT_MS 15U // In MilliSeconds.
|
||||
#define MAX_TIMEOUT_MS (RAWTICK * UINT16_MAX / MS_TO_USEC(1))
|
||||
|
||||
// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
|
||||
#define FNV_PRIME_32 16777619UL
|
||||
#define FNV_BASIS_32 2166136261UL
|
||||
|
||||
// Hitachi AC is the current largest state size.
|
||||
#define STATE_SIZE_MAX HITACHI_AC2_STATE_LENGTH
|
||||
|
||||
// Types
|
||||
// information for the interrupt handler
|
||||
typedef struct {
|
||||
uint8_t recvpin; // pin for IR data from detector
|
||||
uint8_t rcvstate; // state machine
|
||||
uint16_t timer; // state timer, counts 50uS ticks.
|
||||
uint16_t bufsize; // max. nr. of entries in the capture buffer.
|
||||
uint16_t *rawbuf; // raw data
|
||||
// uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt
|
||||
// handler. Don't ask why, I don't know. It just does.
|
||||
uint16_t rawlen; // counter of entries in rawbuf.
|
||||
uint8_t overflow; // Buffer overflow indicator.
|
||||
uint8_t timeout; // Nr. of milliSeconds before we give up.
|
||||
} irparams_t;
|
||||
|
||||
// results from a data match
|
||||
typedef struct {
|
||||
bool success; // Was the match successful?
|
||||
uint64_t data; // The data found.
|
||||
uint16_t used; // How many buffer positions were used.
|
||||
} match_result_t;
|
||||
|
||||
// Classes
|
||||
|
||||
// Results returned from the decoder
|
||||
class decode_results {
|
||||
public:
|
||||
decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN
|
||||
// value, address, & command are all mutually exclusive with state.
|
||||
// i.e. They MUST NOT be used at the same time as state, so we can use a union
|
||||
// structure to save us a handful of valuable bytes of memory.
|
||||
union {
|
||||
struct {
|
||||
uint64_t value; // Decoded value
|
||||
uint32_t address; // Decoded device address.
|
||||
uint32_t command; // Decoded command.
|
||||
};
|
||||
#if DECODE_AC // Only include state if we must. It's big.
|
||||
uint8_t state[STATE_SIZE_MAX]; // Complex multi-byte A/C result.
|
||||
#endif
|
||||
};
|
||||
uint16_t bits; // Number of bits in decoded value
|
||||
volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks
|
||||
uint16_t rawlen; // Number of records in rawbuf.
|
||||
bool overflow;
|
||||
bool repeat; // Is the result a repeat code?
|
||||
};
|
||||
|
||||
// main class for receiving IR
|
||||
class IRrecv {
|
||||
public:
|
||||
explicit IRrecv(uint16_t recvpin, uint16_t bufsize = RAWBUF,
|
||||
uint8_t timeout = TIMEOUT_MS,
|
||||
bool save_buffer = false); // Constructor
|
||||
~IRrecv(); // Destructor
|
||||
bool decode(decode_results *results, irparams_t *save = NULL);
|
||||
void enableIRIn();
|
||||
void disableIRIn();
|
||||
void resume();
|
||||
uint16_t getBufSize();
|
||||
#if DECODE_HASH
|
||||
void setUnknownThreshold(uint16_t length);
|
||||
#endif
|
||||
static bool match(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance = TOLERANCE, uint16_t delta = 0);
|
||||
static bool matchMark(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance = TOLERANCE,
|
||||
int16_t excess = MARK_EXCESS);
|
||||
static bool matchSpace(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance = TOLERANCE,
|
||||
int16_t excess = MARK_EXCESS);
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
#endif
|
||||
irparams_t *irparams_save;
|
||||
#if DECODE_HASH
|
||||
uint16_t unknown_threshold;
|
||||
#endif
|
||||
// These are called by decode
|
||||
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
|
||||
int16_t compare(uint16_t oldval, uint16_t newval);
|
||||
static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = TOLERANCE,
|
||||
uint16_t delta = 0);
|
||||
static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = TOLERANCE,
|
||||
uint16_t delta = 0);
|
||||
bool matchAtLeast(uint32_t measured, uint32_t desired,
|
||||
uint8_t tolerance = TOLERANCE, uint16_t delta = 0);
|
||||
match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance = TOLERANCE);
|
||||
bool decodeHash(decode_results *results);
|
||||
#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO)
|
||||
bool decodeNEC(decode_results *results, uint16_t nbits = NEC_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SONY
|
||||
bool decodeSony(decode_results *results, uint16_t nbits = SONY_MIN_BITS,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if DECODE_SANYO
|
||||
// DISABLED due to poor quality.
|
||||
// bool decodeSanyo(decode_results *results,
|
||||
// uint16_t nbits = SANYO_SA8650B_BITS,
|
||||
// bool strict = false);
|
||||
bool decodeSanyoLC7461(decode_results *results,
|
||||
uint16_t nbits = SANYO_LC7461_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI
|
||||
bool decodeMitsubishi(decode_results *results,
|
||||
uint16_t nbits = MITSUBISHI_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI2
|
||||
bool decodeMitsubishi2(decode_results *results,
|
||||
uint16_t nbits = MITSUBISHI_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG)
|
||||
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
|
||||
uint16_t bitTime, uint8_t tolerance = TOLERANCE,
|
||||
int16_t excess = MARK_EXCESS, uint16_t delta = 0);
|
||||
#endif
|
||||
#if DECODE_RC5
|
||||
bool decodeRC5(decode_results *results, uint16_t nbits = RC5X_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_RC6
|
||||
bool decodeRC6(decode_results *results, uint16_t nbits = RC6_MODE0_BITS,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if DECODE_RCMM
|
||||
bool decodeRCMM(decode_results *results, uint16_t nbits = RCMM_BITS,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if (DECODE_PANASONIC || DECODE_DENON)
|
||||
bool decodePanasonic(decode_results *results, uint16_t nbits = PANASONIC_BITS,
|
||||
bool strict = false,
|
||||
uint32_t manufacturer = PANASONIC_MANUFACTURER);
|
||||
#endif
|
||||
#if DECODE_LG
|
||||
bool decodeLG(decode_results *results, uint16_t nbits = LG_BITS,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if DECODE_JVC
|
||||
bool decodeJVC(decode_results *results, uint16_t nbits = JVC_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG
|
||||
bool decodeSAMSUNG(decode_results *results, uint16_t nbits = SAMSUNG_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_WHYNTER
|
||||
bool decodeWhynter(decode_results *results, uint16_t nbits = WHYNTER_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_COOLIX
|
||||
bool decodeCOOLIX(decode_results *results, uint16_t nbits = COOLIX_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DENON
|
||||
bool decodeDenon(decode_results *results, uint16_t nbits = DENON_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DISH
|
||||
bool decodeDISH(decode_results *results, uint16_t nbits = DISH_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_SHARP || DECODE_DENON)
|
||||
bool decodeSharp(decode_results *results, uint16_t nbits = SHARP_BITS,
|
||||
bool strict = true, bool expansion = true);
|
||||
#endif
|
||||
#if DECODE_AIWA_RC_T501
|
||||
bool decodeAiwaRCT501(decode_results *results,
|
||||
uint16_t nbits = AIWA_RC_T501_BITS, bool strict = true);
|
||||
#endif
|
||||
#if DECODE_NIKAI
|
||||
bool decodeNikai(decode_results *results, uint16_t nbits = NIKAI_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MAGIQUEST
|
||||
bool decodeMagiQuest(decode_results *results, uint16_t nbits = MAGIQUEST_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_KELVINATOR
|
||||
bool decodeKelvinator(decode_results *results,
|
||||
uint16_t nbits = KELVINATOR_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DAIKIN
|
||||
bool decodeDaikin(decode_results *results, uint16_t nbits = DAIKIN_RAW_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_TOSHIBA_AC
|
||||
bool decodeToshibaAC(decode_results *results,
|
||||
uint16_t nbytes = TOSHIBA_AC_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MIDEA
|
||||
bool decodeMidea(decode_results *results, uint16_t nbits = MIDEA_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_FUJITSU_AC
|
||||
bool decodeFujitsuAC(decode_results *results,
|
||||
uint16_t nbits = FUJITSU_AC_BITS,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if DECODE_LASERTAG
|
||||
bool decodeLasertag(decode_results *results, uint16_t nbits = LASERTAG_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_CARRIER_AC
|
||||
bool decodeCarrierAC(decode_results *results,
|
||||
uint16_t nbits = CARRIER_AC_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_GREE
|
||||
bool decodeGree(decode_results *results,
|
||||
uint16_t nbits = GREE_BITS, bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02)
|
||||
bool decodeHaierAC(decode_results *results,
|
||||
uint16_t nbits = HAIER_AC_BITS, bool strict = true);
|
||||
#endif
|
||||
#if DECODE_HAIER_AC_YRW02
|
||||
bool decodeHaierACYRW02(decode_results *results,
|
||||
uint16_t nbits = HAIER_AC_YRW02_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2)
|
||||
bool decodeHitachiAC(decode_results *results,
|
||||
uint16_t nbits = HITACHI_AC_BITS, bool strict = true);
|
||||
#endif
|
||||
#if DECODE_HITACHI_AC1
|
||||
bool decodeHitachiAC1(decode_results *results,
|
||||
uint16_t nbits = HITACHI_AC1_BITS, bool strict = true);
|
||||
#endif
|
||||
#if DECODE_GICABLE
|
||||
bool decodeGICable(decode_results *results, uint16_t nbits = GICABLE_BITS,
|
||||
bool strict = true);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // IRRECV_H_
|
||||
@@ -0,0 +1,350 @@
|
||||
/***************************************************
|
||||
* IRremote for ESP8266
|
||||
*
|
||||
* Based on the IRremote library for Arduino by Ken Shirriff
|
||||
* Version 0.11 August, 2009
|
||||
* Copyright 2009 Ken Shirriff
|
||||
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
|
||||
*
|
||||
* Edited by Mitra to add new controller SANYO
|
||||
*
|
||||
* Interrupt code based on NECIRrcv by Joe Knapp
|
||||
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
|
||||
* Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
|
||||
*
|
||||
* JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
|
||||
* LG added by Darryl Smith (based on the JVC protocol)
|
||||
* Whynter A/C ARC-110WD added by Francesco Meschia
|
||||
* Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit
|
||||
* Denon: sendDenon, decodeDenon added by Massimiliano Pinto
|
||||
(from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp)
|
||||
* Kelvinator A/C and Sherwood added by crankyoldgit
|
||||
* Mitsubishi (TV) sending added by crankyoldgit
|
||||
* Pronto code sending added by crankyoldgit
|
||||
* Mitsubishi & Toshiba A/C added by crankyoldgit
|
||||
* (derived from https://github.com/r45635/HVAC-IR-Control)
|
||||
* DISH decode by marcosamarinho
|
||||
* Gree Heatpump sending added by Ville Skyttä (scop)
|
||||
* (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp)
|
||||
* Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for sending IR code on ESP8266
|
||||
* Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266
|
||||
*
|
||||
* Updated by sillyfrog for Daikin, adopted from
|
||||
* (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/)
|
||||
* Fujitsu A/C code added by jonnygraham
|
||||
* Trotec AC code by stufisher
|
||||
* Carrier & Haier AC code by crankyoldgit
|
||||
*
|
||||
* GPL license, all text above must be included in any redistribution
|
||||
****************************************************/
|
||||
|
||||
#ifndef IRREMOTEESP8266_H_
|
||||
#define IRREMOTEESP8266_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifdef UNIT_TEST
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
// Library Version
|
||||
#define _IRREMOTEESP8266_VERSION_ "2.4.3"
|
||||
// Supported IR protocols
|
||||
// Each protocol you include costs memory and, during decode, costs time
|
||||
// Disable (set to false) all the protocols you do not need/want!
|
||||
// The Air Conditioner protocols are the most expensive memory-wise.
|
||||
//
|
||||
#define DECODE_HASH true // Semi-unique code for unknown messages
|
||||
|
||||
#define SEND_RAW true
|
||||
|
||||
#define DECODE_NEC true
|
||||
#define SEND_NEC true
|
||||
|
||||
#define DECODE_SHERWOOD true // Doesn't exist. Actually is DECODE_NEC
|
||||
#define SEND_SHERWOOD true
|
||||
|
||||
#define DECODE_RC5 true
|
||||
#define SEND_RC5 true
|
||||
|
||||
#define DECODE_RC6 true
|
||||
#define SEND_RC6 true
|
||||
|
||||
#define DECODE_RCMM true
|
||||
#define SEND_RCMM true
|
||||
|
||||
#define DECODE_SONY true
|
||||
#define SEND_SONY true
|
||||
|
||||
#define DECODE_PANASONIC true
|
||||
#define SEND_PANASONIC true
|
||||
|
||||
#define DECODE_JVC true
|
||||
#define SEND_JVC true
|
||||
|
||||
#define DECODE_SAMSUNG true
|
||||
#define SEND_SAMSUNG true
|
||||
|
||||
#define DECODE_WHYNTER true
|
||||
#define SEND_WHYNTER true
|
||||
|
||||
#define DECODE_AIWA_RC_T501 true
|
||||
#define SEND_AIWA_RC_T501 true
|
||||
|
||||
#define DECODE_LG true
|
||||
#define SEND_LG true
|
||||
|
||||
#define DECODE_SANYO true
|
||||
#define SEND_SANYO true
|
||||
|
||||
#define DECODE_MITSUBISHI true
|
||||
#define SEND_MITSUBISHI true
|
||||
|
||||
#define DECODE_MITSUBISHI2 true
|
||||
#define SEND_MITSUBISHI2 true
|
||||
|
||||
#define DECODE_DISH true
|
||||
#define SEND_DISH true
|
||||
|
||||
#define DECODE_SHARP true
|
||||
#define SEND_SHARP true
|
||||
|
||||
#define DECODE_DENON true
|
||||
#define SEND_DENON true
|
||||
|
||||
#define DECODE_KELVINATOR true
|
||||
#define SEND_KELVINATOR true
|
||||
|
||||
#define DECODE_MITSUBISHI_AC false // Not written.
|
||||
#define SEND_MITSUBISHI_AC true
|
||||
|
||||
#define DECODE_FUJITSU_AC true
|
||||
#define SEND_FUJITSU_AC true
|
||||
|
||||
#define DECODE_DAIKIN true
|
||||
#define SEND_DAIKIN true
|
||||
|
||||
#define DECODE_COOLIX true
|
||||
#define SEND_COOLIX true
|
||||
|
||||
#define DECODE_GLOBALCACHE false // Not written.
|
||||
#define SEND_GLOBALCACHE true
|
||||
|
||||
#define DECODE_GREE true
|
||||
#define SEND_GREE true
|
||||
|
||||
#define DECODE_PRONTO false // Not written.
|
||||
#define SEND_PRONTO true
|
||||
|
||||
#define DECODE_ARGO false // Not written.
|
||||
#define SEND_ARGO true
|
||||
|
||||
#define DECODE_TROTEC false // Not implemented.
|
||||
#define SEND_TROTEC true
|
||||
|
||||
#define DECODE_NIKAI true
|
||||
#define SEND_NIKAI true
|
||||
|
||||
#define DECODE_TOSHIBA_AC true
|
||||
#define SEND_TOSHIBA_AC true
|
||||
|
||||
#define DECODE_MAGIQUEST true
|
||||
#define SEND_MAGIQUEST true
|
||||
|
||||
#define DECODE_MIDEA true
|
||||
#define SEND_MIDEA true
|
||||
|
||||
#define DECODE_LASERTAG true
|
||||
#define SEND_LASERTAG true
|
||||
|
||||
#define DECODE_CARRIER_AC true
|
||||
#define SEND_CARRIER_AC true
|
||||
|
||||
#define DECODE_HAIER_AC true
|
||||
#define SEND_HAIER_AC true
|
||||
|
||||
#define DECODE_HITACHI_AC true
|
||||
#define SEND_HITACHI_AC true
|
||||
|
||||
#define DECODE_HITACHI_AC1 true
|
||||
#define SEND_HITACHI_AC1 true
|
||||
|
||||
#define DECODE_HITACHI_AC2 true
|
||||
#define SEND_HITACHI_AC2 true
|
||||
|
||||
#define DECODE_GICABLE true
|
||||
#define SEND_GICABLE true
|
||||
|
||||
#define DECODE_HAIER_AC_YRW02 true
|
||||
#define SEND_HAIER_AC_YRW02 true
|
||||
|
||||
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
|
||||
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
|
||||
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
|
||||
DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02)
|
||||
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
|
||||
#else
|
||||
#define DECODE_AC false // We don't need that infrastructure.
|
||||
#endif
|
||||
|
||||
// Use millisecond 'delay()' calls where we can to avoid tripping the WDT.
|
||||
// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver
|
||||
// library, you need to set ALLOW_DELAY_CALLS to false.
|
||||
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/430
|
||||
#define ALLOW_DELAY_CALLS true
|
||||
|
||||
/*
|
||||
* Always add to the end of the list and should never remove entries
|
||||
* or change order. Projects may save the type number for later usage
|
||||
* so numbering should always stay the same.
|
||||
*/
|
||||
enum decode_type_t {
|
||||
UNKNOWN = -1,
|
||||
UNUSED = 0,
|
||||
RC5,
|
||||
RC6,
|
||||
NEC,
|
||||
SONY,
|
||||
PANASONIC,
|
||||
JVC,
|
||||
SAMSUNG,
|
||||
WHYNTER,
|
||||
AIWA_RC_T501,
|
||||
LG,
|
||||
SANYO,
|
||||
MITSUBISHI,
|
||||
DISH,
|
||||
SHARP,
|
||||
COOLIX,
|
||||
DAIKIN,
|
||||
DENON,
|
||||
KELVINATOR,
|
||||
SHERWOOD,
|
||||
MITSUBISHI_AC,
|
||||
RCMM,
|
||||
SANYO_LC7461,
|
||||
RC5X,
|
||||
GREE,
|
||||
PRONTO, // Technically not a protocol, but an encoding.
|
||||
NEC_LIKE,
|
||||
ARGO,
|
||||
TROTEC,
|
||||
NIKAI,
|
||||
RAW, // Technically not a protocol, but an encoding.
|
||||
GLOBALCACHE, // Technically not a protocol, but an encoding.
|
||||
TOSHIBA_AC,
|
||||
FUJITSU_AC,
|
||||
MIDEA,
|
||||
MAGIQUEST,
|
||||
LASERTAG,
|
||||
CARRIER_AC,
|
||||
HAIER_AC,
|
||||
MITSUBISHI2,
|
||||
HITACHI_AC,
|
||||
HITACHI_AC1,
|
||||
HITACHI_AC2,
|
||||
GICABLE,
|
||||
HAIER_AC_YRW02
|
||||
};
|
||||
|
||||
// Message lengths & required repeat values
|
||||
#define AIWA_RC_T501_BITS 15U
|
||||
#define AIWA_RC_T501_MIN_REPEAT 1U
|
||||
#define COOLIX_BITS 24U
|
||||
#define CARRIER_AC_BITS 32U
|
||||
#define CARRIER_AC_MIN_REPEAT 0U
|
||||
// Daikin has a lot of static stuff that is discarded
|
||||
#define DAIKIN_RAW_BITS 583U
|
||||
#define DAIKIN_COMMAND_LENGTH 27U
|
||||
#define DAIKIN_BITS (DAIKIN_COMMAND_LENGTH * 8)
|
||||
#define DENON_BITS SHARP_BITS
|
||||
#define DENON_48_BITS PANASONIC_BITS
|
||||
#define DENON_LEGACY_BITS 14U
|
||||
#define DISH_BITS 16U
|
||||
#define DISH_MIN_REPEAT 3U
|
||||
#define GICABLE_BITS 16U
|
||||
#define GICABLE_MIN_REPEAT 1U
|
||||
#define GREE_STATE_LENGTH 8U
|
||||
#define GREE_BITS (GREE_STATE_LENGTH * 8)
|
||||
#define HAIER_AC_STATE_LENGTH 9U
|
||||
#define HAIER_AC_BITS (HAIER_AC_STATE_LENGTH * 8)
|
||||
#define HAIER_AC_YRW02_STATE_LENGTH 14U
|
||||
#define HAIER_AC_YRW02_BITS (HAIER_AC_YRW02_STATE_LENGTH * 8)
|
||||
#define HITACHI_AC_STATE_LENGTH 28U
|
||||
#define HITACHI_AC_BITS (HITACHI_AC_STATE_LENGTH * 8)
|
||||
#define HITACHI_AC1_STATE_LENGTH 13U
|
||||
#define HITACHI_AC1_BITS (HITACHI_AC1_STATE_LENGTH * 8)
|
||||
#define HITACHI_AC2_STATE_LENGTH 53U
|
||||
#define HITACHI_AC2_BITS (HITACHI_AC2_STATE_LENGTH * 8)
|
||||
#define JVC_BITS 16U
|
||||
#define KELVINATOR_STATE_LENGTH 16U
|
||||
#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8)
|
||||
#define LG_BITS 28U
|
||||
#define LG32_BITS 32U
|
||||
#define MITSUBISHI_BITS 16U
|
||||
// TODO(anyone): Verify that the Mitsubishi repeat is really needed.
|
||||
#define MITSUBISHI_MIN_REPEAT 1U // Based on marcosamarinho's code.
|
||||
#define MITSUBISHI_AC_STATE_LENGTH 18U
|
||||
#define MITSUBISHI_AC_MIN_REPEAT 1U
|
||||
#define FUJITSU_AC_MIN_REPEAT 0U
|
||||
#define FUJITSU_AC_STATE_LENGTH 16U
|
||||
#define FUJITSU_AC_STATE_LENGTH_SHORT 7U
|
||||
#define FUJITSU_AC_BITS (FUJITSU_AC_STATE_LENGTH * 8)
|
||||
#define FUJITSU_AC_MIN_BITS ((FUJITSU_AC_STATE_LENGTH_SHORT - 1) * 8)
|
||||
#define NEC_BITS 32U
|
||||
#define PANASONIC_BITS 48U
|
||||
#define PANASONIC_MANUFACTURER 0x4004ULL
|
||||
#define PRONTO_MIN_LENGTH 6U
|
||||
#define RC5_RAW_BITS 14U
|
||||
#define RC5_BITS RC5_RAW_BITS - 2U
|
||||
#define RC5X_BITS RC5_RAW_BITS - 1U
|
||||
#define RC6_MODE0_BITS 20U // Excludes the 'start' bit.
|
||||
#define RC6_36_BITS 36U // Excludes the 'start' bit.
|
||||
#define RCMM_BITS 24U
|
||||
#define SAMSUNG_BITS 32U
|
||||
#define SANYO_SA8650B_BITS 12U
|
||||
#define SANYO_LC7461_ADDRESS_BITS 13U
|
||||
#define SANYO_LC7461_COMMAND_BITS 8U
|
||||
#define SANYO_LC7461_BITS ((SANYO_LC7461_ADDRESS_BITS + \
|
||||
SANYO_LC7461_COMMAND_BITS) * 2)
|
||||
#define SHARP_ADDRESS_BITS 5U
|
||||
#define SHARP_COMMAND_BITS 8U
|
||||
#define SHARP_BITS (SHARP_ADDRESS_BITS + SHARP_COMMAND_BITS + 2) // 15U
|
||||
#define SHERWOOD_BITS NEC_BITS
|
||||
#define SHERWOOD_MIN_REPEAT 1U
|
||||
#define SONY_12_BITS 12U
|
||||
#define SONY_15_BITS 15U
|
||||
#define SONY_20_BITS 20U
|
||||
#define SONY_MIN_BITS SONY_12_BITS
|
||||
#define SONY_MIN_REPEAT 2U
|
||||
#define TOSHIBA_AC_STATE_LENGTH 9U
|
||||
#define TOSHIBA_AC_BITS (TOSHIBA_AC_STATE_LENGTH * 8)
|
||||
#define TOSHIBA_AC_MIN_REPEAT 1U
|
||||
#define TROTEC_COMMAND_LENGTH 9U
|
||||
#define WHYNTER_BITS 32U
|
||||
#define ARGO_COMMAND_LENGTH 12U
|
||||
#define NIKAI_BITS 24U
|
||||
#define MAGIQUEST_BITS 56U
|
||||
#define MIDEA_BITS 48U
|
||||
#define MIDEA_MIN_REPEAT 0U
|
||||
#define LASERTAG_BITS 13U
|
||||
#define LASERTAG_MIN_REPEAT 0U
|
||||
|
||||
// Turn on Debugging information by uncommenting the following line.
|
||||
// #define DEBUG 1
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef UNIT_TEST
|
||||
#define DPRINT(x) do { std::cout << x; } while (0)
|
||||
#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0)
|
||||
#endif // UNIT_TEST
|
||||
#ifdef ARDUINO
|
||||
#define DPRINT(x) do { Serial.print(x); } while (0)
|
||||
#define DPRINTLN(x) do { Serial.println(x); } while (0)
|
||||
#endif // ARDUINO
|
||||
#else // DEBUG
|
||||
#define DPRINT(x)
|
||||
#define DPRINTLN(x)
|
||||
#endif // DEBUG
|
||||
|
||||
#endif // IRREMOTEESP8266_H_
|
||||
@@ -0,0 +1,553 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRsend.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#ifdef UNIT_TEST
|
||||
#include <cmath>
|
||||
#endif
|
||||
#include "IRtimer.h"
|
||||
|
||||
// Originally from https://github.com/shirriff/Arduino-IRremote/
|
||||
// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
|
||||
// sending IR code on ESP8266
|
||||
|
||||
// IRsend ----------------------------------------------------------------------
|
||||
// Create an IRsend object.
|
||||
//
|
||||
// Args:
|
||||
// IRsendPin: Which GPIO pin to use when sending an IR command.
|
||||
// inverted: *DANGER* Optional flag to invert the output. (default = false)
|
||||
// e.g. LED is illuminated when GPIO is LOW rather than HIGH.
|
||||
// Setting this to something other than the default could
|
||||
// easily destroy your IR LED if you are overdriving it.
|
||||
// Unless you *REALLY* know what you are doing, don't change this.
|
||||
// use_modulation: Do we do frequency modulation during transmission?
|
||||
// i.e. If not, assume a 100% duty cycle. Ignore attempts
|
||||
// to change the duty cycle etc.
|
||||
// Returns:
|
||||
// An IRsend object.
|
||||
IRsend::IRsend(uint16_t IRsendPin, bool inverted,
|
||||
bool use_modulation) : IRpin(IRsendPin),
|
||||
periodOffset(PERIOD_OFFSET) {
|
||||
if (inverted) {
|
||||
outputOn = LOW;
|
||||
outputOff = HIGH;
|
||||
} else {
|
||||
outputOn = HIGH;
|
||||
outputOff = LOW;
|
||||
}
|
||||
modulation = use_modulation;
|
||||
if (modulation)
|
||||
_dutycycle = DUTY_DEFAULT;
|
||||
else
|
||||
_dutycycle = DUTY_MAX;
|
||||
}
|
||||
|
||||
// Enable the pin for output.
|
||||
void IRsend::begin() {
|
||||
#ifndef UNIT_TEST
|
||||
pinMode(IRpin, OUTPUT);
|
||||
#endif
|
||||
ledOff(); // Ensure the LED is in a known safe state when we start.
|
||||
}
|
||||
|
||||
// Turn off the IR LED.
|
||||
void IRsend::ledOff() {
|
||||
#ifndef UNIT_TEST
|
||||
digitalWrite(IRpin, outputOff);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Turn on the IR LED.
|
||||
void IRsend::ledOn() {
|
||||
#ifndef UNIT_TEST
|
||||
digitalWrite(IRpin, outputOn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calculate the period for a given frequency. (T = 1/f)
|
||||
//
|
||||
// Args:
|
||||
// freq: Frequency in Hz.
|
||||
// use_offset: Should we use the calculated offset or not?
|
||||
// Returns:
|
||||
// nr. of uSeconds.
|
||||
uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) {
|
||||
if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty.
|
||||
uint32_t period = (1000000UL + hz/2) / hz; // The equiv of round(1000000/hz).
|
||||
// Apply the offset and ensure we don't result in a <= 0 value.
|
||||
if (use_offset)
|
||||
return std::max((uint32_t) 1, period + periodOffset);
|
||||
else
|
||||
return std::max((uint32_t) 1, period);
|
||||
}
|
||||
|
||||
// Set the output frequency modulation and duty cycle.
|
||||
//
|
||||
// Args:
|
||||
// freq: The freq we want to modulate at. Assumes < 1000 means kHz else Hz.
|
||||
// duty: Percentage duty cycle of the LED. e.g. 25 = 25% = 1/4 on, 3/4 off.
|
||||
// This is ignored if modulation is disabled at object instantiation.
|
||||
//
|
||||
// Note:
|
||||
// Integer timing functions & math mean we can't do fractions of
|
||||
// microseconds timing. Thus minor changes to the freq & duty values may have
|
||||
// limited effect. You've been warned.
|
||||
void IRsend::enableIROut(uint32_t freq, uint8_t duty) {
|
||||
// Set the duty cycle to use if we want freq. modulation.
|
||||
if (modulation) {
|
||||
_dutycycle = std::min(duty, (uint8_t) DUTY_MAX);
|
||||
} else {
|
||||
_dutycycle = DUTY_MAX;
|
||||
}
|
||||
if (freq < 1000) // Were we given kHz? Supports the old call usage.
|
||||
freq *= 1000;
|
||||
uint32_t period = calcUSecPeriod(freq);
|
||||
// Nr. of uSeconds the LED will be on per pulse.
|
||||
onTimePeriod = (period * _dutycycle) / DUTY_MAX;
|
||||
// Nr. of uSeconds the LED will be off per pulse.
|
||||
offTimePeriod = period - onTimePeriod;
|
||||
}
|
||||
|
||||
#if ALLOW_DELAY_CALLS
|
||||
// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds().
|
||||
// Args:
|
||||
// usec: Nr. of uSeconds to delay for.
|
||||
void IRsend::_delayMicroseconds(uint32_t usec) {
|
||||
// delayMicroseconds() is only accurate to 16383us.
|
||||
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
|
||||
if (usec <= MAX_ACCURATE_USEC_DELAY) {
|
||||
#ifndef UNIT_TEST
|
||||
delayMicroseconds(usec);
|
||||
#endif
|
||||
} else {
|
||||
#ifndef UNIT_TEST
|
||||
// Invoke a delay(), where possible, to avoid triggering the WDT.
|
||||
delay(usec / 1000UL); // Delay for as many whole milliseconds as we can.
|
||||
// Delay the remaining sub-millisecond.
|
||||
delayMicroseconds(static_cast<uint16_t>(usec % 1000UL));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else // ALLOW_DELAY_CALLS
|
||||
// A version of delayMicroseconds() that handles large values and does NOT use
|
||||
// the watch-dog friendly delay() calls where appropriate.
|
||||
// Args:
|
||||
// usec: Nr. of uSeconds to delay for.
|
||||
//
|
||||
// NOTE: Use this only if you know what you are doing as it may cause the WDT
|
||||
// to reset the ESP8266.
|
||||
void IRsend::_delayMicroseconds(uint32_t usec) {
|
||||
for (; usec > MAX_ACCURATE_USEC_DELAY; usec -= MAX_ACCURATE_USEC_DELAY)
|
||||
#ifndef UNIT_TEST
|
||||
delayMicroseconds(MAX_ACCURATE_USEC_DELAY);
|
||||
delayMicroseconds(static_cast<uint16_t>(usec));
|
||||
#endif // UNIT_TEST
|
||||
}
|
||||
#endif // ALLOW_DELAY_CALLS
|
||||
|
||||
// Modulate the IR LED for the given period (usec) and at the duty cycle set.
|
||||
//
|
||||
// Args:
|
||||
// usec: The period of time to modulate the IR LED for, in microseconds.
|
||||
// Returns:
|
||||
// Nr. of pulses actually sent.
|
||||
//
|
||||
// Note:
|
||||
// The ESP8266 has no good way to do hardware PWM, so we have to do it all
|
||||
// in software. There is a horrible kludge/brilliant hack to use the second
|
||||
// serial TX line to do fairly accurate hardware PWM, but it is only
|
||||
// available on a single specific GPIO and only available on some modules.
|
||||
// e.g. It's not available on the ESP-01 module.
|
||||
// Hence, for greater compatibility & choice, we don't use that method.
|
||||
// Ref:
|
||||
// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/
|
||||
uint16_t IRsend::mark(uint16_t usec) {
|
||||
// Handle the simple case of no required frequency modulation.
|
||||
if (!modulation || _dutycycle >= 100) {
|
||||
ledOn();
|
||||
_delayMicroseconds(usec);
|
||||
ledOff();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Not simple, so do it assuming frequency modulation.
|
||||
uint16_t counter = 0;
|
||||
IRtimer usecTimer = IRtimer();
|
||||
// Cache the time taken so far. This saves us calling time, and we can be
|
||||
// assured that we can't have odd math problems. i.e. unsigned under/overflow.
|
||||
uint32_t elapsed = usecTimer.elapsed();
|
||||
|
||||
while (elapsed < usec) { // Loop until we've met/exceeded our required time.
|
||||
ledOn();
|
||||
// Calculate how long we should pulse on for.
|
||||
// e.g. Are we to close to the end of our requested mark time (usec)?
|
||||
_delayMicroseconds(std::min((uint32_t) onTimePeriod, usec - elapsed));
|
||||
ledOff();
|
||||
counter++;
|
||||
if (elapsed + onTimePeriod >= usec)
|
||||
return counter; // LED is now off & we've passed our allotted time.
|
||||
// Wait for the lesser of the rest of the duty cycle, or the time remaining.
|
||||
_delayMicroseconds(std::min(usec - elapsed - onTimePeriod,
|
||||
(uint32_t) offTimePeriod));
|
||||
elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time.
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
// Turn the pin (LED) off for a given time.
|
||||
// Sends an IR space for the specified number of microseconds.
|
||||
// A space is no output, so the PWM output is disabled.
|
||||
//
|
||||
// Args:
|
||||
// time: Time in microseconds (us).
|
||||
void IRsend::space(uint32_t time) {
|
||||
ledOff();
|
||||
if (time == 0) return;
|
||||
_delayMicroseconds(time);
|
||||
}
|
||||
|
||||
// Calculate & set any offsets to account for execution times.
|
||||
//
|
||||
// Args:
|
||||
// hz: The frequency to calibrate at >= 1000Hz. Default is 38000Hz.
|
||||
//
|
||||
// Returns:
|
||||
// The calculated period offset (in uSeconds) which is now in use. e.g. -5.
|
||||
//
|
||||
// Status: Stable / Working.
|
||||
//
|
||||
// NOTE:
|
||||
// This will generate an 65535us mark() IR LED signal.
|
||||
// This only needs to be called once, if at all.
|
||||
int8_t IRsend::calibrate(uint16_t hz) {
|
||||
if (hz < 1000) // Were we given kHz? Supports the old call usage.
|
||||
hz *= 1000;
|
||||
periodOffset = 0; // Turn off any existing offset while we calibrate.
|
||||
enableIROut(hz);
|
||||
IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call.
|
||||
uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.)
|
||||
uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took.
|
||||
// While it shouldn't be necessary, assume at least 1 pulse, to avoid a
|
||||
// divide by 0 situation.
|
||||
pulses = std::max(pulses, (uint16_t) 1U);
|
||||
uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us.
|
||||
// Assuming 38kHz for the example calculations:
|
||||
// In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods.
|
||||
// e.g. 65535.0us / 26us = 2520.5769
|
||||
// This should have caused approx 2520 loops through the main loop in mark().
|
||||
// The average over that many interations should give us a reasonable
|
||||
// approximation at what offset we need to use to account for instruction
|
||||
// execution times.
|
||||
//
|
||||
// Calculate the actual period from the actual time & the actual pulses
|
||||
// generated.
|
||||
double_t actualPeriod = (double_t) timeTaken / (double_t) pulses;
|
||||
// Store the difference between the actual time per period vs. calculated.
|
||||
periodOffset = (int8_t) ((double_t) calcPeriod - actualPeriod);
|
||||
return periodOffset;
|
||||
}
|
||||
|
||||
// Generic method for sending data that is common to most protocols.
|
||||
// Will send leading or trailing 0's if the nbits is larger than the number
|
||||
// of bits in data.
|
||||
//
|
||||
// Args:
|
||||
// onemark: Nr. of usecs for the led to be pulsed for a '1' bit.
|
||||
// onespace: Nr. of usecs for the led to be fully off for a '1' bit.
|
||||
// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit.
|
||||
// zerospace: Nr. of usecs for the led to be fully off for a '0' bit.
|
||||
// data: The data to be transmitted.
|
||||
// nbits: Nr. of bits of data to be sent.
|
||||
// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order.
|
||||
void IRsend::sendData(uint16_t onemark, uint32_t onespace,
|
||||
uint16_t zeromark, uint32_t zerospace,
|
||||
uint64_t data, uint16_t nbits, bool MSBfirst) {
|
||||
if (nbits == 0) // If we are asked to send nothing, just return.
|
||||
return;
|
||||
if (MSBfirst) { // Send the MSB first.
|
||||
// Send 0's until we get down to a bit size we can actually manage.
|
||||
while (nbits > sizeof(data) * 8) {
|
||||
mark(zeromark);
|
||||
space(zerospace);
|
||||
nbits--;
|
||||
}
|
||||
// Send the supplied data.
|
||||
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
|
||||
if (data & mask) { // Send a 1
|
||||
mark(onemark);
|
||||
space(onespace);
|
||||
} else { // Send a 0
|
||||
mark(zeromark);
|
||||
space(zerospace);
|
||||
}
|
||||
} else { // Send the Least Significant Bit (LSB) first / MSB last.
|
||||
for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1)
|
||||
if (data & 1) { // Send a 1
|
||||
mark(onemark);
|
||||
space(onespace);
|
||||
} else { // Send a 0
|
||||
mark(zeromark);
|
||||
space(zerospace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generic method for sending simple protocol messages.
|
||||
// Will send leading or trailing 0's if the nbits is larger than the number
|
||||
// of bits in data.
|
||||
//
|
||||
// Args:
|
||||
// headermark: Nr. of usecs for the led to be pulsed for the header mark.
|
||||
// A value of 0 means no header mark.
|
||||
// headerspace: Nr. of usecs for the led to be off after the header mark.
|
||||
// A value of 0 means no header space.
|
||||
// onemark: Nr. of usecs for the led to be pulsed for a '1' bit.
|
||||
// onespace: Nr. of usecs for the led to be fully off for a '1' bit.
|
||||
// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit.
|
||||
// zerospace: Nr. of usecs for the led to be fully off for a '0' bit.
|
||||
// footermark: Nr. of usecs for the led to be pulsed for the footer mark.
|
||||
// A value of 0 means no footer mark.
|
||||
// gap: Nr. of usecs for the led to be off after the footer mark.
|
||||
// This is effectively the gap between messages.
|
||||
// A value of 0 means no gap space.
|
||||
// data: The data to be transmitted.
|
||||
// nbits: Nr. of bits of data to be sent.
|
||||
// frequency: The frequency we want to modulate at.
|
||||
// Assumes < 1000 means kHz otherwise it is in Hz.
|
||||
// Most common value is 38000 or 38, for 38kHz.
|
||||
// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order.
|
||||
// repeat: Nr. of extra times the message will be sent.
|
||||
// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages
|
||||
// dutycycle: Percentage duty cycle of the LED.
|
||||
// e.g. 25 = 25% = 1/4 on, 3/4 off.
|
||||
// If you are not sure, try 50 percent.
|
||||
void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle) {
|
||||
sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace,
|
||||
footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat,
|
||||
dutycycle);
|
||||
}
|
||||
|
||||
// Generic method for sending simple protocol messages.
|
||||
// Will send leading or trailing 0's if the nbits is larger than the number
|
||||
// of bits in data.
|
||||
//
|
||||
// Args:
|
||||
// headermark: Nr. of usecs for the led to be pulsed for the header mark.
|
||||
// A value of 0 means no header mark.
|
||||
// headerspace: Nr. of usecs for the led to be off after the header mark.
|
||||
// A value of 0 means no header space.
|
||||
// onemark: Nr. of usecs for the led to be pulsed for a '1' bit.
|
||||
// onespace: Nr. of usecs for the led to be fully off for a '1' bit.
|
||||
// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit.
|
||||
// zerospace: Nr. of usecs for the led to be fully off for a '0' bit.
|
||||
// footermark: Nr. of usecs for the led to be pulsed for the footer mark.
|
||||
// A value of 0 means no footer mark.
|
||||
// gap: Min. nr. of usecs for the led to be off after the footer mark.
|
||||
// This is effectively the absolute minimum gap between messages.
|
||||
// mesgtime: Min. nr. of usecs a single message needs to be.
|
||||
// This is effectively the min. total length of a single message.
|
||||
// data: The data to be transmitted.
|
||||
// nbits: Nr. of bits of data to be sent.
|
||||
// frequency: The frequency we want to modulate at.
|
||||
// Assumes < 1000 means kHz otherwise it is in Hz.
|
||||
// Most common value is 38000 or 38, for 38kHz.
|
||||
// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order.
|
||||
// repeat: Nr. of extra times the message will be sent.
|
||||
// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages
|
||||
// dutycycle: Percentage duty cycle of the LED.
|
||||
// e.g. 25 = 25% = 1/4 on, 3/4 off.
|
||||
// If you are not sure, try 50 percent.
|
||||
void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint32_t mesgtime,
|
||||
const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle) {
|
||||
// Setup
|
||||
enableIROut(frequency, dutycycle);
|
||||
IRtimer usecs = IRtimer();
|
||||
|
||||
// We always send a message, even for repeat=0, hence '<= repeat'.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
usecs.reset();
|
||||
|
||||
// Header
|
||||
if (headermark) mark(headermark);
|
||||
if (headerspace) space(headerspace);
|
||||
|
||||
// Data
|
||||
sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst);
|
||||
|
||||
// Footer
|
||||
if (footermark) mark(footermark);
|
||||
uint32_t elapsed = usecs.elapsed();
|
||||
// Avoid potential unsigned integer underflow. e.g. when mesgtime is 0.
|
||||
if (elapsed >= mesgtime)
|
||||
space(gap);
|
||||
else
|
||||
space(std::max(gap, mesgtime - elapsed));
|
||||
}
|
||||
}
|
||||
|
||||
// Generic method for sending simple protocol messages.
|
||||
//
|
||||
// Args:
|
||||
// headermark: Nr. of usecs for the led to be pulsed for the header mark.
|
||||
// A value of 0 means no header mark.
|
||||
// headerspace: Nr. of usecs for the led to be off after the header mark.
|
||||
// A value of 0 means no header space.
|
||||
// onemark: Nr. of usecs for the led to be pulsed for a '1' bit.
|
||||
// onespace: Nr. of usecs for the led to be fully off for a '1' bit.
|
||||
// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit.
|
||||
// zerospace: Nr. of usecs for the led to be fully off for a '0' bit.
|
||||
// footermark: Nr. of usecs for the led to be pulsed for the footer mark.
|
||||
// A value of 0 means no footer mark.
|
||||
// gap: Nr. of usecs for the led to be off after the footer mark.
|
||||
// This is effectively the gap between messages.
|
||||
// A value of 0 means no gap space.
|
||||
// dataptr: Pointer to the data to be transmitted.
|
||||
// nbytes: Nr. of bytes of data to be sent.
|
||||
// frequency: The frequency we want to modulate at.
|
||||
// Assumes < 1000 means kHz otherwise it is in Hz.
|
||||
// Most common value is 38000 or 38, for 38kHz.
|
||||
// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order.
|
||||
// repeat: Nr. of extra times the message will be sent.
|
||||
// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages
|
||||
// dutycycle: Percentage duty cycle of the LED.
|
||||
// e.g. 25 = 25% = 1/4 on, 3/4 off.
|
||||
// If you are not sure, try 50 percent.
|
||||
void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint8_t *dataptr, const uint16_t nbytes,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle) {
|
||||
// Setup
|
||||
enableIROut(frequency, dutycycle);
|
||||
// We always send a message, even for repeat=0, hence '<= repeat'.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
if (headermark) mark(headermark);
|
||||
if (headerspace) space(headerspace);
|
||||
|
||||
// Data
|
||||
for (uint16_t i = 0; i < nbytes; i++)
|
||||
sendData(onemark, onespace, zeromark, zerospace,
|
||||
*(dataptr + i), 8, MSBfirst);
|
||||
|
||||
// Footer
|
||||
if (footermark) mark(footermark);
|
||||
space(gap);
|
||||
}
|
||||
}
|
||||
|
||||
#if SEND_RAW
|
||||
// Send a raw IRremote message.
|
||||
//
|
||||
// Args:
|
||||
// buf: An array of uint16_t's that has microseconds elements.
|
||||
// len: Nr. of elements in the buf[] array.
|
||||
// hz: Frequency to send the message at. (kHz < 1000; Hz >= 1000)
|
||||
//
|
||||
// Status: STABLE / Known working.
|
||||
//
|
||||
// Notes:
|
||||
// Even elements are Mark times (On), Odd elements are Space times (Off).
|
||||
//
|
||||
// Ref:
|
||||
// examples/IRrecvDumpV2/IRrecvDumpV2.ino
|
||||
void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) {
|
||||
// Set IR carrier frequency
|
||||
enableIROut(hz);
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
if (i & 1) { // Odd bit.
|
||||
space(buf[i]);
|
||||
} else { // Even bit.
|
||||
mark(buf[i]);
|
||||
}
|
||||
}
|
||||
ledOff(); // We potentially have ended with a mark(), so turn of the LED.
|
||||
}
|
||||
#endif // SEND_RAW
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
|
||||
switch (type) {
|
||||
#if SEND_NEC
|
||||
case NEC: sendNEC(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_SONY
|
||||
case SONY: sendSony(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_RC5
|
||||
case RC5: sendRC5(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_RC6
|
||||
case RC6: sendRC6(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_DISH
|
||||
case DISH: sendDISH(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_JVC
|
||||
case JVC: sendJVC(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_SAMSUNG
|
||||
case SAMSUNG: sendSAMSUNG(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_LG
|
||||
case LG: sendLG(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_WHYNTER
|
||||
case WHYNTER: sendWhynter(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_COOLIX
|
||||
case COOLIX: sendCOOLIX(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_DENON
|
||||
case DENON: sendDenon(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_SHERWOOD
|
||||
case SHERWOOD: sendSherwood(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_RCMM
|
||||
case RCMM: sendRCMM(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_MITSUBISHI
|
||||
case MITSUBISHI: sendMitsubishi(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_MITSUBISHI2
|
||||
case MITSUBISHI2: sendMitsubishi2(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_SHARP
|
||||
case SHARP: sendSharpRaw(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_AIWA_RC_T501
|
||||
case AIWA_RC_T501: sendAiwaRCT501(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_MIDEA
|
||||
case MIDEA: sendMidea(data, nbits); break;
|
||||
#endif
|
||||
#if SEND_GICABLE
|
||||
case GICABLE: sendGICable(data, nbits); break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,296 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2017 David Conran
|
||||
#ifndef IRSEND_H_
|
||||
#define IRSEND_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Originally from https://github.com/shirriff/Arduino-IRremote/
|
||||
// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
|
||||
// sending IR code on ESP8266
|
||||
|
||||
#if TEST || UNIT_TEST
|
||||
#define VIRTUAL virtual
|
||||
#else
|
||||
#define VIRTUAL
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
// Offset (in microseconds) to use in Period time calculations to account for
|
||||
// code excution time in producing the software PWM signal.
|
||||
// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core
|
||||
#define PERIOD_OFFSET -5
|
||||
#define DUTY_DEFAULT 50
|
||||
#define DUTY_MAX 100 // Percentage
|
||||
// delayMicroseconds() is only accurate to 16383us.
|
||||
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
|
||||
#define MAX_ACCURATE_USEC_DELAY 16383U
|
||||
|
||||
// Classes
|
||||
class IRsend {
|
||||
public:
|
||||
explicit IRsend(uint16_t IRsendPin, bool inverted = false,
|
||||
bool use_modulation = true);
|
||||
void begin();
|
||||
void enableIROut(uint32_t freq, uint8_t duty = DUTY_DEFAULT);
|
||||
VIRTUAL void _delayMicroseconds(uint32_t usec);
|
||||
VIRTUAL uint16_t mark(uint16_t usec);
|
||||
VIRTUAL void space(uint32_t usec);
|
||||
int8_t calibrate(uint16_t hz = 38000U);
|
||||
void sendRaw(uint16_t buf[], uint16_t len, uint16_t hz);
|
||||
void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark,
|
||||
uint32_t zerospace, uint64_t data, uint16_t nbits,
|
||||
bool MSBfirst = true);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint32_t mesgtime,
|
||||
const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint8_t *dataptr, const uint16_t nbytes,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
void send(uint16_t type, uint64_t data, uint16_t nbits);
|
||||
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
|
||||
void sendNEC(uint64_t data, uint16_t nbits = NEC_BITS, uint16_t repeat = 0);
|
||||
uint32_t encodeNEC(uint16_t address, uint16_t command);
|
||||
#endif
|
||||
#if SEND_SONY
|
||||
// sendSony() should typically be called with repeat=2 as Sony devices
|
||||
// expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes)
|
||||
// Legacy use of this procedure was to only send a single code so call it with
|
||||
// repeat=0 for backward compatibility. As of v2.0 it defaults to sending
|
||||
// a Sony command that will be accepted be a device.
|
||||
void sendSony(uint64_t data, uint16_t nbits = SONY_20_BITS,
|
||||
uint16_t repeat = SONY_MIN_REPEAT);
|
||||
uint32_t encodeSony(uint16_t nbits, uint16_t command, uint16_t address,
|
||||
uint16_t extended = 0);
|
||||
#endif
|
||||
#if SEND_SHERWOOD
|
||||
void sendSherwood(uint64_t data, uint16_t nbits = SHERWOOD_BITS,
|
||||
uint16_t repeat = SHERWOOD_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_SAMSUNG
|
||||
void sendSAMSUNG(uint64_t data, uint16_t nbits = SAMSUNG_BITS,
|
||||
uint16_t repeat = 0);
|
||||
uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command);
|
||||
#endif
|
||||
#if SEND_LG
|
||||
void sendLG(uint64_t data, uint16_t nbits = LG_BITS, uint16_t repeat = 0);
|
||||
uint32_t encodeLG(uint16_t address, uint16_t command);
|
||||
#endif
|
||||
#if (SEND_SHARP || SEND_DENON)
|
||||
uint32_t encodeSharp(uint16_t address, uint16_t command,
|
||||
uint16_t expansion = 1, uint16_t check = 0,
|
||||
bool MSBfirst = false);
|
||||
void sendSharp(uint16_t address, uint16_t command,
|
||||
uint16_t nbits = SHARP_BITS, uint16_t repeat = 0);
|
||||
void sendSharpRaw(uint64_t data, uint16_t nbits = SHARP_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_JVC
|
||||
void sendJVC(uint64_t data, uint16_t nbits = JVC_BITS, uint16_t repeat = 0);
|
||||
uint16_t encodeJVC(uint8_t address, uint8_t command);
|
||||
#endif
|
||||
#if SEND_DENON
|
||||
void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_SANYO
|
||||
uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command);
|
||||
void sendSanyoLC7461(uint64_t data, uint16_t nbits = SANYO_LC7461_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_DISH
|
||||
// sendDISH() should typically be called with repeat=3 as DISH devices
|
||||
// expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes)
|
||||
// Legacy use of this procedure was only to send a single code
|
||||
// so use repeat=0 for backward compatibility.
|
||||
void sendDISH(uint64_t data, uint16_t nbits = DISH_BITS,
|
||||
uint16_t repeat = DISH_MIN_REPEAT);
|
||||
#endif
|
||||
#if (SEND_PANASONIC || SEND_DENON)
|
||||
void sendPanasonic64(uint64_t data, uint16_t nbits = PANASONIC_BITS,
|
||||
uint16_t repeat = 0);
|
||||
void sendPanasonic(uint16_t address, uint32_t data,
|
||||
uint16_t nbits = PANASONIC_BITS, uint16_t repeat = 0);
|
||||
uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device,
|
||||
uint8_t subdevice, uint8_t function);
|
||||
#endif
|
||||
#if SEND_RC5
|
||||
void sendRC5(uint64_t data, uint16_t nbits = RC5X_BITS, uint16_t repeat = 0);
|
||||
uint16_t encodeRC5(uint8_t address, uint8_t command,
|
||||
bool key_released = false);
|
||||
uint16_t encodeRC5X(uint8_t address, uint8_t command,
|
||||
bool key_released = false);
|
||||
uint64_t toggleRC5(uint64_t data);
|
||||
#endif
|
||||
#if SEND_RC6
|
||||
void sendRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS,
|
||||
uint16_t repeat = 0);
|
||||
uint64_t encodeRC6(uint32_t address, uint8_t command,
|
||||
uint16_t mode = RC6_MODE0_BITS);
|
||||
uint64_t toggleRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS);
|
||||
#endif
|
||||
#if SEND_RCMM
|
||||
void sendRCMM(uint64_t data, uint16_t nbits = RCMM_BITS, uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_COOLIX
|
||||
void sendCOOLIX(uint64_t data, uint16_t nbits = COOLIX_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_WHYNTER
|
||||
void sendWhynter(uint64_t data, uint16_t nbits = WHYNTER_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI
|
||||
void sendMitsubishi(uint64_t data, uint16_t nbits = MITSUBISHI_BITS,
|
||||
uint16_t repeat = MITSUBISHI_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI2
|
||||
void sendMitsubishi2(uint64_t data, uint16_t nbits = MITSUBISHI_BITS,
|
||||
uint16_t repeat = MITSUBISHI_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI_AC
|
||||
void sendMitsubishiAC(unsigned char data[],
|
||||
uint16_t nbytes = MITSUBISHI_AC_STATE_LENGTH,
|
||||
uint16_t repeat = MITSUBISHI_AC_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_FUJITSU_AC
|
||||
void sendFujitsuAC(unsigned char data[],
|
||||
uint16_t nbytes,
|
||||
uint16_t repeat = FUJITSU_AC_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_GLOBALCACHE
|
||||
void sendGC(uint16_t buf[], uint16_t len);
|
||||
#endif
|
||||
#if SEND_KELVINATOR
|
||||
void sendKelvinator(unsigned char data[],
|
||||
uint16_t nbytes = KELVINATOR_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_DAIKIN
|
||||
void sendDaikin(unsigned char data[],
|
||||
uint16_t nbytes = DAIKIN_COMMAND_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
void sendDaikinGapHeader();
|
||||
#endif
|
||||
#if SEND_AIWA_RC_T501
|
||||
void sendAiwaRCT501(uint64_t data, uint16_t nbits = AIWA_RC_T501_BITS,
|
||||
uint16_t repeat = AIWA_RC_T501_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_GREE
|
||||
void sendGree(uint64_t data, uint16_t nbits = GREE_BITS, uint16_t repeat = 0);
|
||||
void sendGree(uint8_t data[], uint16_t nbytes = GREE_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_PRONTO
|
||||
void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_ARGO
|
||||
void sendArgo(unsigned char data[],
|
||||
uint16_t nbytes = ARGO_COMMAND_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_TROTEC
|
||||
void sendTrotec(unsigned char data[],
|
||||
uint16_t nbytes = TROTEC_COMMAND_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_NIKAI
|
||||
void sendNikai(uint64_t data, uint16_t nbits = NIKAI_BITS,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_TOSHIBA_AC
|
||||
void sendToshibaAC(unsigned char data[],
|
||||
uint16_t nbytes = TOSHIBA_AC_STATE_LENGTH,
|
||||
uint16_t repeat = TOSHIBA_AC_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_MIDEA
|
||||
void sendMidea(uint64_t data, uint16_t nbits = MIDEA_BITS,
|
||||
uint16_t repeat = MIDEA_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_MAGIQUEST
|
||||
void sendMagiQuest(uint64_t data, uint16_t nbits = MAGIQUEST_BITS,
|
||||
uint16_t repeat = 0);
|
||||
uint64_t encodeMagiQuest(uint32_t wand_id, uint16_t magnitude);
|
||||
#endif
|
||||
#if SEND_LASERTAG
|
||||
void sendLasertag(uint64_t data, uint16_t nbits = LASERTAG_BITS,
|
||||
uint16_t repeat = LASERTAG_MIN_REPEAT);
|
||||
#endif
|
||||
#if SEND_CARRIER_AC
|
||||
void sendCarrierAC(uint64_t data, uint16_t nbits = CARRIER_AC_BITS,
|
||||
uint16_t repeat = CARRIER_AC_MIN_REPEAT);
|
||||
#endif
|
||||
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
|
||||
void sendHaierAC(unsigned char data[],
|
||||
uint16_t nbytes = HAIER_AC_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void sendHaierACYRW02(unsigned char data[],
|
||||
uint16_t nbytes = HAIER_AC_YRW02_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC
|
||||
void sendHitachiAC(unsigned char data[],
|
||||
uint16_t nbytes = HITACHI_AC_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC1
|
||||
void sendHitachiAC1(unsigned char data[],
|
||||
uint16_t nbytes = HITACHI_AC1_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC2
|
||||
void sendHitachiAC2(unsigned char data[],
|
||||
uint16_t nbytes = HITACHI_AC2_STATE_LENGTH,
|
||||
uint16_t repeat = 0);
|
||||
#endif
|
||||
#if SEND_GICABLE
|
||||
void sendGICable(uint64_t data, uint16_t nbits = GICABLE_BITS,
|
||||
uint16_t repeat = GICABLE_MIN_REPEAT);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#ifdef UNIT_TEST
|
||||
#ifndef HIGH
|
||||
#define HIGH 0x1
|
||||
#endif
|
||||
#ifndef LOW
|
||||
#define LOW 0x0
|
||||
#endif
|
||||
#endif // UNIT_TEST
|
||||
uint8_t outputOn;
|
||||
uint8_t outputOff;
|
||||
VIRTUAL void ledOff();
|
||||
VIRTUAL void ledOn();
|
||||
|
||||
private:
|
||||
uint16_t onTimePeriod;
|
||||
uint16_t offTimePeriod;
|
||||
uint16_t IRpin;
|
||||
int8_t periodOffset;
|
||||
uint8_t _dutycycle;
|
||||
bool modulation;
|
||||
uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true);
|
||||
};
|
||||
|
||||
#endif // IRSEND_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRtimer.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
// Used to help simulate elapsed time in unit tests.
|
||||
extern uint32_t _IRtimer_unittest_now;
|
||||
#endif // UNIT_TEST
|
||||
|
||||
// This class performs a simple time in useconds since instantiated.
|
||||
// Handles when the system timer wraps around (once).
|
||||
|
||||
IRtimer::IRtimer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void IRtimer::reset() {
|
||||
#ifndef UNIT_TEST
|
||||
start = micros();
|
||||
#else
|
||||
start = _IRtimer_unittest_now;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t IRtimer::elapsed() {
|
||||
#ifndef UNIT_TEST
|
||||
uint32_t now = micros();
|
||||
#else
|
||||
uint32_t now = _IRtimer_unittest_now;
|
||||
#endif
|
||||
if (start <= now) // Check if the system timer has wrapped.
|
||||
return now - start; // No wrap.
|
||||
else
|
||||
return UINT32_MAX - start + now; // Has wrapped.
|
||||
}
|
||||
|
||||
// Only used in unit testing.
|
||||
#ifdef UNIT_TEST
|
||||
void IRtimer::add(uint32_t usecs) {
|
||||
_IRtimer_unittest_now += usecs;
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef IRTIMER_H_
|
||||
#define IRTIMER_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
|
||||
// Classes
|
||||
class IRtimer {
|
||||
public:
|
||||
IRtimer();
|
||||
void reset();
|
||||
uint32_t elapsed();
|
||||
#ifdef UNIT_TEST
|
||||
static void add(uint32_t usecs);
|
||||
#endif // UNIT_TEST
|
||||
|
||||
private:
|
||||
uint32_t start;
|
||||
};
|
||||
|
||||
#endif // IRTIMER_H_
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRutils.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Reverse the order of the requested least significant nr. of bits.
|
||||
// Args:
|
||||
// input: Bit pattern/integer to reverse.
|
||||
// nbits: Nr. of bits to reverse.
|
||||
// Returns:
|
||||
// The reversed bit pattern.
|
||||
uint64_t reverseBits(uint64_t input, uint16_t nbits) {
|
||||
if (nbits <= 1)
|
||||
return input; // Reversing <= 1 bits makes no change at all.
|
||||
// Cap the nr. of bits to rotate to the max nr. of bits in the input.
|
||||
nbits = std::min(nbits, (uint16_t) (sizeof(input) * 8));
|
||||
uint64_t output = 0;
|
||||
for (uint16_t i = 0; i < nbits; i++) {
|
||||
output <<= 1;
|
||||
output |= (input & 1);
|
||||
input >>= 1;
|
||||
}
|
||||
// Merge any remaining unreversed bits back to the top of the reversed bits.
|
||||
return (input << nbits) | output;
|
||||
}
|
||||
|
||||
// Convert a uint64_t (unsigned long long) to a string.
|
||||
// Arduino String/toInt/Serial.print() can't handle printing 64 bit values.
|
||||
//
|
||||
// Args:
|
||||
// input: The value to print
|
||||
// base: The output base.
|
||||
// Returns:
|
||||
// A string representation of the integer.
|
||||
// Note: Based on Arduino's Print::printNumber()
|
||||
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
|
||||
String uint64ToString(uint64_t input, uint8_t base) {
|
||||
String result = "";
|
||||
#else
|
||||
std::string uint64ToString(uint64_t input, uint8_t base) {
|
||||
std::string result = "";
|
||||
#endif
|
||||
// prevent issues if called with base <= 1
|
||||
if (base < 2) base = 10;
|
||||
// Check we have a base that we can actually print.
|
||||
// i.e. [0-9A-Z] == 36
|
||||
if (base > 36) base = 10;
|
||||
|
||||
do {
|
||||
char c = input % base;
|
||||
input /= base;
|
||||
|
||||
if (c < 10)
|
||||
c +='0';
|
||||
else
|
||||
c += 'A' - 10;
|
||||
result = c + result;
|
||||
} while (input);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO
|
||||
// Print a uint64_t/unsigned long long to the Serial port
|
||||
// Serial.print() can't handle printing long longs. (uint64_t)
|
||||
//
|
||||
// Args:
|
||||
// input: The value to print
|
||||
// base: The output base.
|
||||
void serialPrintUint64(uint64_t input, uint8_t base) {
|
||||
Serial.print(uint64ToString(input, base));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Convert a protocol type (enum etc) to a human readable string.
|
||||
// Args:
|
||||
// protocol: Nr. (enum) of the protocol.
|
||||
// isRepeat: A flag indicating if it is a repeat message of the protocol.
|
||||
// Returns:
|
||||
// A string containing the protocol name.
|
||||
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
|
||||
String typeToString(const decode_type_t protocol, const bool isRepeat) {
|
||||
String result = "";
|
||||
#else
|
||||
std::string typeToString(const decode_type_t protocol,
|
||||
const bool isRepeat) {
|
||||
std::string result = "";
|
||||
#endif
|
||||
switch (protocol) {
|
||||
default:
|
||||
case UNKNOWN: result = "UNKNOWN"; break;
|
||||
case UNUSED: result = "UNUSED"; break;
|
||||
case AIWA_RC_T501: result = "AIWA_RC_T501"; break;
|
||||
case ARGO: result = "ARGO"; break;
|
||||
case CARRIER_AC: result = "CARRIER_AC"; break;
|
||||
case COOLIX: result = "COOLIX"; break;
|
||||
case DAIKIN: result = "DAIKIN"; break;
|
||||
case DENON: result = "DENON"; break;
|
||||
case DISH: result = "DISH"; break;
|
||||
case FUJITSU_AC: result = "FUJITSU_AC"; break;
|
||||
case GICABLE: result = "GICABLE"; break;
|
||||
case GLOBALCACHE: result = "GLOBALCACHE"; break;
|
||||
case GREE: result = "GREE"; break;
|
||||
case HAIER_AC: result = "HAIER_AC"; break;
|
||||
case HAIER_AC_YRW02: result = "HAIER_AC_YRW02"; break;
|
||||
case HITACHI_AC: result = "HITACHI_AC"; break;
|
||||
case HITACHI_AC1: result = "HITACHI_AC1"; break;
|
||||
case HITACHI_AC2: result = "HITACHI_AC2"; break;
|
||||
case JVC: result = "JVC"; break;
|
||||
case KELVINATOR: result = "KELVINATOR"; break;
|
||||
case LG: result = "LG"; break;
|
||||
case LASERTAG: result = "LASERTAG"; break;
|
||||
case MAGIQUEST: result = "MAGIQUEST"; break;
|
||||
case MIDEA: result = "MIDEA"; break;
|
||||
case MITSUBISHI: result = "MITSUBISHI"; break;
|
||||
case MITSUBISHI2: result = "MITSUBISHI2"; break;
|
||||
case MITSUBISHI_AC: result = "MITSUBISHI_AC"; break;
|
||||
case NEC: result = "NEC"; break;
|
||||
case NEC_LIKE: result = "NEC (non-strict)"; break;
|
||||
case NIKAI: result = "NIKAI"; break;
|
||||
case PANASONIC: result = "PANASONIC"; break;
|
||||
case PRONTO: result = "PRONTO"; break;
|
||||
case RAW: result = "RAW"; break;
|
||||
case RC5: result = "RC5"; break;
|
||||
case RC5X: result = "RC5X"; break;
|
||||
case RC6: result = "RC6"; break;
|
||||
case RCMM: result = "RCMM"; break;
|
||||
case SAMSUNG: result = "SAMSUNG"; break;
|
||||
case SANYO: result = "SANYO"; break;
|
||||
case SANYO_LC7461: result = "SANYO_LC7461"; break;
|
||||
case SHARP: result = "SHARP"; break;
|
||||
case SHERWOOD: result = "SHERWOOD"; break;
|
||||
case SONY: result = "SONY"; break;
|
||||
case TOSHIBA_AC: result = "TOSHIBA_AC"; break;
|
||||
case TROTEC: result = "TROTEC"; break;
|
||||
case WHYNTER: result = "WHYNTER"; break;
|
||||
}
|
||||
if (isRepeat) result += " (Repeat)";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Does the given protocol use a complex state as part of the decode?
|
||||
bool hasACState(const decode_type_t protocol) {
|
||||
switch (protocol) {
|
||||
case DAIKIN:
|
||||
case FUJITSU_AC:
|
||||
case GREE:
|
||||
case HAIER_AC:
|
||||
case HAIER_AC_YRW02:
|
||||
case HITACHI_AC:
|
||||
case HITACHI_AC1:
|
||||
case HITACHI_AC2:
|
||||
case KELVINATOR:
|
||||
case MITSUBISHI_AC:
|
||||
case TOSHIBA_AC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the corrected length of a 'raw' format array structure
|
||||
// after over-large values are converted into multiple entries.
|
||||
// Args:
|
||||
// results: A ptr to a decode result.
|
||||
// Returns:
|
||||
// A uint16_t containing the length.
|
||||
uint16_t getCorrectedRawLength(const decode_results *results) {
|
||||
uint16_t extended_length = results->rawlen - 1;
|
||||
for (uint16_t i = 0; i < results->rawlen - 1; i++) {
|
||||
uint32_t usecs = results->rawbuf[i] * RAWTICK;
|
||||
// Add two extra entries for multiple larger than UINT16_MAX it is.
|
||||
extended_length += (usecs / (UINT16_MAX + 1)) * 2;
|
||||
}
|
||||
return extended_length;
|
||||
}
|
||||
|
||||
// Return a string containing the key values of a decode_results structure
|
||||
// in a C/C++ code style format.
|
||||
#ifdef ARDUINO
|
||||
String resultToSourceCode(const decode_results *results) {
|
||||
String output = "";
|
||||
#else
|
||||
std::string resultToSourceCode(const decode_results *results) {
|
||||
std::string output = "";
|
||||
#endif
|
||||
// Start declaration
|
||||
output += "uint16_t "; // variable type
|
||||
output += "rawData["; // array name
|
||||
output += uint64ToString(getCorrectedRawLength(results), 10);
|
||||
// array size
|
||||
output += "] = {"; // Start declaration
|
||||
|
||||
// Dump data
|
||||
for (uint16_t i = 1; i < results->rawlen; i++) {
|
||||
uint32_t usecs;
|
||||
for (usecs = results->rawbuf[i] * RAWTICK;
|
||||
usecs > UINT16_MAX;
|
||||
usecs -= UINT16_MAX) {
|
||||
output += uint64ToString(UINT16_MAX);
|
||||
if (i % 2)
|
||||
output += ", 0, ";
|
||||
else
|
||||
output += ", 0, ";
|
||||
}
|
||||
output += uint64ToString(usecs, 10);
|
||||
if (i < results->rawlen - 1)
|
||||
output += ", "; // ',' not needed on the last one
|
||||
if (i % 2 == 0) output += " "; // Extra if it was even.
|
||||
}
|
||||
|
||||
// End declaration
|
||||
output +="};";
|
||||
|
||||
// Comment
|
||||
output += " // " + typeToString(results->decode_type, results->repeat);
|
||||
// Only display the value if the decode type doesn't have an A/C state.
|
||||
if (!hasACState(results->decode_type))
|
||||
output += " " + uint64ToString(results->value, 16);
|
||||
output += "\n";
|
||||
|
||||
// Now dump "known" codes
|
||||
if (results->decode_type != UNKNOWN) {
|
||||
if (hasACState(results->decode_type)) {
|
||||
#if DECODE_AC
|
||||
uint16_t nbytes = results->bits / 8;
|
||||
output += "uint8_t state[" + uint64ToString(nbytes) + "] = {";
|
||||
for (uint16_t i = 0; i < nbytes; i++) {
|
||||
output += "0x";
|
||||
if (results->state[i] < 0x10) output += "0";
|
||||
output += uint64ToString(results->state[i], 16);
|
||||
if (i < nbytes - 1)
|
||||
output += ", ";
|
||||
}
|
||||
output += "};\n";
|
||||
#endif // DECODE_AC
|
||||
} else {
|
||||
// Simple protocols
|
||||
// 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) {
|
||||
output += "uint32_t address = 0x" +
|
||||
uint64ToString(results->address, 16) + ";\n";
|
||||
output += "uint32_t command = 0x" +
|
||||
uint64ToString(results->command, 16) + ";\n";
|
||||
}
|
||||
// Most protocols have data
|
||||
output += "uint64_t data = 0x" + uint64ToString(results->value, 16) +
|
||||
";\n";
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// Dump out the decode_results structure.
|
||||
//
|
||||
#ifdef ARDUINO
|
||||
String resultToTimingInfo(const decode_results *results) {
|
||||
String output = "";
|
||||
String value = "";
|
||||
#else
|
||||
std::string resultToTimingInfo(const decode_results *results) {
|
||||
std::string output = "";
|
||||
std::string value = "";
|
||||
#endif
|
||||
output += "Raw Timing[" + uint64ToString(results->rawlen - 1, 10) + "]:\n";
|
||||
|
||||
for (uint16_t i = 1; i < results->rawlen; i++) {
|
||||
if (i % 2 == 0)
|
||||
output += "-"; // even
|
||||
else
|
||||
output += " +"; // odd
|
||||
value = uint64ToString(results->rawbuf[i] * RAWTICK);
|
||||
// Space pad the value till it is at least 6 chars long.
|
||||
while (value.length() < 6)
|
||||
value = " " + value;
|
||||
output += value;
|
||||
if (i < results->rawlen - 1)
|
||||
output += ", "; // ',' not needed for last one
|
||||
if (!(i % 8)) output += "\n"; // Newline every 8 entries.
|
||||
}
|
||||
output += "\n";
|
||||
return output;
|
||||
}
|
||||
|
||||
// Dump out the decode_results structure.
|
||||
//
|
||||
#ifdef ARDUINO
|
||||
String resultToHumanReadableBasic(const decode_results *results) {
|
||||
String output = "";
|
||||
#else
|
||||
std::string resultToHumanReadableBasic(const decode_results *results) {
|
||||
std::string output = "";
|
||||
#endif
|
||||
// Show Encoding standard
|
||||
output += "Encoding : " +
|
||||
typeToString(results->decode_type, results->repeat) + "\n";
|
||||
|
||||
// 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 += " (" + uint64ToString(results->bits) + " bits)\n";
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
|
||||
uint8_t checksum = init;
|
||||
uint8_t *ptr;
|
||||
for (ptr = start; ptr - start < length; ptr++)
|
||||
checksum += *ptr;
|
||||
return checksum;
|
||||
}
|
||||
|
||||
uint64_t invertBits(const uint64_t data, const uint16_t nbits) {
|
||||
// No change if we are asked to invert no bits.
|
||||
if (nbits == 0) return data;
|
||||
uint64_t result = ~data;
|
||||
// If we are asked to invert all the bits or more than we have, it's simple.
|
||||
if (nbits >= sizeof(data) * 8) return result;
|
||||
// Mask off any unwanted bits and return the result.
|
||||
return (result & ((1ULL << nbits) - 1));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef IRUTILS_H_
|
||||
#define IRUTILS_H_
|
||||
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
|
||||
uint64_t reverseBits(uint64_t input, uint16_t nbits);
|
||||
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
|
||||
String uint64ToString(uint64_t input, uint8_t base = 10);
|
||||
String typeToString(const decode_type_t protocol,
|
||||
const bool isRepeat = false);
|
||||
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);
|
||||
#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);
|
||||
#endif
|
||||
bool hasACState(const decode_type_t protocol);
|
||||
uint16_t getCorrectedRawLength(const decode_results *results);
|
||||
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0);
|
||||
uint64_t invertBits(const uint64_t data, const uint16_t nbits);
|
||||
|
||||
#endif // IRUTILS_H_
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// AAA IIIII W W AAA
|
||||
// A A I W W A A
|
||||
// AAAAA I W W W AAAAA
|
||||
// A A I W W W A A
|
||||
// A A IIIII WWW A A
|
||||
|
||||
// Based off the RC-T501 RCU
|
||||
// Added by David Conran. (Inspired by IRremoteESP8266's implementation:
|
||||
// https://github.com/z3t0/Arduino-IRremote)
|
||||
|
||||
#define AIWA_RC_T501_PRE_BITS 26U
|
||||
#define AIWA_RC_T501_POST_BITS 1U
|
||||
// NOTE: These are the compliment (inverted) of lirc values as
|
||||
// lirc uses a '0' for a mark, and a '1' for a space.
|
||||
#define AIWA_RC_T501_PRE_DATA 0x1D8113FULL // 26-bits
|
||||
#define AIWA_RC_T501_POST_DATA 1ULL
|
||||
|
||||
#if SEND_AIWA_RC_T501
|
||||
// Send an Aiwa RC T501 formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The message to be sent.
|
||||
// nbits: The number of bits of the message to be sent.
|
||||
// Typically AIWA_RC_T501_BITS. Max is 37 = (64 - 27)
|
||||
// repeat: The number of times the command is to be repeated.
|
||||
//
|
||||
// Status: BETA / Should work.
|
||||
//
|
||||
// Ref:
|
||||
// http://lirc.sourceforge.net/remotes/aiwa/RC-T501
|
||||
void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Appears to be an extended NEC1 protocol. i.e. 42 bits instead of 32 bits.
|
||||
// So use sendNEC instead, however the twist is it has a fixed 26 bit
|
||||
// prefix, and a fixed postfix bit.
|
||||
uint64_t new_data = (
|
||||
(AIWA_RC_T501_PRE_DATA << (nbits + AIWA_RC_T501_POST_BITS)) |
|
||||
(data << AIWA_RC_T501_POST_BITS) | AIWA_RC_T501_POST_DATA);
|
||||
nbits += AIWA_RC_T501_PRE_BITS + AIWA_RC_T501_POST_BITS;
|
||||
if (nbits > sizeof(new_data) * 8)
|
||||
return; // We are overflowing. Abort, and don't send.
|
||||
sendNEC(new_data, nbits, repeat);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_AIWA_RC_T501
|
||||
// Decode the supplied Aiwa RC T501 message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically AIWA_RC_T501_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Should work.
|
||||
//
|
||||
// Notes:
|
||||
// Aiwa RC T501 appears to be a 42 bit variant of the NEC1 protocol.
|
||||
// However, we historically (original Arduino IRremote project) treats it as
|
||||
// a 15 bit (data) protocol. So, we expect nbits to typically be 15, and we
|
||||
// will remove the prefix and postfix from the raw data, and use that as
|
||||
// the result.
|
||||
//
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/nec.php
|
||||
bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
// Compliance
|
||||
if (strict && nbits != AIWA_RC_T501_BITS)
|
||||
return false; // Doesn't match our protocol defn.
|
||||
|
||||
// Add on the pre & post bits to our requested bit length.
|
||||
uint16_t expected_nbits = nbits + AIWA_RC_T501_PRE_BITS +
|
||||
AIWA_RC_T501_POST_BITS;
|
||||
uint64_t new_data;
|
||||
if (expected_nbits > sizeof(new_data) * 8)
|
||||
return false; // We can't possibly match something that big.
|
||||
// Decode it as a much bigger (non-standard) NEC message, so we have to turn
|
||||
// off strict mode checking for NEC.
|
||||
if (!decodeNEC(results, expected_nbits, false))
|
||||
return false; // The NEC decode had a problem, so we should too.
|
||||
uint16_t actual_bits = results->bits;
|
||||
new_data = results->value;
|
||||
if (actual_bits < expected_nbits)
|
||||
return false; // The data we caught was undersized. Throw it back.
|
||||
if ((new_data & 0x1ULL) != AIWA_RC_T501_POST_DATA)
|
||||
return false; // The post data doesn't match, so it can't be this protocol.
|
||||
// Trim off the post data bit.
|
||||
new_data >>= AIWA_RC_T501_POST_BITS;
|
||||
actual_bits -= AIWA_RC_T501_POST_BITS;
|
||||
|
||||
// Extract out our likely new value and put it back in the results.
|
||||
actual_bits -= AIWA_RC_T501_PRE_BITS;
|
||||
results->value = new_data & ((1ULL << actual_bits) - 1);
|
||||
|
||||
// Check the prefix data matches.
|
||||
new_data >>= actual_bits; // Trim off the new data to expose the prefix.
|
||||
if (new_data != AIWA_RC_T501_PRE_DATA) // Check the prefix.
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && results->bits != expected_nbits)
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = AIWA_RC_T501;
|
||||
results->bits = actual_bits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote
|
||||
Controls Argo Ulisse 13 DCI A/C
|
||||
Copyright 2017 Schmolders
|
||||
*/
|
||||
|
||||
#include "ir_Argo.h"
|
||||
#include <algorithm>
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
// using SPACE modulation. MARK is always const 400u
|
||||
#define ARGO_HDR_MARK 6400U // Mark
|
||||
#define ARGO_HDR_SPACE 3300U // Space
|
||||
#define ARGO_BIT_MARK 400U
|
||||
#define ARGO_ONE_SPACE 2200U
|
||||
#define ARGO_ZERO_SPACE 900U
|
||||
|
||||
#if SEND_ARGO
|
||||
// Send an Argo A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of ARGO_COMMAND_LENGTH bytes containing the IR command.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
|
||||
void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
|
||||
// Check if we have enough bytes to send a proper message.
|
||||
if (nbytes < ARGO_COMMAND_LENGTH) return;
|
||||
// TODO(kaschmo): validate
|
||||
sendGeneric(ARGO_HDR_MARK, ARGO_HDR_SPACE,
|
||||
ARGO_BIT_MARK, ARGO_ONE_SPACE,
|
||||
ARGO_BIT_MARK, ARGO_ZERO_SPACE,
|
||||
0, 0, // No Footer.
|
||||
data, nbytes, 38, false, repeat, 50);
|
||||
}
|
||||
#endif // SEND_ARGO
|
||||
|
||||
IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRArgoAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_ARGO
|
||||
void IRArgoAC::send() {
|
||||
checksum(); // Create valid checksum before sending
|
||||
_irsend.sendArgo(argo);
|
||||
}
|
||||
#endif // SEND_ARGO
|
||||
|
||||
void IRArgoAC::checksum() {
|
||||
uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01
|
||||
uint8_t i;
|
||||
|
||||
// Only add up bytes to 9. byte 10 is 0b01 constant anyway.
|
||||
// Assume that argo array is MSB first (left)
|
||||
for (i = 0; i < 10; i++)
|
||||
sum += argo[i];
|
||||
|
||||
sum = sum % 256; // modulo 256
|
||||
// Append sum to end of array
|
||||
// Set const part of checksum bit 10
|
||||
argo[10] = 0b00000010;
|
||||
argo[10] += sum << 2; // Shift up 2 bits and append to byte 10
|
||||
argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11
|
||||
}
|
||||
|
||||
void IRArgoAC::stateReset() {
|
||||
for (uint8_t i = 0; i < ARGO_COMMAND_LENGTH; i++)
|
||||
argo[i] = 0x0;
|
||||
|
||||
// Argo Message. Store MSB left.
|
||||
// Default message:
|
||||
argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble
|
||||
argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble
|
||||
// Keep payload 2-9 at zero
|
||||
argo[10] = 0b00000010; // Const 01, checksum 6bit
|
||||
argo[11] = 0b00000000; // Checksum 2bit
|
||||
|
||||
this->off();
|
||||
this->setTemp(20);
|
||||
this->setRoomTemp(25);
|
||||
this->setCoolMode(ARGO_COOL_AUTO);
|
||||
this->setFan(ARGO_FAN_AUTO);
|
||||
}
|
||||
|
||||
uint8_t* IRArgoAC::getRaw() {
|
||||
checksum(); // Ensure correct bit array before returning
|
||||
return argo;
|
||||
}
|
||||
|
||||
void IRArgoAC::on() {
|
||||
// state = ON;
|
||||
ac_state = 1;
|
||||
// Bit 5 of byte 9 is on/off
|
||||
// in MSB first
|
||||
argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1
|
||||
}
|
||||
|
||||
void IRArgoAC::off() {
|
||||
// state = OFF;
|
||||
ac_state = 0;
|
||||
// in MSB first
|
||||
// bit 5 of byte 9 to off
|
||||
argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0
|
||||
}
|
||||
|
||||
void IRArgoAC::setPower(bool state) {
|
||||
if (state)
|
||||
on();
|
||||
else
|
||||
off();
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getPower() {
|
||||
return ac_state;
|
||||
}
|
||||
|
||||
void IRArgoAC::setMax(bool state) {
|
||||
max_mode = state;
|
||||
if (max_mode)
|
||||
argo[9] |= 0b00001000;
|
||||
else
|
||||
argo[9] &= 0b11110111;
|
||||
}
|
||||
|
||||
bool IRArgoAC::getMax() {
|
||||
return max_mode;
|
||||
}
|
||||
|
||||
// Set the temp in deg C
|
||||
// Sending 0 equals +4
|
||||
void IRArgoAC::setTemp(uint8_t temp) {
|
||||
if (temp < ARGO_MIN_TEMP)
|
||||
temp = ARGO_MIN_TEMP;
|
||||
else if (temp > ARGO_MAX_TEMP)
|
||||
temp = ARGO_MAX_TEMP;
|
||||
|
||||
// Store in attributes
|
||||
set_temp = temp;
|
||||
// offset 4 degrees. "If I want 12 degrees, I need to send 8"
|
||||
temp -= 4;
|
||||
// Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3
|
||||
// mask out bits
|
||||
// argo[13] & 0x00000100; // mask out ON/OFF Bit
|
||||
argo[2] &= 0b00111111;
|
||||
argo[3] &= 0b11111000;
|
||||
|
||||
argo[2] += temp << 6; // append to bit 6,7
|
||||
argo[3] += temp >> 2; // remove lowest to bits and append in 0-2
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getTemp() {
|
||||
return set_temp;
|
||||
}
|
||||
|
||||
// Set the speed of the fan
|
||||
void IRArgoAC::setFan(uint8_t fan) {
|
||||
// Set the fan speed bits, leave low 4 bits alone
|
||||
fan_mode = fan;
|
||||
// Mask out bits
|
||||
argo[3] &= 0b11100111;
|
||||
// Set fan mode at bit positions
|
||||
argo[3] += fan << 3;
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getFan() {
|
||||
return fan_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setFlap(uint8_t flap) {
|
||||
flap_mode = flap;
|
||||
// TODO(kaschmo): set correct bits for flap mode
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getFlap() {
|
||||
return flap_mode;
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getMode() {
|
||||
// return cooling 0, heating 1
|
||||
return ac_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setCoolMode(uint8_t mode) {
|
||||
ac_mode = 0; // Set ac mode to cooling
|
||||
cool_mode = mode;
|
||||
// Mask out bits, also leave bit 5 on 0 for cooling
|
||||
argo[2] &= 0b11000111;
|
||||
|
||||
// Set cool mode at bit positions
|
||||
argo[2] += mode << 3;
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getCoolMode() {
|
||||
return cool_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setHeatMode(uint8_t mode) {
|
||||
ac_mode = 1; // Set ac mode to heating
|
||||
heat_mode = mode;
|
||||
// Mask out bits
|
||||
argo[2] &= 0b11000111;
|
||||
// Set heating bit
|
||||
argo[2] |= 0b00100000;
|
||||
// Set cool mode at bit positions
|
||||
argo[2] += mode << 3;
|
||||
}
|
||||
|
||||
uint8_t IRArgoAC::getHeatMode() {
|
||||
return heat_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setNight(bool state) {
|
||||
night_mode = state;
|
||||
if (night_mode)
|
||||
// Set bit at night position: bit 2
|
||||
argo[9] |= 0b00000100;
|
||||
else
|
||||
argo[9] &= 0b11111011;
|
||||
}
|
||||
|
||||
bool IRArgoAC::getNight() {
|
||||
return night_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setiFeel(bool state) {
|
||||
ifeel_mode = state;
|
||||
if (ifeel_mode)
|
||||
// Set bit at iFeel position: bit 7
|
||||
argo[9] |= 0b10000000;
|
||||
else
|
||||
argo[9] &= 0b01111111;
|
||||
}
|
||||
|
||||
bool IRArgoAC::getiFeel() {
|
||||
return ifeel_mode;
|
||||
}
|
||||
|
||||
void IRArgoAC::setTime() {
|
||||
// TODO(kaschmo): use function call from checksum to set time first
|
||||
}
|
||||
|
||||
void IRArgoAC::setRoomTemp(uint8_t temp) {
|
||||
temp -= 4;
|
||||
// Mask out bits
|
||||
argo[3] &= 0b00011111;
|
||||
argo[4] &= 0b11111100;
|
||||
|
||||
argo[3] += temp << 5; // Append to bit 5,6,7
|
||||
argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/* Copyright 2017 Schmolders
|
||||
// Adds support for Argo Ulisse 13 DCI Mobile Split ACs.
|
||||
*/
|
||||
#ifndef IR_ARGO_H_
|
||||
#define IR_ARGO_H_
|
||||
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// ARGO Ulisse DCI
|
||||
|
||||
/*
|
||||
Protocol Description:
|
||||
All in LSB first as it is sent. argo message array will be stored MSB first!
|
||||
do LSB-MSB conversion in sendData
|
||||
Byte 0: const 0 0 1 1 0 1 0 1
|
||||
Byte 1: const 1 0 1 0 1 1 1 1
|
||||
Byte 2: 0 0 0, 3bit Cool/Heat Mode, 2bit start SetTemp LSB first
|
||||
Byte 3: 3bit End SetTemp, 2bit Fan Mode, 3bit RoomTemp LSB first
|
||||
Byte 4: 2bit RoomTemp, 3bit Flap Mode, 3bit OnTimer
|
||||
Byte 5: 8bit OnTimer
|
||||
Byte 6: 8Bit OffTimer
|
||||
Byte 7: 3bit OffTimer, 5bit Time
|
||||
Byte 8: 6bit Time, 1bit Timer On/Off, 1bit Timer Program
|
||||
Byte 9: 1bit Timer Program, 1bit Timer 1h, 1 bit Night Mode, 1bit Max Mode, 1bit Filter, 1bit on/off, 1bit const 0, 1bit iFeel
|
||||
Byte 10: 2bit const 0 1, 6bit Checksum
|
||||
Byte 11: 2bit Checksum
|
||||
*/
|
||||
|
||||
// Constants. Store MSB left.
|
||||
|
||||
#define ARGO_COOL_ON 0U // 0b000
|
||||
#define ARGO_COOL_OFF 3U // 0b110
|
||||
#define ARGO_COOL_AUTO 2U // 0b010
|
||||
#define ARGO_COOl_HUM 1U // 0b100
|
||||
|
||||
#define ARGO_HEAT_ON 0U // 0b001
|
||||
#define ARGO_HEAT_AUTO 1U // 0b101
|
||||
#define ARGO_HEAT_BLINK 2U // 0b011 // ??no idea what mode that is
|
||||
|
||||
|
||||
#define ARGO_MIN_TEMP 10U // Celsius offset +4
|
||||
#define ARGO_MAX_TEMP 32U // Celsius
|
||||
|
||||
#define ARGO_FAN_AUTO 0U // 0b00
|
||||
#define ARGO_FAN_3 3U // 0b11
|
||||
#define ARGO_FAN_2 2U // 0b01
|
||||
#define ARGO_FAN_1 1U // 0b10
|
||||
|
||||
#define ARGO_FLAP_AUTO 0U // 0b000
|
||||
#define ARGO_FLAP_1 1U // 0b100
|
||||
#define ARGO_FLAP_2 2U // 0b010
|
||||
#define ARGO_FLAP_3 3U // 0b110
|
||||
#define ARGO_FLAP_4 4U // 0b001
|
||||
#define ARGO_FLAP_5 5U // 0b101
|
||||
#define ARGO_FLAP_6 6U // 0b011
|
||||
#define ARGO_FLAP_FULL 7U // 0b111
|
||||
|
||||
|
||||
class IRArgoAC {
|
||||
public:
|
||||
explicit IRArgoAC(uint16_t pin);
|
||||
|
||||
#if SEND_ARGO
|
||||
void send();
|
||||
#endif // SEND_ARGO
|
||||
void begin();
|
||||
void on();
|
||||
void off();
|
||||
|
||||
void setPower(bool state);
|
||||
uint8_t getPower();
|
||||
|
||||
void setTemp(uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
|
||||
void setFan(uint8_t fan);
|
||||
uint8_t getFan();
|
||||
|
||||
void setFlap(uint8_t flap);
|
||||
uint8_t getFlap();
|
||||
|
||||
void setCoolMode(uint8_t mode);
|
||||
uint8_t getCoolMode();
|
||||
|
||||
void setHeatMode(uint8_t mode);
|
||||
uint8_t getHeatMode();
|
||||
uint8_t getMode();
|
||||
|
||||
void setMax(bool state);
|
||||
bool getMax();
|
||||
|
||||
void setNight(bool state);
|
||||
bool getNight();
|
||||
|
||||
void setiFeel(bool state);
|
||||
bool getiFeel();
|
||||
|
||||
void setTime();
|
||||
void setRoomTemp(uint8_t temp);
|
||||
|
||||
uint8_t* getRaw();
|
||||
|
||||
private:
|
||||
// # of bytes per command
|
||||
uint8_t argo[ARGO_COMMAND_LENGTH]; // Defined in IRremoteESP8266.h
|
||||
void stateReset();
|
||||
void checksum();
|
||||
IRsend _irsend; // instance of the IR send class
|
||||
|
||||
// Attributes
|
||||
uint8_t set_temp;
|
||||
uint8_t fan_mode;
|
||||
uint8_t flap_mode;
|
||||
uint8_t ac_state;
|
||||
uint8_t ac_mode; // heat 1, cool 0
|
||||
uint8_t heat_mode;
|
||||
uint8_t cool_mode;
|
||||
uint8_t night_mode; // on/off
|
||||
uint8_t max_mode; // on/off
|
||||
uint8_t ifeel_mode; // on/off
|
||||
};
|
||||
|
||||
#endif // IR_ARGO_H_
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2018 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR
|
||||
// CC C AAAAA RR RR RR RR III EE RR RR
|
||||
// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR
|
||||
// CC C AAAAAAA RR RR RR RR III EE RR RR
|
||||
// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR
|
||||
|
||||
// Suits Carrier/Surrey HVAC models:
|
||||
// 42QG5A55970 (remote)
|
||||
// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units)
|
||||
// 53NGK009/012 (inverter)
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/385
|
||||
#define CARRIER_AC_HDR_MARK 8532U
|
||||
#define CARRIER_AC_HDR_SPACE 4228U
|
||||
#define CARRIER_AC_BIT_MARK 628U
|
||||
#define CARRIER_AC_ONE_SPACE 1320U
|
||||
#define CARRIER_AC_ZERO_SPACE 532U
|
||||
#define CARRIER_AC_GAP 20000U
|
||||
|
||||
#if SEND_CARRIER_AC
|
||||
// Send a Carrier HVAC formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The message to be sent.
|
||||
// nbits: The bit size of the message being sent. typically CARRIER_AC_BITS.
|
||||
// repeat: The number of times the message is to be repeated.
|
||||
//
|
||||
// Status: BETA / Appears to work on real devices.
|
||||
//
|
||||
void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
uint64_t temp_data = data;
|
||||
// Carrier sends the data block three times. normal + inverted + normal.
|
||||
for (uint16_t i = 0; i < 3; i++) {
|
||||
sendGeneric(CARRIER_AC_HDR_MARK, CARRIER_AC_HDR_SPACE,
|
||||
CARRIER_AC_BIT_MARK, CARRIER_AC_ONE_SPACE,
|
||||
CARRIER_AC_BIT_MARK, CARRIER_AC_ZERO_SPACE,
|
||||
CARRIER_AC_BIT_MARK, CARRIER_AC_GAP,
|
||||
temp_data, nbits, 38, true, 0, 50);
|
||||
temp_data = invertBits(temp_data, nbits);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_CARRIER_AC
|
||||
// Decode the supplied Carrier HVAC message.
|
||||
// Carrier HVAC messages contain only 32 bits, but it is sent three(3) times.
|
||||
// i.e. normal + inverted + normal
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion.
|
||||
// Typically CARRIER_AC_BITS.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (results->rawlen < ((2 * nbits + HEADER + FOOTER) * 3) - 1)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
if (strict && nbits != CARRIER_AC_BITS)
|
||||
return false; // We expect Carrier to be 32 bits of message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t prev_data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
prev_data = data;
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], CARRIER_AC_HDR_MARK))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], CARRIER_AC_HDR_SPACE))
|
||||
return false;
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
CARRIER_AC_BIT_MARK,
|
||||
CARRIER_AC_ONE_SPACE,
|
||||
CARRIER_AC_BIT_MARK,
|
||||
CARRIER_AC_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], CARRIER_AC_BIT_MARK))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset++], CARRIER_AC_GAP))
|
||||
return false;
|
||||
// Compliance.
|
||||
if (strict) {
|
||||
// Check if the data is an inverted copy of the previous data.
|
||||
if (i > 0 && prev_data != invertBits(data, nbits)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = CARRIER_AC;
|
||||
results->address = data >> 16;
|
||||
results->command = data & 0xFFFF;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright bakrus
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// CCCCC OOOOO OOOOO LL IIIII XX XX
|
||||
// CC C OO OO OO OO LL III XX XX
|
||||
// CC OO OO OO OO LL III XXXX
|
||||
// CC C OO OO OO OO LL III XX XX
|
||||
// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX
|
||||
|
||||
// Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit
|
||||
|
||||
// Constants
|
||||
// Pulse parms are *50-100 for the Mark and *50+100 for the space
|
||||
// First MARK is the one after the long gap
|
||||
// pulse parameters in usec
|
||||
#define COOLIX_TICK 560U // Approximately 21 cycles at 38kHz
|
||||
#define COOLIX_BIT_MARK_TICKS 1U
|
||||
#define COOLIX_BIT_MARK (COOLIX_BIT_MARK_TICKS * COOLIX_TICK)
|
||||
#define COOLIX_ONE_SPACE_TICKS 3U
|
||||
#define COOLIX_ONE_SPACE (COOLIX_ONE_SPACE_TICKS * COOLIX_TICK)
|
||||
#define COOLIX_ZERO_SPACE_TICKS 1U
|
||||
#define COOLIX_ZERO_SPACE (COOLIX_ZERO_SPACE_TICKS * COOLIX_TICK)
|
||||
#define COOLIX_HDR_MARK_TICKS 8U
|
||||
#define COOLIX_HDR_MARK (COOLIX_HDR_MARK_TICKS * COOLIX_TICK)
|
||||
#define COOLIX_HDR_SPACE_TICKS 8U
|
||||
#define COOLIX_HDR_SPACE (COOLIX_HDR_SPACE_TICKS * COOLIX_TICK)
|
||||
#define COOLIX_MIN_GAP_TICKS (COOLIX_HDR_MARK_TICKS + \
|
||||
COOLIX_ZERO_SPACE_TICKS)
|
||||
#define COOLIX_MIN_GAP (COOLIX_MIN_GAP_TICKS * COOLIX_TICK)
|
||||
|
||||
#if SEND_COOLIX
|
||||
// Send a Coolix message
|
||||
//
|
||||
// Args:
|
||||
// data: Contents of the message to be sent.
|
||||
// nbits: Nr. of bits of data to be sent. Typically COOLIX_BITS.
|
||||
// repeat: Nr. of additional times the message is to be sent.
|
||||
//
|
||||
// Status: BETA / Probably works.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp
|
||||
// TODO(anyone): Verify repeat functionality against a real unit.
|
||||
void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits % 8 != 0)
|
||||
return; // nbits is required to be a multiple of 8.
|
||||
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
mark(COOLIX_HDR_MARK);
|
||||
space(COOLIX_HDR_SPACE);
|
||||
|
||||
// Data
|
||||
// Break data into byte segments, starting at the Most Significant
|
||||
// Byte. Each byte then being sent normal, then followed inverted.
|
||||
for (uint16_t i = 8; i <= nbits; i += 8) {
|
||||
// Grab a bytes worth of data.
|
||||
uint8_t segment = (data >> (nbits - i)) & 0xFF;
|
||||
// Normal
|
||||
sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
|
||||
COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
|
||||
segment, 8, true);
|
||||
// Inverted.
|
||||
sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
|
||||
COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
|
||||
segment ^ 0xFF, 8, true);
|
||||
}
|
||||
|
||||
// Footer
|
||||
mark(COOLIX_BIT_MARK);
|
||||
space(COOLIX_MIN_GAP); // Pause before repeating
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_COOLIX
|
||||
// Decode the supplied Coolix message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically COOLIX_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Probably working.
|
||||
bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
// The protocol sends the data normal + inverted, alternating on
|
||||
// each byte. Hence twice the number of expected data bits.
|
||||
if (results->rawlen < 2 * 2 * nbits + HEADER + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid COOLIX message.
|
||||
if (strict && nbits != COOLIX_BITS)
|
||||
return false; // Not strictly a COOLIX message.
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t inverted = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
if (nbits > sizeof(data) * 8)
|
||||
return false; // We can't possibly capture a Coolix packet that big.
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], COOLIX_HDR_MARK)) return false;
|
||||
// Calculate how long the common tick time is based on the header mark.
|
||||
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / COOLIX_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], COOLIX_HDR_SPACE)) return false;
|
||||
// Calculate how long the common tick time is based on the header space.
|
||||
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
COOLIX_HDR_SPACE_TICKS;
|
||||
|
||||
// Data
|
||||
// Twice as many bits as there are normal plus inverted bits.
|
||||
for (uint16_t i = 0; i < nbits * 2; i++, offset++) {
|
||||
bool flip = (i / 8) % 2;
|
||||
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE_TICKS * s_tick)) {
|
||||
if (flip)
|
||||
inverted = (inverted << 1) | 1;
|
||||
else
|
||||
data = (data << 1) | 1;
|
||||
} else if (matchSpace(results->rawbuf[offset],
|
||||
COOLIX_ZERO_SPACE_TICKS * s_tick)) {
|
||||
if (flip)
|
||||
inverted <<= 1;
|
||||
else
|
||||
data <<= 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], COOLIX_MIN_GAP_TICKS * s_tick))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
uint64_t orig = data; // Save a copy of the data.
|
||||
if (strict) {
|
||||
for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8)
|
||||
if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = COOLIX;
|
||||
results->bits = nbits;
|
||||
results->value = orig;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
An Arduino sketch to emulate IR Daikin ARC433** remote control unit
|
||||
Read more at:
|
||||
http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/
|
||||
|
||||
Copyright 2016 sillyfrog
|
||||
Copyright 2017 sillyfrog, crankyoldgit
|
||||
*/
|
||||
|
||||
#include "ir_Daikin.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRutils.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// DDDDD AAA IIIII KK KK IIIII NN NN
|
||||
// DD DD AAAAA III KK KK III NNN NN
|
||||
// DD DD AA AA III KKKK III NN N NN
|
||||
// DD DD AAAAAAA III KK KK III NN NNN
|
||||
// DDDDDD AA AA IIIII KK KK IIIII NN NN
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote
|
||||
// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol
|
||||
|
||||
#if SEND_DAIKIN
|
||||
// Original header
|
||||
// static uint8_t header1[DAIKIN_HEADER1_LENGTH];
|
||||
// header1[0] = 0b00010001;
|
||||
// header1[1] = 0b11011010;
|
||||
// header1[2] = 0b00100111;
|
||||
// header1[3] = 0b00000000;
|
||||
// header1[4] = 0b11000101;
|
||||
// header1[5] = 0b00000000;
|
||||
// header1[6] = 0b00000000;
|
||||
// header1[7] = 0b11010111;
|
||||
|
||||
// Send a Daikin A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of DAIKIN_COMMAND_LENGTH bytes containing the IR command.
|
||||
//
|
||||
// Status: STABLE
|
||||
//
|
||||
// Ref:
|
||||
// IRDaikinESP.cpp
|
||||
// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote
|
||||
void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < DAIKIN_COMMAND_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Send the header, 0b00000
|
||||
sendGeneric(0, 0, // No header for the header
|
||||
DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP,
|
||||
(uint64_t) 0b00000, 5, 38, false, 0, 50);
|
||||
// Leading header
|
||||
// Do this as a constant to save RAM and keep in flash memory
|
||||
sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP,
|
||||
DAIKIN_FIRST_HEADER64, 64, 38, false, 0, 50);
|
||||
// Data #1
|
||||
sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP,
|
||||
data, 8, 38, false, 0, 50);
|
||||
// Data #2
|
||||
sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE,
|
||||
DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP,
|
||||
data + 8, nbytes - 8, 38, false, 0, 50);
|
||||
}
|
||||
}
|
||||
#endif // SEND_DAIKIN
|
||||
|
||||
IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRDaikinESP::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_DAIKIN
|
||||
void IRDaikinESP::send() {
|
||||
checksum();
|
||||
_irsend.sendDaikin(daikin);
|
||||
}
|
||||
#endif // SEND_DAIKIN
|
||||
|
||||
// Calculate the checksum for a given data block.
|
||||
// Args:
|
||||
// block: Ptr to the start of the data block.
|
||||
// length: Nr. of bytes to checksum.
|
||||
// Returns:
|
||||
// A byte containing the calculated checksum.
|
||||
uint8_t IRDaikinESP::calcBlockChecksum(const uint8_t *block,
|
||||
const uint16_t length) {
|
||||
uint8_t sum = 0;
|
||||
// Daikin checksum is just the addition of all the data bytes
|
||||
// in the block but capped to 8 bits.
|
||||
for (uint16_t i = 0; i < length; i++, block++)
|
||||
sum += *block;
|
||||
return sum & 0xFFU;
|
||||
}
|
||||
|
||||
// Verify the checksum is valid for a given state.
|
||||
// Args:
|
||||
// state: The array to verify the checksum of.
|
||||
// length: The size of the state.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRDaikinESP::validChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
if (length < 8 || state[7] != calcBlockChecksum(state, 7)) return false;
|
||||
if (length < 10 ||
|
||||
state[length - 1] != calcBlockChecksum(state + 8, length - 9))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate and set the checksum values for the internal state.
|
||||
void IRDaikinESP::checksum() {
|
||||
daikin[7] = calcBlockChecksum(daikin, 7);
|
||||
daikin[26] = calcBlockChecksum(daikin + 8, 17);
|
||||
}
|
||||
|
||||
void IRDaikinESP::stateReset() {
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++)
|
||||
daikin[i] = 0x0;
|
||||
|
||||
daikin[0] = 0x11;
|
||||
daikin[1] = 0xDA;
|
||||
daikin[2] = 0x27;
|
||||
daikin[4] = 0x42;
|
||||
// daikin[7] is a checksum byte, it will be set by checksum().
|
||||
daikin[8] = 0x11;
|
||||
daikin[9] = 0xDA;
|
||||
daikin[10] = 0x27;
|
||||
daikin[13] = 0x49;
|
||||
daikin[14] = 0x1E;
|
||||
daikin[16] = 0xB0;
|
||||
daikin[19] = 0x06;
|
||||
daikin[20] = 0x60;
|
||||
daikin[23] = 0xC0;
|
||||
// daikin[26] is a checksum byte, it will be set by checksum().
|
||||
checksum();
|
||||
}
|
||||
|
||||
uint8_t* IRDaikinESP::getRaw() {
|
||||
checksum(); // Ensure correct settings before sending.
|
||||
return daikin;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setRaw(uint8_t new_code[]) {
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++)
|
||||
daikin[i] = new_code[i];
|
||||
}
|
||||
|
||||
void IRDaikinESP::on() {
|
||||
// state = ON;
|
||||
setBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER);
|
||||
}
|
||||
|
||||
void IRDaikinESP::off() {
|
||||
// state = OFF;
|
||||
clearBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setPower(bool state) {
|
||||
if (state)
|
||||
on();
|
||||
else
|
||||
off();
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getPower() {
|
||||
return (getBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER) > 0);
|
||||
}
|
||||
|
||||
// Set the temp in deg C
|
||||
void IRDaikinESP::setTemp(uint8_t temp) {
|
||||
if (temp < DAIKIN_MIN_TEMP)
|
||||
temp = DAIKIN_MIN_TEMP;
|
||||
else if (temp > DAIKIN_MAX_TEMP)
|
||||
temp = DAIKIN_MAX_TEMP;
|
||||
daikin[14] = temp * 2;
|
||||
}
|
||||
|
||||
uint8_t IRDaikinESP::getTemp() {
|
||||
return daikin[14] / 2;
|
||||
}
|
||||
|
||||
// Set the speed of the fan, 1-5 or DAIKIN_FAN_AUTO or DAIKIN_FAN_QUIET
|
||||
void IRDaikinESP::setFan(uint8_t fan) {
|
||||
// Set the fan speed bits, leave low 4 bits alone
|
||||
uint8_t fanset;
|
||||
if (fan == DAIKIN_FAN_QUIET || fan == DAIKIN_FAN_AUTO)
|
||||
fanset = fan;
|
||||
else if (fan < DAIKIN_FAN_MIN || fan > DAIKIN_FAN_MAX)
|
||||
fanset = DAIKIN_FAN_AUTO;
|
||||
else
|
||||
fanset = 2 + fan;
|
||||
daikin[16] &= 0x0F;
|
||||
daikin[16] |= (fanset << 4);
|
||||
}
|
||||
|
||||
uint8_t IRDaikinESP::getFan() {
|
||||
uint8_t fan = daikin[16] >> 4;
|
||||
if (fan != DAIKIN_FAN_QUIET && fan != DAIKIN_FAN_AUTO)
|
||||
fan -= 2;
|
||||
return fan;
|
||||
}
|
||||
|
||||
uint8_t IRDaikinESP::getMode() {
|
||||
/*
|
||||
DAIKIN_COOL
|
||||
DAIKIN_HEAT
|
||||
DAIKIN_FAN
|
||||
DAIKIN_AUTO
|
||||
DAIKIN_DRY
|
||||
*/
|
||||
return daikin[13] >> 4;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setMode(uint8_t mode) {
|
||||
switch (mode) {
|
||||
case DAIKIN_COOL:
|
||||
case DAIKIN_HEAT:
|
||||
case DAIKIN_FAN:
|
||||
case DAIKIN_DRY:
|
||||
break;
|
||||
default:
|
||||
mode = DAIKIN_AUTO;
|
||||
}
|
||||
mode <<= 4;
|
||||
daikin[13] &= 0b10001111;
|
||||
daikin[13] |= mode;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setSwingVertical(bool state) {
|
||||
if (state)
|
||||
daikin[16] |= 0x0F;
|
||||
else
|
||||
daikin[16] &= 0xF0;
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getSwingVertical() {
|
||||
return daikin[16] & 0x01;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setSwingHorizontal(bool state) {
|
||||
if (state)
|
||||
daikin[17] |= 0x0F;
|
||||
else
|
||||
daikin[17] &= 0xF0;
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getSwingHorizontal() {
|
||||
return daikin[17] & 0x01;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setQuiet(bool state) {
|
||||
if (state) {
|
||||
setBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT);
|
||||
// Powerful & Quiet mode being on are mutually exclusive.
|
||||
setPowerful(false);
|
||||
} else {
|
||||
clearBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getQuiet() {
|
||||
return (getBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setPowerful(bool state) {
|
||||
if (state) {
|
||||
setBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL);
|
||||
// Powerful, Quiet, & Econo mode being on are mutually exclusive.
|
||||
setQuiet(false);
|
||||
setEcono(false);
|
||||
} else {
|
||||
clearBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getPowerful() {
|
||||
return (getBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setSensor(bool state) {
|
||||
if (state)
|
||||
setBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR);
|
||||
else
|
||||
clearBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR);
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getSensor() {
|
||||
return (getBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setEcono(bool state) {
|
||||
if (state) {
|
||||
setBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO);
|
||||
// Powerful & Econo mode being on are mutually exclusive.
|
||||
setPowerful(false);
|
||||
} else {
|
||||
clearBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getEcono() {
|
||||
return (getBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setEye(bool state) {
|
||||
if (state)
|
||||
setBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE);
|
||||
else
|
||||
clearBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE);
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getEye() {
|
||||
return (getBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setMold(bool state) {
|
||||
if (state)
|
||||
setBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD);
|
||||
else
|
||||
clearBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD);
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getMold() {
|
||||
return (getBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD) > 0);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) {
|
||||
daikin[byte] |= bitmask;
|
||||
}
|
||||
|
||||
void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) {
|
||||
bitmask = ~bitmask;
|
||||
daikin[byte] &= bitmask;
|
||||
}
|
||||
|
||||
uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) {
|
||||
return daikin[byte] & bitmask;
|
||||
}
|
||||
|
||||
// starttime: Number of minutes after midnight, in 10 minutes increments
|
||||
void IRDaikinESP::enableOnTimer(uint16_t starttime) {
|
||||
setBit(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER);
|
||||
daikin[18] = (uint8_t) (starttime & 0x00FF);
|
||||
// only keep 4 bits
|
||||
daikin[19] &= 0xF0;
|
||||
daikin[19] |= (uint8_t) ((starttime >> 8) & 0x0F);
|
||||
}
|
||||
|
||||
void IRDaikinESP::disableOnTimer() {
|
||||
enableOnTimer(0x600);
|
||||
clearBit(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER);
|
||||
}
|
||||
|
||||
uint16_t IRDaikinESP::getOnTime() {
|
||||
uint16_t ret;
|
||||
ret = daikin[19] & 0x0F;
|
||||
ret = ret << 8;
|
||||
ret += daikin[18];
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getOnTimerEnabled() {
|
||||
return getBit(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER);
|
||||
}
|
||||
|
||||
// endtime: Number of minutes after midnight, in 10 minutes increments
|
||||
void IRDaikinESP::enableOffTimer(uint16_t endtime) {
|
||||
setBit(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER);
|
||||
daikin[20] = (uint8_t)((endtime >> 4) & 0xFF);
|
||||
daikin[19] &= 0x0F;
|
||||
daikin[19] |= (uint8_t) ((endtime & 0x000F) << 4);
|
||||
}
|
||||
|
||||
void IRDaikinESP::disableOffTimer() {
|
||||
enableOffTimer(0x600);
|
||||
clearBit(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER);
|
||||
}
|
||||
|
||||
uint16_t IRDaikinESP::getOffTime() {
|
||||
uint16_t ret, tmp;
|
||||
ret = daikin[20];
|
||||
ret <<= 4;
|
||||
tmp = daikin[19] & 0xF0;
|
||||
tmp >>= 4;
|
||||
ret += tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IRDaikinESP::getOffTimerEnabled() {
|
||||
return getBit(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER);
|
||||
}
|
||||
|
||||
void IRDaikinESP::setCurrentTime(uint16_t numMins) {
|
||||
if (numMins > 24 * 60) numMins = 0; // If > 23:59, set to 00:00
|
||||
daikin[5] = (uint8_t) (numMins & 0x00FF);
|
||||
// only keep 4 bits
|
||||
daikin[6] &= 0xF0;
|
||||
daikin[6] |= (uint8_t) ((numMins >> 8) & 0x0F);
|
||||
}
|
||||
|
||||
uint16_t IRDaikinESP::getCurrentTime() {
|
||||
uint16_t ret;
|
||||
ret = daikin[6] & 0x0F;
|
||||
ret <<= 8;
|
||||
ret += daikin[5];
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO
|
||||
String IRDaikinESP::renderTime(uint16_t timemins) {
|
||||
String ret;
|
||||
#else // ARDUINO
|
||||
std::string IRDaikinESP::renderTime(uint16_t timemins) {
|
||||
std::string ret;
|
||||
#endif // ARDUINO
|
||||
uint16_t hours, mins;
|
||||
hours = timemins / 60;
|
||||
ret = uint64ToString(hours) + ":";
|
||||
mins = timemins - (hours * 60);
|
||||
if (mins < 10)
|
||||
ret += "0";
|
||||
ret += uint64ToString(mins);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRDaikinESP::toString() {
|
||||
String result = "";
|
||||
#else // ARDUINO
|
||||
std::string IRDaikinESP::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case DAIKIN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case DAIKIN_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case DAIKIN_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case DAIKIN_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case DAIKIN_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case DAIKIN_FAN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case DAIKIN_FAN_QUIET:
|
||||
result += " (QUIET)";
|
||||
break;
|
||||
case DAIKIN_FAN_MIN:
|
||||
result += " (MIN)";
|
||||
break;
|
||||
case DAIKIN_FAN_MAX:
|
||||
result += " (MAX)";
|
||||
break;
|
||||
}
|
||||
result += ", Powerful: ";
|
||||
if (getPowerful())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Quiet: ";
|
||||
if (getQuiet())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Sensor: ";
|
||||
if (getSensor())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Eye: ";
|
||||
if (getEye())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mold: ";
|
||||
if (getMold())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Swing (Horizontal): ";
|
||||
if (getSwingHorizontal())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Swing (Vertical): ";
|
||||
if (getSwingVertical())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Current Time: " + renderTime(getCurrentTime());
|
||||
result += ", On Time: ";
|
||||
if (getOnTimerEnabled())
|
||||
result += renderTime(getOnTime());
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Off Time: ";
|
||||
if (getOffTimerEnabled())
|
||||
result += renderTime(getOffTime());
|
||||
else
|
||||
result += "Off";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DAIKIN_DEBUG
|
||||
// Print what we have
|
||||
void IRDaikinESP::printState() {
|
||||
#ifdef ARDUINO
|
||||
String strbits;
|
||||
#else // ARDUINO
|
||||
std::string strbits;
|
||||
#endif // ARDUINO
|
||||
DPRINTLN("Raw Bits:");
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++) {
|
||||
strbits = uint64ToString(daikin[i], BIN);
|
||||
while (strbits.length() < 8)
|
||||
strbits = "0" + strbits;
|
||||
DPRINT(strbits);
|
||||
DPRINT(" ");
|
||||
}
|
||||
DPRINTLN("");
|
||||
DPRINTLN(toString());
|
||||
}
|
||||
#endif // DAIKIN_DEBUG
|
||||
|
||||
/*
|
||||
* Return most important bits to allow replay
|
||||
* layout is:
|
||||
* 0: Power
|
||||
* 1-3: Mode
|
||||
* 4-7: Fan speed/mode
|
||||
* 8-14: Target Temperature
|
||||
* 15: Econo
|
||||
* 16: Powerful
|
||||
* 17: Quiet
|
||||
* 18: Sensor
|
||||
* 19: Swing Vertical
|
||||
* 20-31: Current time (mins since midnight)
|
||||
* */
|
||||
uint32_t IRDaikinESP::getCommand() {
|
||||
uint32_t ret = 0;
|
||||
uint32_t tmp = 0;
|
||||
if (getPower())
|
||||
ret |= 0b00000000000000000000000000000001;
|
||||
tmp = getMode();
|
||||
tmp = tmp << 1;
|
||||
ret |= tmp;
|
||||
|
||||
tmp = getFan();
|
||||
tmp <<= 4;
|
||||
ret |= tmp;
|
||||
|
||||
tmp = getTemp();
|
||||
tmp <<= 8;
|
||||
ret |= tmp;
|
||||
|
||||
if (getEcono())
|
||||
ret |= 0b00000000000000001000000000000000;
|
||||
if (getPowerful())
|
||||
ret |= 0b00000000000000010000000000000000;
|
||||
if (getQuiet())
|
||||
ret |= 0b00000000000000100000000000000000;
|
||||
if (getSensor())
|
||||
ret |= 0b00000000000001000000000000000000;
|
||||
if (getSwingVertical())
|
||||
ret |= 0b00000000000010000000000000000000;
|
||||
ret |= (getCurrentTime() << 20);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void IRDaikinESP::setCommand(uint32_t value) {
|
||||
uint32_t tmp = 0;
|
||||
if (value & 0b00000000000000000000000000000001)
|
||||
setPower(true);
|
||||
tmp = value & 0b00000000000000000000000000001110;
|
||||
tmp >>= 1;
|
||||
setMode(tmp);
|
||||
|
||||
tmp = value & 0b00000000000000000000000011110000;
|
||||
tmp >>= 4;
|
||||
setFan(tmp);
|
||||
|
||||
tmp = value & 0b00000000000000000111111100000000;
|
||||
tmp >>= 8;
|
||||
setTemp(tmp);
|
||||
|
||||
if (value & 0b00000000000000001000000000000000)
|
||||
setEcono(true);
|
||||
if (value & 0b00000000000000010000000000000000)
|
||||
setPowerful(true);
|
||||
if (value & 0b00000000000000100000000000000000)
|
||||
setQuiet(true);
|
||||
if (value & 0b00000000000001000000000000000000)
|
||||
setSensor(true);
|
||||
if (value & 0b00000000000010000000000000000000)
|
||||
setSwingVertical(true);
|
||||
|
||||
value >>= 20;
|
||||
setCurrentTime(value);
|
||||
}
|
||||
|
||||
#if DECODE_DAIKIN
|
||||
|
||||
void addbit(bool val, unsigned char data[]) {
|
||||
uint8_t curbit = data[DAIKIN_CURBIT];
|
||||
uint8_t curindex = data[DAIKIN_CURINDEX];
|
||||
if (val) {
|
||||
unsigned char bit = 1;
|
||||
bit = bit << curbit;
|
||||
data[curindex] |= bit;
|
||||
}
|
||||
curbit++;
|
||||
if (curbit == 8) {
|
||||
curbit = 0;
|
||||
curindex++;
|
||||
}
|
||||
data[DAIKIN_CURBIT] = curbit;
|
||||
data[DAIKIN_CURINDEX] = curindex;
|
||||
}
|
||||
|
||||
bool checkheader(decode_results *results, uint16_t* offset) {
|
||||
if (!IRrecv::matchMark(results->rawbuf[(*offset)++], DAIKIN_BIT_MARK,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
return false;
|
||||
if (!IRrecv::matchSpace(results->rawbuf[(*offset)++],
|
||||
DAIKIN_ZERO_SPACE + DAIKIN_GAP,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
return false;
|
||||
if (!IRrecv::matchMark(results->rawbuf[(*offset)++], DAIKIN_HDR_MARK,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
return false;
|
||||
if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], DAIKIN_HDR_SPACE,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readbits(decode_results *results, uint16_t *offset,
|
||||
unsigned char daikin_code[], uint16_t countbits) {
|
||||
for (uint16_t i = 0; i < countbits && *offset < results->rawlen - 1;
|
||||
i++, (*offset)++) {
|
||||
if (!IRrecv::matchMark(results->rawbuf[(*offset)++], DAIKIN_BIT_MARK,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
return false;
|
||||
if (IRrecv::matchSpace(results->rawbuf[*offset], DAIKIN_ONE_SPACE,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
addbit(1, daikin_code);
|
||||
else if (IRrecv::matchSpace(results->rawbuf[*offset], DAIKIN_ZERO_SPACE,
|
||||
DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS))
|
||||
addbit(0, daikin_code);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decode the supplied Daikin A/C message.
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion. (DAIKIN_RAW_BITS)
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Should be working.
|
||||
//
|
||||
// Notes:
|
||||
// If DAIKIN_DEBUG enabled, will print all the set options and values.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote
|
||||
bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (results->rawlen < DAIKIN_RAW_BITS)
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != DAIKIN_RAW_BITS)
|
||||
return false;
|
||||
|
||||
uint16_t offset = OFFSET_START;
|
||||
unsigned char daikin_code[DAIKIN_COMMAND_LENGTH + 2];
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH+2; i++)
|
||||
daikin_code[i] = 0;
|
||||
|
||||
// Header (#1)
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
if (!matchMark(results->rawbuf[offset++], DAIKIN_BIT_MARK))
|
||||
return false;
|
||||
}
|
||||
if (!checkheader(results, &offset)) return false;
|
||||
|
||||
// Data (#1)
|
||||
if (!readbits(results, &offset, daikin_code, 8 * 8)) return false;
|
||||
|
||||
// Ignore everything that has just been captured as it is not needed.
|
||||
// Some remotes may not send this portion, my remote did, but it's not
|
||||
// required.
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH + 2; i++)
|
||||
daikin_code[i] = 0;
|
||||
|
||||
// Header (#2)
|
||||
if (!checkheader(results, &offset)) return false;
|
||||
|
||||
// Data (#2)
|
||||
if (!readbits(results, &offset, daikin_code, 8 * 8)) return false;
|
||||
|
||||
// Header (#3)
|
||||
if (!checkheader(results, &offset)) return false;
|
||||
|
||||
// Data (#3), read up everything else
|
||||
if (!readbits(results, &offset, daikin_code, DAIKIN_BITS - (8 * 8)))
|
||||
return false;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], DAIKIN_BIT_MARK))
|
||||
return false;
|
||||
if (offset < results->rawlen && !matchAtLeast(results->rawbuf[offset],
|
||||
DAIKIN_GAP))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (!IRDaikinESP::validChecksum(daikin_code)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
#if DAIKIN_DEBUG
|
||||
IRDaikinESP dako = IRDaikinESP(0);
|
||||
dako.setRaw(daikin_code);
|
||||
#ifdef ARDUINO
|
||||
yield();
|
||||
#endif // ARDUINO
|
||||
dako.printState();
|
||||
#endif // DAIKIN_DEBUG
|
||||
|
||||
// Copy across the bits to state
|
||||
for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++)
|
||||
results->state[i] = daikin_code[i];
|
||||
results->bits = DAIKIN_COMMAND_LENGTH * 8;
|
||||
results->decode_type = DAIKIN;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_DAIKIN
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright 2016 sillyfrog
|
||||
// Copyright 2017 sillyfrog, crankyoldgit
|
||||
#ifndef IR_DAIKIN_H_
|
||||
#define IR_DAIKIN_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// Option to disable the additional Daikin debug info to conserve memory
|
||||
#define DAIKIN_DEBUG false
|
||||
|
||||
// DDDDD AAA IIIII KK KK IIIII NN NN
|
||||
// DD DD AAAAA III KK KK III NNN NN
|
||||
// DD DD AA AA III KKKK III NN N NN
|
||||
// DD DD AAAAAAA III KK KK III NN NNN
|
||||
// DDDDDD AA AA IIIII KK KK IIIII NN NN
|
||||
|
||||
/*
|
||||
Daikin AC map
|
||||
byte 5=Current time, mins past midnight, low bits
|
||||
byte 6
|
||||
b0-b3=Current time, mins past midnight, high bits
|
||||
byte 7= checksum of the first part (and last byte before a 29ms pause)
|
||||
byte 13=mode
|
||||
b7 = 0
|
||||
b6+b5+b4 = Mode
|
||||
Modes: b6+b5+b4
|
||||
011 = Cool
|
||||
100 = Heat (temp 23)
|
||||
110 = FAN (temp not shown, but 25)
|
||||
000 = Fully Automatic (temp 25)
|
||||
010 = DRY (temp 0xc0 = 96 degrees c)
|
||||
b3 = 1
|
||||
b2 = OFF timer set
|
||||
b1 = ON timer set
|
||||
b0 = Air Conditioner ON
|
||||
byte 14=temp*2 (Temp should be between 10 - 32)
|
||||
byte 16=Fan
|
||||
FAN control
|
||||
b7+b6+b5+b4 = Fan speed
|
||||
Fan: b7+b6+b5+b4
|
||||
0×3 = 1 bar
|
||||
0×4 = 2 bar
|
||||
0×5 = 3 bar
|
||||
0×6 = 4 bar
|
||||
0×7 = 5 bar
|
||||
0xa = Auto
|
||||
0xb = Quite
|
||||
b3+b2+b1+b0 = Swing control up/down
|
||||
Swing control up/down:
|
||||
0000 = Swing up/down off
|
||||
1111 = Swing up/down on
|
||||
byte 17
|
||||
Swing control left/right:
|
||||
0000 = Swing left/right off
|
||||
1111 = Swing left/right on
|
||||
byte 18=On timer mins past midnight, low bits
|
||||
byte 19
|
||||
b0-b3=On timer mins past midnight, high bits
|
||||
b4-b7=Off timer mins past midnight, low bits
|
||||
byte 20=Off timer mins past midnight, high bits
|
||||
byte 21=Aux -> Powerful (bit 1), Silent (bit 5)
|
||||
byte 24=Aux2
|
||||
b1: Sensor
|
||||
b2: Econo mode
|
||||
b7: Intelligent eye on
|
||||
byte 25=Aux3
|
||||
b1: Mold Proof
|
||||
byte 26= checksum of the second part
|
||||
*/
|
||||
|
||||
// Constants
|
||||
#define DAIKIN_COOL 0b011
|
||||
#define DAIKIN_HEAT 0b100
|
||||
#define DAIKIN_FAN 0b110
|
||||
#define DAIKIN_AUTO 0b000
|
||||
#define DAIKIN_DRY 0b010
|
||||
#define DAIKIN_MIN_TEMP 10U // Celsius
|
||||
#define DAIKIN_MAX_TEMP 32U // Celsius
|
||||
#define DAIKIN_FAN_MIN (uint8_t) 1U
|
||||
#define DAIKIN_FAN_MAX (uint8_t) 5U
|
||||
#define DAIKIN_FAN_AUTO (uint8_t) 0b1010
|
||||
#define DAIKIN_FAN_QUIET (uint8_t) 0b1011
|
||||
|
||||
#define DAIKIN_BYTE_POWER 13
|
||||
#define DAIKIN_BIT_POWER 0b00000001
|
||||
|
||||
#define DAIKIN_BYTE_POWERFUL 21
|
||||
#define DAIKIN_BIT_POWERFUL 0b00000001
|
||||
#define DAIKIN_BYTE_SILENT 21
|
||||
#define DAIKIN_BIT_SILENT 0b00100000
|
||||
|
||||
#define DAIKIN_BYTE_SENSOR 24
|
||||
#define DAIKIN_BIT_SENSOR 0b00000010
|
||||
#define DAIKIN_BYTE_ECONO 24
|
||||
#define DAIKIN_BIT_ECONO 0b00000100
|
||||
#define DAIKIN_BYTE_EYE 24
|
||||
#define DAIKIN_BIT_EYE 0b10000000
|
||||
#define DAIKIN_BYTE_MOLD 25
|
||||
#define DAIKIN_BIT_MOLD 0b00000010
|
||||
|
||||
#define DAIKIN_BYTE_OFF_TIMER 13
|
||||
#define DAIKIN_BIT_OFF_TIMER 0b00000100
|
||||
#define DAIKIN_BYTE_ON_TIMER 13
|
||||
#define DAIKIN_BIT_ON_TIMER 0b00000010
|
||||
|
||||
#define DAIKIN_CURBIT DAIKIN_COMMAND_LENGTH
|
||||
#define DAIKIN_CURINDEX (DAIKIN_COMMAND_LENGTH + 1)
|
||||
#define OFFSET_ERR 65432
|
||||
|
||||
#define DAIKIN_TOLERANCE 35
|
||||
#define DAIKIN_MARK_EXCESS MARK_EXCESS
|
||||
|
||||
#define DAIKIN_HDR_MARK 3650U // DAIKIN_BIT_MARK * 8
|
||||
#define DAIKIN_HDR_SPACE 1623U // DAIKIN_BIT_MARK * 4
|
||||
#define DAIKIN_BIT_MARK 428U
|
||||
#define DAIKIN_ZERO_SPACE 428U
|
||||
#define DAIKIN_ONE_SPACE 1280U
|
||||
#define DAIKIN_GAP 29000U
|
||||
|
||||
// Note bits in each octet swapped so can be sent as a single value
|
||||
#define DAIKIN_FIRST_HEADER64 \
|
||||
0b1101011100000000000000001100010100000000001001111101101000010001
|
||||
|
||||
class IRDaikinESP {
|
||||
public:
|
||||
explicit IRDaikinESP(uint16_t pin);
|
||||
|
||||
#if SEND_DAIKIN
|
||||
void send();
|
||||
#endif
|
||||
void begin();
|
||||
void on();
|
||||
void off();
|
||||
void setPower(bool state);
|
||||
bool getPower();
|
||||
void setTemp(uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
void setFan(uint8_t fan);
|
||||
uint8_t getFan();
|
||||
uint8_t getMode();
|
||||
void setMode(uint8_t mode);
|
||||
void setSwingVertical(bool state);
|
||||
bool getSwingVertical();
|
||||
void setSwingHorizontal(bool state);
|
||||
bool getSwingHorizontal();
|
||||
bool getQuiet();
|
||||
void setQuiet(bool state);
|
||||
bool getPowerful();
|
||||
void setPowerful(bool state);
|
||||
void setSensor(bool state);
|
||||
bool getSensor();
|
||||
void setEcono(bool state);
|
||||
bool getEcono();
|
||||
void setEye(bool state);
|
||||
bool getEye();
|
||||
void setMold(bool state);
|
||||
bool getMold();
|
||||
void enableOnTimer(uint16_t starttime);
|
||||
void disableOnTimer();
|
||||
uint16_t getOnTime();
|
||||
bool getOnTimerEnabled();
|
||||
void enableOffTimer(uint16_t endtime);
|
||||
void disableOffTimer();
|
||||
uint16_t getOffTime();
|
||||
bool getOffTimerEnabled();
|
||||
void setCurrentTime(uint16_t time);
|
||||
uint16_t getCurrentTime();
|
||||
uint8_t* getRaw();
|
||||
void setRaw(uint8_t new_code[]);
|
||||
#if DAIKIN_DEBUG
|
||||
void printState();
|
||||
#endif // DAIKIN_DEBUG
|
||||
uint32_t getCommand();
|
||||
void setCommand(uint32_t value);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = DAIKIN_COMMAND_LENGTH);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
static String renderTime(uint16_t timemins);
|
||||
#else
|
||||
std::string toString();
|
||||
static std::string renderTime(uint16_t timemins);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// # of bytes per command
|
||||
uint8_t daikin[DAIKIN_COMMAND_LENGTH];
|
||||
void stateReset();
|
||||
static uint8_t calcBlockChecksum(const uint8_t *block, const uint16_t length);
|
||||
void checksum();
|
||||
void setBit(uint8_t byte, uint8_t bitmask);
|
||||
void clearBit(uint8_t byte, uint8_t bitmask);
|
||||
uint8_t getBit(uint8_t byte, uint8_t bitmask);
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_DAIKIN_H_
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright 2016 Massimiliano Pinto
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// DDDD EEEEE N N OOO N N
|
||||
// D D E NN N O O NN N
|
||||
// D D EEE N N N O O N N N
|
||||
// D D E N NN O O N NN
|
||||
// DDDD EEEEE N N OOO N N
|
||||
|
||||
// Original Denon support added by https://github.com/csBlueChip
|
||||
// Ported over by Massimiliano Pinto
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
|
||||
#define DENON_TICK 263U
|
||||
#define DENON_HDR_MARK_TICKS 1U
|
||||
#define DENON_HDR_MARK (DENON_HDR_MARK_TICKS * DENON_TICK)
|
||||
#define DENON_HDR_SPACE_TICKS 3U
|
||||
#define DENON_HDR_SPACE (DENON_HDR_SPACE_TICKS * DENON_TICK)
|
||||
#define DENON_BIT_MARK_TICKS 1U
|
||||
#define DENON_BIT_MARK (DENON_BIT_MARK_TICKS * DENON_TICK)
|
||||
#define DENON_ONE_SPACE_TICKS 7U
|
||||
#define DENON_ONE_SPACE (DENON_ONE_SPACE_TICKS * DENON_TICK)
|
||||
#define DENON_ZERO_SPACE_TICKS 3U
|
||||
#define DENON_ZERO_SPACE (DENON_ZERO_SPACE_TICKS * DENON_TICK)
|
||||
#define DENON_MIN_COMMAND_LENGTH_TICKS 510U
|
||||
#define DENON_MIN_COMMAND_LENGTH (DENON_MIN_COMMAND_LENGTH_TICKS * DENON_TICK)
|
||||
#define DENON_MIN_GAP_TICKS (DENON_MIN_COMMAND_LENGTH_TICKS - \
|
||||
(DENON_HDR_MARK_TICKS + DENON_HDR_SPACE_TICKS + \
|
||||
DENON_BITS * (DENON_BIT_MARK_TICKS + DENON_ONE_SPACE_TICKS) + \
|
||||
DENON_BIT_MARK_TICKS))
|
||||
#define DENON_MIN_GAP (DENON_MIN_GAP_TICKS * DENON_TICK)
|
||||
#define DENON_MANUFACTURER 0x2A4CULL
|
||||
|
||||
#if SEND_DENON
|
||||
// Send a Denon message
|
||||
//
|
||||
// Args:
|
||||
// data: Contents of the message to be sent.
|
||||
// nbits: Nr. of bits of data to be sent. Typically DENON_BITS.
|
||||
// repeat: Nr. of additional times the message is to be sent.
|
||||
//
|
||||
// Status: BETA / Should be working.
|
||||
//
|
||||
// Notes:
|
||||
// Some Denon devices use a Kaseikyo/Panasonic 48-bit format
|
||||
// Others use the Sharp protocol.
|
||||
// Ref:
|
||||
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
|
||||
// http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
|
||||
void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits >= PANASONIC_BITS) // Is this really Panasonic?
|
||||
sendPanasonic64(data, nbits, repeat);
|
||||
else if (nbits == DENON_LEGACY_BITS)
|
||||
// Support legacy (broken) calls of sendDenon().
|
||||
sendSharpRaw(data & (~0x2000ULL), nbits + 1, repeat);
|
||||
else
|
||||
sendSharpRaw(data, nbits, repeat);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_DENON
|
||||
// Decode a Denon message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Expected nr. of data bits. (Typically DENON_BITS)
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Should work fine.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
|
||||
bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
|
||||
// Compliance
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case DENON_BITS:
|
||||
case DENON_48_BITS:
|
||||
case DENON_LEGACY_BITS:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Denon uses the Sharp & Panasonic(Kaseikyo) protocol for some
|
||||
// devices, so check for those first.
|
||||
// It is not exactly like Sharp's protocols, but close enough.
|
||||
// e.g. The expansion bit is not set for Denon vs. set for Sharp.
|
||||
// Ditto for Panasonic, it's the same except for a different
|
||||
// manufacturer code.
|
||||
|
||||
if (!decodeSharp(results, nbits, true, false) &&
|
||||
!decodePanasonic(results, nbits, true, DENON_MANUFACTURER)) {
|
||||
// We couldn't decode it as expected, so try the old legacy method.
|
||||
// NOTE: I don't think this following protocol actually exists.
|
||||
// Looks like a partial version of the Sharp protocol.
|
||||
// Check we have enough data
|
||||
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
|
||||
return false;
|
||||
if (strict && nbits != DENON_LEGACY_BITS)
|
||||
return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], DENON_HDR_MARK)) return false;
|
||||
// Calculate how long the common tick time is based on the header mark.
|
||||
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
DENON_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], DENON_HDR_SPACE)) return false;
|
||||
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
DENON_HDR_SPACE_TICKS;
|
||||
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
DENON_BIT_MARK_TICKS * m_tick,
|
||||
DENON_ONE_SPACE_TICKS * s_tick,
|
||||
DENON_BIT_MARK_TICKS * m_tick,
|
||||
DENON_ZERO_SPACE_TICKS * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], DENON_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
} // Legacy decode.
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != results->bits) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = DENON;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright Todd Treece
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// DDDD IIIII SSSS H H
|
||||
// D D I S H H
|
||||
// D D I SSS HHHHH
|
||||
// D D I S H H
|
||||
// DDDD IIIII SSSS H H
|
||||
|
||||
// DISH support originally by Todd Treece
|
||||
// http://unionbridge.org/design/ircommand
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
|
||||
// http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
#define DISH_TICK 100U
|
||||
#define DISH_HDR_MARK_TICKS 4U
|
||||
#define DISH_HDR_MARK (DISH_HDR_MARK_TICKS * DISH_TICK)
|
||||
#define DISH_HDR_SPACE_TICKS 61U
|
||||
#define DISH_HDR_SPACE (DISH_HDR_SPACE_TICKS * DISH_TICK)
|
||||
#define DISH_BIT_MARK_TICKS 4U
|
||||
#define DISH_BIT_MARK (DISH_BIT_MARK_TICKS * DISH_TICK)
|
||||
#define DISH_ONE_SPACE_TICKS 17U
|
||||
#define DISH_ONE_SPACE (DISH_ONE_SPACE_TICKS * DISH_TICK)
|
||||
#define DISH_ZERO_SPACE_TICKS 28U
|
||||
#define DISH_ZERO_SPACE (DISH_ZERO_SPACE_TICKS * DISH_TICK)
|
||||
#define DISH_RPT_SPACE_TICKS DISH_HDR_SPACE_TICKS
|
||||
#define DISH_RPT_SPACE (DISH_RPT_SPACE_TICKS * DISH_TICK)
|
||||
|
||||
#if SEND_DISH
|
||||
// Send an IR command to a DISH NETWORK device.
|
||||
//
|
||||
// Args:
|
||||
// data: The contents of the command you want to send.
|
||||
// nbits: The bit size of the command being sent.
|
||||
// repeat: The number of times you want the command to be repeated.
|
||||
//
|
||||
// Status: BETA / Previously working.
|
||||
//
|
||||
// Note:
|
||||
// Dishplayer is a different protocol.
|
||||
// Typically a DISH device needs to get a command a total of at least 4
|
||||
// times to accept it. e.g. repeat=3
|
||||
//
|
||||
// Here is the LIRC file I found that seems to match the remote codes from the
|
||||
// oscilloscope:
|
||||
// DISH NETWORK (echostar 301):
|
||||
// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
|
||||
//
|
||||
// Ref:
|
||||
// http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
enableIROut(57600); // Set modulation freq. to 57.6kHz.
|
||||
// Header is only ever sent once.
|
||||
mark(DISH_HDR_MARK);
|
||||
space(DISH_HDR_SPACE);
|
||||
|
||||
sendGeneric(0, 0, // No headers from here on in.
|
||||
DISH_BIT_MARK, DISH_ONE_SPACE,
|
||||
DISH_BIT_MARK, DISH_ZERO_SPACE,
|
||||
DISH_BIT_MARK, DISH_RPT_SPACE,
|
||||
data, nbits, 57600, true, repeat, 50);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_DISH
|
||||
// Decode the supplied DISH NETWORK message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion. Typically DISH_BITS.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA (untested and unconfirmed.)
|
||||
//
|
||||
// Note:
|
||||
// Dishplayer is a different protocol.
|
||||
// Typically a DISH device needs to get a command a total of at least 4
|
||||
// times to accept it.
|
||||
// Ref:
|
||||
// http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
|
||||
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
|
||||
bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) {
|
||||
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
|
||||
return false; // Not enough entries to be valid.
|
||||
if (strict && nbits != DISH_BITS)
|
||||
return false; // Not strictly compliant.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// Header
|
||||
if (!match(results->rawbuf[offset], DISH_HDR_MARK)) return false;
|
||||
// Calculate how long the common tick time is based on the header mark.
|
||||
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / DISH_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], DISH_HDR_SPACE)) return false;
|
||||
// Calculate how long the common tick time is based on the header space.
|
||||
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / DISH_HDR_SPACE_TICKS;
|
||||
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
DISH_BIT_MARK_TICKS * m_tick,
|
||||
DISH_ONE_SPACE_TICKS * s_tick,
|
||||
DISH_BIT_MARK_TICKS * m_tick,
|
||||
DISH_ZERO_SPACE_TICKS * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], DISH_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// The DISH protocol calls for a repeated message, so strictly speaking
|
||||
// there should be a code following this. Only require it if we are set to
|
||||
// strict matching.
|
||||
if (!matchSpace(results->rawbuf[offset], DISH_RPT_SPACE_TICKS * s_tick))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = DISH;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,547 @@
|
||||
// Copyright 2017 Jonny Graham, David Conran
|
||||
#include "ir_Fujitsu.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Fujitsu A/C support added by Jonny Graham & David Conran
|
||||
|
||||
// Equipment it seems compatible with:
|
||||
// * Fujitsu ASYG30LFCA with remote AR-RAH2E
|
||||
// * Fujitsu AST9RSGCW with remote AR-DB1
|
||||
// * <Add models (A/C & remotes) you've gotten it working with here>
|
||||
|
||||
// Ref:
|
||||
// These values are based on averages of measurements
|
||||
#define FUJITSU_AC_HDR_MARK 3324U
|
||||
#define FUJITSU_AC_HDR_SPACE 1574U
|
||||
#define FUJITSU_AC_BIT_MARK 448U
|
||||
#define FUJITSU_AC_ONE_SPACE 1182U
|
||||
#define FUJITSU_AC_ZERO_SPACE 390U
|
||||
#define FUJITSU_AC_MIN_GAP 8100U
|
||||
|
||||
#if SEND_FUJITSU_AC
|
||||
// Send a Fujitsu A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. Typically one of:
|
||||
// FUJITSU_AC_STATE_LENGTH
|
||||
// FUJITSU_AC_STATE_LENGTH - 1
|
||||
// FUJITSU_AC_STATE_LENGTH_SHORT
|
||||
// FUJITSU_AC_STATE_LENGTH_SHORT - 1
|
||||
// repeat: Nr. of times the message is to be repeated.
|
||||
// (Default = FUJITSU_AC_MIN_REPEAT).
|
||||
//
|
||||
// Status: BETA / Appears to be working.
|
||||
//
|
||||
void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
sendGeneric(FUJITSU_AC_HDR_MARK, FUJITSU_AC_HDR_SPACE,
|
||||
FUJITSU_AC_BIT_MARK, FUJITSU_AC_ONE_SPACE,
|
||||
FUJITSU_AC_BIT_MARK, FUJITSU_AC_ZERO_SPACE,
|
||||
FUJITSU_AC_BIT_MARK, FUJITSU_AC_MIN_GAP,
|
||||
data, nbytes, 38, false, repeat, 50);
|
||||
}
|
||||
#endif // SEND_FUJITSU_AC
|
||||
|
||||
// Code to emulate Fujitsu A/C IR remote control unit.
|
||||
|
||||
// Initialise the object.
|
||||
IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model)
|
||||
: _irsend(pin) {
|
||||
setModel(model);
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) {
|
||||
_model = model;
|
||||
switch (model) {
|
||||
case ARDB1:
|
||||
_state_length = FUJITSU_AC_STATE_LENGTH - 1;
|
||||
_state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT - 1;
|
||||
break;
|
||||
default:
|
||||
_state_length = FUJITSU_AC_STATE_LENGTH;
|
||||
_state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the state of the remote to a known good state/sequence.
|
||||
void IRFujitsuAC::stateReset() {
|
||||
_temp = 24;
|
||||
_fanSpeed = FUJITSU_AC_FAN_HIGH;
|
||||
_mode = FUJITSU_AC_MODE_COOL;
|
||||
_swingMode = FUJITSU_AC_SWING_BOTH;
|
||||
_cmd = FUJITSU_AC_CMD_TURN_ON;
|
||||
buildState();
|
||||
}
|
||||
|
||||
// Configure the pin for output.
|
||||
void IRFujitsuAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_FUJITSU_AC
|
||||
// Send the current desired state to the IR LED.
|
||||
void IRFujitsuAC::send() {
|
||||
getRaw();
|
||||
_irsend.sendFujitsuAC(remote_state, getStateLength());
|
||||
}
|
||||
#endif // SEND_FUJITSU_AC
|
||||
|
||||
void IRFujitsuAC::buildState() {
|
||||
remote_state[0] = 0x14;
|
||||
remote_state[1] = 0x63;
|
||||
remote_state[2] = 0x00;
|
||||
remote_state[3] = 0x10;
|
||||
remote_state[4] = 0x10;
|
||||
bool fullCmd = false;
|
||||
switch (_cmd) {
|
||||
case FUJITSU_AC_CMD_TURN_OFF:
|
||||
remote_state[5] = 0x02;
|
||||
break;
|
||||
case FUJITSU_AC_CMD_STEP_HORIZ:
|
||||
remote_state[5] = 0x79;
|
||||
break;
|
||||
case FUJITSU_AC_CMD_STEP_VERT:
|
||||
remote_state[5] = 0x6C;
|
||||
break;
|
||||
default:
|
||||
switch (_model) {
|
||||
case ARRAH2E:
|
||||
remote_state[5] = 0xFE;
|
||||
break;
|
||||
case ARDB1:
|
||||
remote_state[5] = 0xFC;
|
||||
break;
|
||||
}
|
||||
fullCmd = true;
|
||||
break;
|
||||
}
|
||||
if (fullCmd) { // long codes
|
||||
uint8_t tempByte = _temp - FUJITSU_AC_MIN_TEMP;
|
||||
// Nr. of bytes in the message after this byte.
|
||||
remote_state[6] = _state_length - 7;
|
||||
|
||||
remote_state[7] = 0x30;
|
||||
remote_state[8] = (_cmd == FUJITSU_AC_CMD_TURN_ON) | (tempByte << 4);
|
||||
remote_state[9] = _mode | 0 << 4; // timer off
|
||||
remote_state[10] = _fanSpeed | _swingMode << 4;
|
||||
remote_state[11] = 0; // timerOff values
|
||||
remote_state[12] = 0; // timerOff/On values
|
||||
remote_state[13] = 0; // timerOn values
|
||||
if (_model == ARRAH2E)
|
||||
remote_state[14] = 0x20;
|
||||
else
|
||||
remote_state[14] = 0x00;
|
||||
|
||||
uint8_t checksum = 0;
|
||||
uint8_t checksum_complement = 0;
|
||||
if (_model == ARRAH2E) {
|
||||
checksum = sumBytes(remote_state + _state_length_short,
|
||||
_state_length - _state_length_short - 1);
|
||||
} else if (_model == ARDB1) {
|
||||
checksum = sumBytes(remote_state, _state_length - 1);
|
||||
checksum_complement = 0x9B;
|
||||
}
|
||||
// and negate the checksum and store it in the last byte.
|
||||
remote_state[_state_length - 1] = checksum_complement - checksum;
|
||||
} else { // short codes
|
||||
if (_model == ARRAH2E)
|
||||
// The last byte is the inverse of penultimate byte
|
||||
remote_state[_state_length_short - 1] = ~remote_state[_state_length_short
|
||||
- 2];
|
||||
// Zero the rest of the state.
|
||||
for (uint8_t i = _state_length_short;
|
||||
i < FUJITSU_AC_STATE_LENGTH;
|
||||
i++)
|
||||
remote_state[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRFujitsuAC::getStateLength() {
|
||||
buildState(); // Force an update of the internal state.
|
||||
if ((_model == ARRAH2E && remote_state[5] != 0xFE) ||
|
||||
(_model == ARDB1 && remote_state[5] != 0xFC))
|
||||
return _state_length_short;
|
||||
else
|
||||
return _state_length;
|
||||
}
|
||||
|
||||
// Return a pointer to the internal state date of the remote.
|
||||
uint8_t* IRFujitsuAC::getRaw() {
|
||||
buildState();
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
void IRFujitsuAC::buildFromState(const uint16_t length) {
|
||||
switch (length) {
|
||||
case FUJITSU_AC_STATE_LENGTH - 1:
|
||||
case FUJITSU_AC_STATE_LENGTH_SHORT - 1:
|
||||
setModel(ARDB1);
|
||||
break;
|
||||
default:
|
||||
setModel(ARRAH2E);
|
||||
}
|
||||
switch (remote_state[6]) {
|
||||
case 8:
|
||||
setModel(ARDB1);
|
||||
break;
|
||||
case 9:
|
||||
setModel(ARRAH2E);
|
||||
break;
|
||||
}
|
||||
setTemp((remote_state[8] >> 4) + FUJITSU_AC_MIN_TEMP);
|
||||
if (remote_state[8] & 0x1)
|
||||
setCmd(FUJITSU_AC_CMD_TURN_ON);
|
||||
else
|
||||
setCmd(FUJITSU_AC_CMD_STAY_ON);
|
||||
setMode(remote_state[9] & 0b111);
|
||||
setFanSpeed(remote_state[10] & 0b111);
|
||||
setSwing(remote_state[10] >> 4);
|
||||
switch (remote_state[5]) {
|
||||
case FUJITSU_AC_CMD_TURN_OFF:
|
||||
case FUJITSU_AC_CMD_STEP_HORIZ:
|
||||
case FUJITSU_AC_CMD_STEP_VERT:
|
||||
setCmd(remote_state[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
|
||||
if (length > FUJITSU_AC_STATE_LENGTH) return false;
|
||||
for (uint16_t i = 0; i < FUJITSU_AC_STATE_LENGTH; i++) {
|
||||
if (i < length)
|
||||
remote_state[i] = newState[i];
|
||||
else
|
||||
remote_state[i] = 0;
|
||||
}
|
||||
buildFromState(length);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C to off.
|
||||
void IRFujitsuAC::off() {
|
||||
_cmd = FUJITSU_AC_CMD_TURN_OFF;
|
||||
}
|
||||
|
||||
void IRFujitsuAC::stepHoriz() {
|
||||
switch (_model) {
|
||||
case ARDB1: break; // This remote doesn't have a horizontal option.
|
||||
default:
|
||||
_cmd = FUJITSU_AC_CMD_STEP_HORIZ;
|
||||
}
|
||||
}
|
||||
|
||||
void IRFujitsuAC::stepVert() {
|
||||
_cmd = FUJITSU_AC_CMD_STEP_VERT;
|
||||
}
|
||||
|
||||
// Set the requested command of the A/C.
|
||||
void IRFujitsuAC::setCmd(uint8_t cmd) {
|
||||
switch (cmd) {
|
||||
case FUJITSU_AC_CMD_TURN_OFF:
|
||||
case FUJITSU_AC_CMD_TURN_ON:
|
||||
case FUJITSU_AC_CMD_STAY_ON:
|
||||
case FUJITSU_AC_CMD_STEP_VERT:
|
||||
_cmd = cmd;
|
||||
break;
|
||||
case FUJITSU_AC_CMD_STEP_HORIZ:
|
||||
if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal.
|
||||
_cmd = cmd;
|
||||
default:
|
||||
_cmd = FUJITSU_AC_CMD_STAY_ON;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRFujitsuAC::getCmd() {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
bool IRFujitsuAC::getPower() {
|
||||
return _cmd != FUJITSU_AC_CMD_TURN_OFF;
|
||||
}
|
||||
|
||||
// Set the temp. in deg C
|
||||
void IRFujitsuAC::setTemp(uint8_t temp) {
|
||||
temp = std::max((uint8_t) FUJITSU_AC_MIN_TEMP, temp);
|
||||
temp = std::min((uint8_t) FUJITSU_AC_MAX_TEMP, temp);
|
||||
_temp = temp;
|
||||
}
|
||||
|
||||
uint8_t IRFujitsuAC::getTemp() {
|
||||
return _temp;
|
||||
}
|
||||
|
||||
// Set the speed of the fan
|
||||
void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) {
|
||||
if (fanSpeed > FUJITSU_AC_FAN_QUIET)
|
||||
fanSpeed = FUJITSU_AC_FAN_HIGH; // Set the fan to maximum if out of range.
|
||||
_fanSpeed = fanSpeed;
|
||||
}
|
||||
uint8_t IRFujitsuAC::getFanSpeed() {
|
||||
return _fanSpeed;
|
||||
}
|
||||
|
||||
// Set the requested climate operation mode of the a/c unit.
|
||||
void IRFujitsuAC::setMode(uint8_t mode) {
|
||||
if (mode > FUJITSU_AC_MODE_HEAT)
|
||||
mode = FUJITSU_AC_MODE_HEAT; // Set the mode to maximum if out of range.
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
uint8_t IRFujitsuAC::getMode() {
|
||||
return _mode;
|
||||
}
|
||||
// Set the requested swing operation mode of the a/c unit.
|
||||
void IRFujitsuAC::setSwing(uint8_t swingMode) {
|
||||
switch (_model) {
|
||||
case ARDB1:
|
||||
// Set the mode to max if out of range
|
||||
if (swingMode > FUJITSU_AC_SWING_VERT)
|
||||
swingMode = FUJITSU_AC_SWING_VERT;
|
||||
break;
|
||||
case ARRAH2E:
|
||||
default:
|
||||
// Set the mode to max if out of range
|
||||
if (swingMode > FUJITSU_AC_SWING_BOTH)
|
||||
swingMode = FUJITSU_AC_SWING_BOTH;
|
||||
}
|
||||
_swingMode = swingMode;
|
||||
}
|
||||
|
||||
uint8_t IRFujitsuAC::getSwing() {
|
||||
return _swingMode;
|
||||
}
|
||||
|
||||
bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) {
|
||||
uint8_t sum = 0;
|
||||
uint8_t sum_complement = 0;
|
||||
uint8_t checksum = 0;
|
||||
switch (length) {
|
||||
case FUJITSU_AC_STATE_LENGTH_SHORT: // ARRAH2E
|
||||
return state[length - 1] == (uint8_t) ~state[length - 2];
|
||||
case FUJITSU_AC_STATE_LENGTH - 1: // ARDB1
|
||||
sum = sumBytes(state, length - 1);
|
||||
sum_complement = 0x9B;
|
||||
checksum = state[length - 1];
|
||||
break;
|
||||
case FUJITSU_AC_STATE_LENGTH: // ARRAH2E
|
||||
sum = sumBytes(state + FUJITSU_AC_STATE_LENGTH_SHORT,
|
||||
length - 1 - FUJITSU_AC_STATE_LENGTH_SHORT);
|
||||
default: // Includes ARDB1 short.
|
||||
return true; // Assume the checksum is valid for other lengths.
|
||||
}
|
||||
return checksum == (uint8_t) (sum_complement - sum); // Does it match?
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRFujitsuAC::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRFujitsuAC::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case FUJITSU_AC_MODE_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case FUJITSU_AC_MODE_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case FUJITSU_AC_MODE_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case FUJITSU_AC_MODE_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case FUJITSU_AC_MODE_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFanSpeed());
|
||||
switch (getFanSpeed()) {
|
||||
case FUJITSU_AC_FAN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case FUJITSU_AC_FAN_HIGH:
|
||||
result += " (HIGH)";
|
||||
break;
|
||||
case FUJITSU_AC_FAN_MED:
|
||||
result += " (MED)";
|
||||
break;
|
||||
case FUJITSU_AC_FAN_LOW:
|
||||
result += " (LOW)";
|
||||
break;
|
||||
case FUJITSU_AC_FAN_QUIET:
|
||||
result += " (QUIET)";
|
||||
break;
|
||||
}
|
||||
result += ", Swing: ";
|
||||
switch (getSwing()) {
|
||||
case FUJITSU_AC_SWING_OFF:
|
||||
result += "Off";
|
||||
break;
|
||||
case FUJITSU_AC_SWING_VERT:
|
||||
result += "Vert";
|
||||
break;
|
||||
case FUJITSU_AC_SWING_HORIZ:
|
||||
result += "Horiz";
|
||||
break;
|
||||
case FUJITSU_AC_SWING_BOTH:
|
||||
result += "Vert + Horiz";
|
||||
break;
|
||||
default:
|
||||
result += "UNKNOWN";
|
||||
}
|
||||
result += ", Command: ";
|
||||
switch (getCmd()) {
|
||||
case FUJITSU_AC_CMD_STEP_HORIZ:
|
||||
result += "Step vane horizontally";
|
||||
break;
|
||||
case FUJITSU_AC_CMD_STEP_VERT:
|
||||
result += "Step vane vertically";
|
||||
break;
|
||||
default:
|
||||
result += "N/A";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_FUJITSU_AC
|
||||
// Decode a Fujitsu AC IR message if possible.
|
||||
// Places successful decode information in the results pointer.
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically FUJITSU_AC_BITS.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
// Ref:
|
||||
//
|
||||
bool IRrecv::decodeFujitsuAC(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
uint16_t offset = OFFSET_START;
|
||||
uint16_t dataBitsSoFar = 0;
|
||||
|
||||
// Have we got enough data to successfully decode?
|
||||
if (results->rawlen < (2 * FUJITSU_AC_MIN_BITS) + HEADER + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid message.
|
||||
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case FUJITSU_AC_BITS:
|
||||
case FUJITSU_AC_BITS - 8:
|
||||
case FUJITSU_AC_MIN_BITS:
|
||||
case FUJITSU_AC_MIN_BITS + 8:
|
||||
break;
|
||||
default:
|
||||
return false; // Must be called with the correct nr. of bits.
|
||||
}
|
||||
}
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], FUJITSU_AC_HDR_MARK))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], FUJITSU_AC_HDR_SPACE))
|
||||
return false;
|
||||
|
||||
// Data (Fixed signature)
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]),
|
||||
FUJITSU_AC_MIN_BITS - 8,
|
||||
FUJITSU_AC_BIT_MARK,
|
||||
FUJITSU_AC_ONE_SPACE,
|
||||
FUJITSU_AC_BIT_MARK,
|
||||
FUJITSU_AC_ZERO_SPACE);
|
||||
if (data_result.success == false) return false; // Fail
|
||||
if (reverseBits(data_result.data, FUJITSU_AC_MIN_BITS - 8) != 0x1010006314)
|
||||
return false; // Signature failed.
|
||||
dataBitsSoFar += FUJITSU_AC_MIN_BITS - 8;
|
||||
offset += data_result.used;
|
||||
results->state[0] = 0x14;
|
||||
results->state[1] = 0x63;
|
||||
results->state[2] = 0x00;
|
||||
results->state[3] = 0x10;
|
||||
results->state[4] = 0x10;
|
||||
|
||||
// Keep reading bytes until we either run out of message or state to fill.
|
||||
for (uint16_t i = 5;
|
||||
offset <= results->rawlen - 16 && i < FUJITSU_AC_STATE_LENGTH;
|
||||
i++, dataBitsSoFar += 8, offset += data_result.used) {
|
||||
data_result = matchData(&(results->rawbuf[offset]), 8,
|
||||
FUJITSU_AC_BIT_MARK,
|
||||
FUJITSU_AC_ONE_SPACE,
|
||||
FUJITSU_AC_BIT_MARK,
|
||||
FUJITSU_AC_ZERO_SPACE);
|
||||
if (data_result.success == false) break; // Fail
|
||||
results->state[i] = (uint8_t) reverseBits(data_result.data, 8);
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (offset > results->rawlen ||
|
||||
!matchMark(results->rawbuf[offset++], FUJITSU_AC_BIT_MARK)) return false;
|
||||
// The space is optional if we are out of capture.
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], FUJITSU_AC_MIN_GAP)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (dataBitsSoFar != nbits) return false;
|
||||
}
|
||||
|
||||
results->decode_type = FUJITSU_AC;
|
||||
results->bits = dataBitsSoFar;
|
||||
|
||||
// Compliance
|
||||
switch (dataBitsSoFar) {
|
||||
case FUJITSU_AC_MIN_BITS:
|
||||
// Check if this values indicate that this should have been a long state
|
||||
// message.
|
||||
if (results->state[5] == 0xFC) return false;
|
||||
return true; // Success
|
||||
case FUJITSU_AC_MIN_BITS + 8:
|
||||
// Check if this values indicate that this should have been a long state
|
||||
// message.
|
||||
if (results->state[5] == 0xFE) return false;
|
||||
// The last byte needs to be the inverse of the penultimate byte.
|
||||
if (results->state[5] != (uint8_t) ~results->state[6]) return false;
|
||||
return true; // Success
|
||||
case FUJITSU_AC_BITS - 8:
|
||||
// Long messages of this size require this byte be correct.
|
||||
if (results->state[5] != 0xFC) return false;
|
||||
break;
|
||||
case FUJITSU_AC_BITS:
|
||||
// Long messages of this size require this byte be correct.
|
||||
if (results->state[5] != 0xFE) return false;
|
||||
break;
|
||||
default:
|
||||
return false; // Unexpected size.
|
||||
}
|
||||
if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
return true; // All good.
|
||||
}
|
||||
#endif // DECODE_FUJITSU_AC
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2017 Jonny Graham
|
||||
#ifndef IR_FUJITSU_H_
|
||||
#define IR_FUJITSU_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
|
||||
// FUJITSU A/C support added by Jonny Graham
|
||||
|
||||
// Constants
|
||||
|
||||
#define FUJITSU_AC_MODE_AUTO 0x00U
|
||||
#define FUJITSU_AC_MODE_COOL 0x01U
|
||||
#define FUJITSU_AC_MODE_DRY 0x02U
|
||||
#define FUJITSU_AC_MODE_FAN 0x03U
|
||||
#define FUJITSU_AC_MODE_HEAT 0x04U
|
||||
|
||||
#define FUJITSU_AC_CMD_STAY_ON 0x00U
|
||||
#define FUJITSU_AC_CMD_TURN_ON 0x01U
|
||||
#define FUJITSU_AC_CMD_TURN_OFF 0x02U
|
||||
#define FUJITSU_AC_CMD_STEP_HORIZ 0x79U
|
||||
#define FUJITSU_AC_CMD_STEP_VERT 0x6CU
|
||||
|
||||
#define FUJITSU_AC_FAN_AUTO 0x00U
|
||||
#define FUJITSU_AC_FAN_HIGH 0x01U
|
||||
#define FUJITSU_AC_FAN_MED 0x02U
|
||||
#define FUJITSU_AC_FAN_LOW 0x03U
|
||||
#define FUJITSU_AC_FAN_QUIET 0x04U
|
||||
|
||||
#define FUJITSU_AC_MIN_TEMP 16U // 16C
|
||||
#define FUJITSU_AC_MAX_TEMP 30U // 30C
|
||||
|
||||
#define FUJITSU_AC_SWING_OFF 0x00U
|
||||
#define FUJITSU_AC_SWING_VERT 0x01U
|
||||
#define FUJITSU_AC_SWING_HORIZ 0x02U
|
||||
#define FUJITSU_AC_SWING_BOTH 0x03U
|
||||
|
||||
|
||||
enum fujitsu_ac_remote_model_t {
|
||||
ARRAH2E = 1,
|
||||
ARDB1,
|
||||
};
|
||||
|
||||
class IRFujitsuAC {
|
||||
public:
|
||||
explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E);
|
||||
|
||||
void setModel(fujitsu_ac_remote_model_t model);
|
||||
void stateReset();
|
||||
#if SEND_FUJITSU_AC
|
||||
void send();
|
||||
#endif // SEND_FUJITSU_AC
|
||||
void begin();
|
||||
void off();
|
||||
void stepHoriz();
|
||||
void stepVert();
|
||||
void setCmd(uint8_t cmd);
|
||||
uint8_t getCmd();
|
||||
void setTemp(uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
void setFanSpeed(uint8_t fan);
|
||||
uint8_t getFanSpeed();
|
||||
void setMode(uint8_t mode);
|
||||
uint8_t getMode();
|
||||
void setSwing(uint8_t mode);
|
||||
uint8_t getSwing();
|
||||
uint8_t* getRaw();
|
||||
bool setRaw(const uint8_t newState[], const uint16_t length);
|
||||
uint8_t getStateLength();
|
||||
static bool validChecksum(uint8_t *state, uint16_t length);
|
||||
bool getPower();
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t remote_state[FUJITSU_AC_STATE_LENGTH];
|
||||
IRsend _irsend;
|
||||
uint8_t _temp;
|
||||
uint8_t _fanSpeed;
|
||||
uint8_t _mode;
|
||||
uint8_t _swingMode;
|
||||
uint8_t _cmd;
|
||||
fujitsu_ac_remote_model_t _model;
|
||||
uint8_t _state_length;
|
||||
uint8_t _state_length_short;
|
||||
void buildState();
|
||||
void buildFromState(const uint16_t length);
|
||||
};
|
||||
|
||||
#endif // IR_FUJITSU_H_
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 2018 David Conran
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE
|
||||
// GG GG III CC C AAAAA BB B LL EE
|
||||
// GG III CC AA AA BBBBBB LL EEEEE
|
||||
// GG GG ... III ... CC C AAAAAAA BB BB LL EE
|
||||
// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/447
|
||||
|
||||
// Constants
|
||||
#define GICABLE_HDR_MARK 9000U
|
||||
#define GICABLE_HDR_SPACE 4400U
|
||||
#define GICABLE_BIT_MARK 550U
|
||||
#define GICABLE_ONE_SPACE 4400U
|
||||
#define GICABLE_ZERO_SPACE 2200U
|
||||
#define GICABLE_RPT_SPACE 2200U
|
||||
#define GICABLE_MIN_COMMAND_LENGTH 99600U
|
||||
#define GICABLE_MIN_GAP (GICABLE_MIN_COMMAND_LENGTH - \
|
||||
(GICABLE_HDR_MARK + GICABLE_HDR_SPACE + \
|
||||
GICABLE_BITS * (GICABLE_BIT_MARK + GICABLE_ONE_SPACE) + GICABLE_BIT_MARK))
|
||||
|
||||
|
||||
#if SEND_GICABLE
|
||||
// Send a raw G.I. Cable formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The message to be sent.
|
||||
// nbits: The number of bits of the message to be sent.
|
||||
// Typically GICABLE_BITS.
|
||||
// repeat: The number of times the command is to be repeated.
|
||||
//
|
||||
// Status: Alpha / Untested.
|
||||
//
|
||||
// Ref:
|
||||
void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(GICABLE_HDR_MARK, GICABLE_HDR_SPACE,
|
||||
GICABLE_BIT_MARK, GICABLE_ONE_SPACE,
|
||||
GICABLE_BIT_MARK, GICABLE_ZERO_SPACE,
|
||||
GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH,
|
||||
data, nbits, 39, true, 0, // Repeats are handled later.
|
||||
50);
|
||||
// Message repeat sequence.
|
||||
if (repeat)
|
||||
sendGeneric(GICABLE_HDR_MARK, GICABLE_RPT_SPACE,
|
||||
0, 0, 0, 0, // No actual data sent.
|
||||
GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH,
|
||||
0, 0, // No data to be sent.
|
||||
39, true, repeat - 1, 50);
|
||||
}
|
||||
#endif // SEND_GICABLE
|
||||
|
||||
#if DECODE_GICABLE
|
||||
// Decode the supplied G.I. Cable message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically GICABLE_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: Alpha / Not tested against a real device.
|
||||
bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (results->rawlen < 2 * (nbits + HEADER + FOOTER) - 1)
|
||||
return false; // Can't possibly be a valid GICABLE message.
|
||||
if (strict && nbits != GICABLE_BITS)
|
||||
return false; // Not strictly an GICABLE message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], GICABLE_HDR_SPACE)) return false;
|
||||
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
GICABLE_BIT_MARK,
|
||||
GICABLE_ONE_SPACE,
|
||||
GICABLE_BIT_MARK,
|
||||
GICABLE_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset++], GICABLE_MIN_GAP))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// We expect a repeat frame.
|
||||
if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], GICABLE_RPT_SPACE)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = GICABLE;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GICABLE
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2016 Hisham Khalifa
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRsend.h"
|
||||
|
||||
// GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE
|
||||
// G G L O O B B AAAA L C C AAAA C C H H E
|
||||
// G L O O BBBBB A A L C A A C HHHHHH EEEE
|
||||
// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E
|
||||
// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE
|
||||
|
||||
// Global Cache IR format sender originally added by Hisham Khalifa
|
||||
// (http://www.hishamkhalifa.com)
|
||||
|
||||
// Constants
|
||||
#define GLOBALCACHE_MAX_REPEAT 50U
|
||||
#define GLOBALCACHE_MIN_USEC 80U
|
||||
#define GLOBALCACHE_FREQ_INDEX 0U
|
||||
#define GLOBALCACHE_RPT_INDEX GLOBALCACHE_FREQ_INDEX + 1U
|
||||
#define GLOBALCACHE_RPT_START_INDEX GLOBALCACHE_RPT_INDEX + 1U
|
||||
#define GLOBALCACHE_START_INDEX GLOBALCACHE_RPT_START_INDEX + 1U
|
||||
|
||||
#if SEND_GLOBALCACHE
|
||||
// Send a shortened GlobalCache (GC) IRdb/control tower formatted message.
|
||||
//
|
||||
// Args:
|
||||
// buf: An array of uint16_t containing the shortened GlobalCache data.
|
||||
// len: Nr. of entries in the buf[] array.
|
||||
//
|
||||
// Status: STABLE / Known working.
|
||||
//
|
||||
// Note:
|
||||
// Global Cache format without the emitter ID or request ID.
|
||||
// Starts at the frequency (Hertz), followed by nr. of times to emit (count),
|
||||
// then the offset for repeats (where a repeat will start from),
|
||||
// then the rest of entries are the actual IR message as units of periodic
|
||||
// time.
|
||||
// e.g. sendir,1:1,1,38000,1,1,9,70,9,30,9,... -> 38000,1,1,9,70,9,30,9,...
|
||||
// Ref:
|
||||
// https://irdb.globalcache.com/Home/Database
|
||||
void IRsend::sendGC(uint16_t buf[], uint16_t len) {
|
||||
uint16_t hz = buf[GLOBALCACHE_FREQ_INDEX]; // GC frequency is in Hz.
|
||||
enableIROut(hz);
|
||||
uint32_t periodic_time = calcUSecPeriod(hz, false);
|
||||
uint8_t emits = std::min(buf[GLOBALCACHE_RPT_INDEX],
|
||||
(uint16_t) GLOBALCACHE_MAX_REPEAT);
|
||||
// Repeat
|
||||
for (uint8_t repeat = 0; repeat < emits; repeat++) {
|
||||
// First time through, start at the beginning (GLOBALCACHE_START_INDEX),
|
||||
// otherwise for repeats, we start a specified offset from that.
|
||||
uint16_t offset = GLOBALCACHE_START_INDEX;
|
||||
if (repeat)
|
||||
offset += buf[GLOBALCACHE_RPT_START_INDEX] - 1;
|
||||
// Data
|
||||
for (; offset < len; offset++) {
|
||||
// Convert periodic units to microseconds.
|
||||
// Minimum is GLOBALCACHE_MIN_USEC for actual GC units.
|
||||
uint32_t microseconds = std::max(buf[offset] * periodic_time,
|
||||
GLOBALCACHE_MIN_USEC);
|
||||
// These codes start at an odd index (not even as with sendRaw).
|
||||
if (offset & 1) // Odd bit.
|
||||
mark(microseconds);
|
||||
else // Even bit.
|
||||
space(microseconds);
|
||||
}
|
||||
}
|
||||
// It's possible that we've ended on a mark(), thus ensure the LED is off.
|
||||
ledOff();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,498 @@
|
||||
// Copyright 2017 Ville Skyttä (scop)
|
||||
// Copyright 2017, 2018 David Conran
|
||||
//
|
||||
// Code to emulate Gree protocol compatible HVAC devices.
|
||||
// Should be compatible with:
|
||||
// * Heat pumps carrying the "Ultimate" brand name.
|
||||
// * EKOKAI air conditioners.
|
||||
//
|
||||
|
||||
#include "ir_Gree.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
#include "ir_Kelvinator.h"
|
||||
|
||||
// GGGG RRRRRR EEEEEEE EEEEEEE
|
||||
// GG GG RR RR EE EE
|
||||
// GG RRRRRR EEEEE EEEEE
|
||||
// GG GG RR RR EE EE
|
||||
// GGGGGG RR RR EEEEEEE EEEEEEE
|
||||
|
||||
// Constants
|
||||
// Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h
|
||||
#define GREE_HDR_MARK 9000U
|
||||
#define GREE_HDR_SPACE 4000U
|
||||
#define GREE_BIT_MARK 620U
|
||||
#define GREE_ONE_SPACE 1600U
|
||||
#define GREE_ZERO_SPACE 540U
|
||||
#define GREE_MSG_SPACE 19000U
|
||||
#define GREE_BLOCK_FOOTER 0b010U
|
||||
#define GREE_BLOCK_FOOTER_BITS 3U
|
||||
|
||||
#if SEND_GREE
|
||||
// Send a Gree Heat Pump message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=GREE_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
|
||||
void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
|
||||
if (nbytes < GREE_STATE_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Block #1
|
||||
sendGeneric(GREE_HDR_MARK, GREE_HDR_SPACE,
|
||||
GREE_BIT_MARK, GREE_ONE_SPACE,
|
||||
GREE_BIT_MARK, GREE_ZERO_SPACE,
|
||||
0, 0, // No Footer.
|
||||
data, 4, 38, false, 0, 50);
|
||||
// Footer #1
|
||||
sendGeneric(0, 0, // No Header
|
||||
GREE_BIT_MARK, GREE_ONE_SPACE,
|
||||
GREE_BIT_MARK, GREE_ZERO_SPACE,
|
||||
GREE_BIT_MARK, GREE_MSG_SPACE,
|
||||
0b010, 3, 38, true, 0, false);
|
||||
|
||||
// Block #2
|
||||
sendGeneric(0, 0, // No Header for Block #2
|
||||
GREE_BIT_MARK, GREE_ONE_SPACE,
|
||||
GREE_BIT_MARK, GREE_ZERO_SPACE,
|
||||
GREE_BIT_MARK, GREE_MSG_SPACE,
|
||||
data + 4, nbytes - 4, 38, false, 0, 50);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a Gree Heat Pump message.
|
||||
//
|
||||
// Args:
|
||||
// data: The raw message to be sent.
|
||||
// nbits: Nr. of bits of data in the message. (Default is GREE_BITS)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
|
||||
void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits != GREE_BITS)
|
||||
return; // Wrong nr. of bits to send a proper message.
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
mark(GREE_HDR_MARK);
|
||||
space(GREE_HDR_SPACE);
|
||||
|
||||
// Data
|
||||
for (int16_t i = 8; i <= nbits; i += 8) {
|
||||
sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE,
|
||||
(data >> (nbits - i)) & 0xFF, 8, false);
|
||||
if (i == nbits / 2) {
|
||||
// Send the mid-message Footer.
|
||||
sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE,
|
||||
0b010, 3);
|
||||
mark(GREE_BIT_MARK);
|
||||
space(GREE_MSG_SPACE);
|
||||
}
|
||||
}
|
||||
// Footer
|
||||
mark(GREE_BIT_MARK);
|
||||
space(GREE_MSG_SPACE);
|
||||
}
|
||||
}
|
||||
#endif // SEND_GREE
|
||||
|
||||
IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRGreeAC::stateReset() {
|
||||
// This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C.
|
||||
for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++)
|
||||
remote_state[i] = 0x0;
|
||||
remote_state[1] = 0x09;
|
||||
remote_state[2] = 0x20;
|
||||
remote_state[3] = 0x50;
|
||||
remote_state[5] = 0x20;
|
||||
remote_state[7] = 0x50;
|
||||
}
|
||||
|
||||
void IRGreeAC::fixup() {
|
||||
checksum(); // Calculate the checksums
|
||||
}
|
||||
|
||||
void IRGreeAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_GREE
|
||||
void IRGreeAC::send() {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
_irsend.sendGree(remote_state);
|
||||
}
|
||||
#endif // SEND_GREE
|
||||
|
||||
uint8_t* IRGreeAC::getRaw() {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
void IRGreeAC::setRaw(uint8_t new_code[]) {
|
||||
for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++) {
|
||||
remote_state[i] = new_code[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IRGreeAC::checksum(const uint16_t length) {
|
||||
// Gree uses the same checksum alg. as Kelvinator's block checksum.
|
||||
uint8_t sum = IRKelvinatorAC::calcBlockChecksum(remote_state, length);
|
||||
remote_state[length - 1] = (sum << 4) | (remote_state[length - 1] & 0xFU);
|
||||
}
|
||||
|
||||
// Verify the checksum is valid for a given state.
|
||||
// Args:
|
||||
// state: The array to verify the checksum of.
|
||||
// length: The size of the state.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
// Top 4 bits of the last byte in the state is the state's checksum.
|
||||
if (state[length - 1] >> 4 == IRKelvinatorAC::calcBlockChecksum(state,
|
||||
length))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRGreeAC::on() {
|
||||
remote_state[0] |= GREE_POWER1_MASK;
|
||||
remote_state[2] |= GREE_POWER2_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::off() {
|
||||
remote_state[0] &= ~GREE_POWER1_MASK;
|
||||
remote_state[2] &= ~GREE_POWER2_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::setPower(const bool state) {
|
||||
if (state)
|
||||
on();
|
||||
else
|
||||
off();
|
||||
}
|
||||
|
||||
bool IRGreeAC::getPower() {
|
||||
return (remote_state[0] & GREE_POWER1_MASK) &&
|
||||
(remote_state[2] & GREE_POWER2_MASK);
|
||||
}
|
||||
|
||||
// Set the temp. in deg C
|
||||
void IRGreeAC::setTemp(const uint8_t temp) {
|
||||
uint8_t new_temp = std::max((uint8_t) GREE_MIN_TEMP, temp);
|
||||
new_temp = std::min((uint8_t) GREE_MAX_TEMP, new_temp);
|
||||
if (getMode() == GREE_AUTO) new_temp = 25;
|
||||
remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - GREE_MIN_TEMP);
|
||||
}
|
||||
|
||||
// Return the set temp. in deg C
|
||||
uint8_t IRGreeAC::getTemp() {
|
||||
return ((remote_state[1] & 0xFU) + GREE_MIN_TEMP);
|
||||
}
|
||||
|
||||
// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed
|
||||
void IRGreeAC::setFan(const uint8_t speed) {
|
||||
uint8_t fan = std::min((uint8_t) GREE_FAN_MAX, speed); // Bounds check
|
||||
|
||||
if (getMode() == GREE_DRY) fan = 1; // DRY mode is always locked to fan 1.
|
||||
// Set the basic fan values.
|
||||
remote_state[0] &= ~GREE_FAN_MASK;
|
||||
remote_state[0] |= (fan << 4);
|
||||
}
|
||||
|
||||
uint8_t IRGreeAC::getFan() {
|
||||
return ((remote_state[0] & GREE_FAN_MASK) >> 4);
|
||||
}
|
||||
|
||||
void IRGreeAC::setMode(const uint8_t new_mode) {
|
||||
uint8_t mode = new_mode;
|
||||
switch (mode) {
|
||||
case GREE_AUTO:
|
||||
// AUTO is locked to 25C
|
||||
setTemp(25);
|
||||
break;
|
||||
case GREE_DRY:
|
||||
// DRY always sets the fan to 1.
|
||||
setFan(1);
|
||||
break;
|
||||
case GREE_COOL:
|
||||
case GREE_FAN:
|
||||
case GREE_HEAT:
|
||||
break;
|
||||
default:
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
mode = GREE_AUTO;
|
||||
}
|
||||
remote_state[0] &= ~GREE_MODE_MASK;
|
||||
remote_state[0] |= mode;
|
||||
}
|
||||
|
||||
uint8_t IRGreeAC::getMode() {
|
||||
return (remote_state[0] & GREE_MODE_MASK);
|
||||
}
|
||||
|
||||
void IRGreeAC::setLight(const bool state) {
|
||||
remote_state[2] &= ~GREE_LIGHT_MASK;
|
||||
remote_state[2] |= (state << 5);
|
||||
}
|
||||
|
||||
bool IRGreeAC::getLight() {
|
||||
return remote_state[2] & GREE_LIGHT_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::setXFan(const bool state) {
|
||||
remote_state[2] &= ~GREE_XFAN_MASK;
|
||||
remote_state[2] |= (state << 7);
|
||||
}
|
||||
|
||||
bool IRGreeAC::getXFan() {
|
||||
return remote_state[2] & GREE_XFAN_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::setSleep(const bool state) {
|
||||
remote_state[0] &= ~GREE_SLEEP_MASK;
|
||||
remote_state[0] |= (state << 7);
|
||||
}
|
||||
|
||||
bool IRGreeAC::getSleep() {
|
||||
return remote_state[0] & GREE_SLEEP_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::setTurbo(const bool state) {
|
||||
remote_state[2] &= ~GREE_TURBO_MASK;
|
||||
remote_state[2] |= (state << 4);
|
||||
}
|
||||
|
||||
bool IRGreeAC::getTurbo() {
|
||||
return remote_state[2] & GREE_TURBO_MASK;
|
||||
}
|
||||
|
||||
void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
|
||||
remote_state[0] &= ~GREE_SWING_AUTO_MASK;
|
||||
remote_state[0] |= (automatic << 6);
|
||||
uint8_t new_position = position;
|
||||
if (!automatic) {
|
||||
switch (position) {
|
||||
case GREE_SWING_UP:
|
||||
case GREE_SWING_MIDDLE_UP:
|
||||
case GREE_SWING_MIDDLE:
|
||||
case GREE_SWING_MIDDLE_DOWN:
|
||||
case GREE_SWING_DOWN:
|
||||
break;
|
||||
default:
|
||||
new_position = GREE_SWING_LAST_POS;
|
||||
}
|
||||
} else {
|
||||
switch (position) {
|
||||
case GREE_SWING_AUTO:
|
||||
case GREE_SWING_DOWN_AUTO:
|
||||
case GREE_SWING_MIDDLE_AUTO:
|
||||
case GREE_SWING_UP_AUTO:
|
||||
break;
|
||||
default:
|
||||
new_position = GREE_SWING_AUTO;
|
||||
}
|
||||
}
|
||||
remote_state[4] &= ~GREE_SWING_POS_MASK;
|
||||
remote_state[4] |= new_position;
|
||||
}
|
||||
|
||||
bool IRGreeAC::getSwingVerticalAuto() {
|
||||
return remote_state[0] & GREE_SWING_AUTO_MASK;
|
||||
}
|
||||
|
||||
uint8_t IRGreeAC::getSwingVerticalPosition() {
|
||||
return remote_state[4] & GREE_SWING_POS_MASK;
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRGreeAC::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRGreeAC::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case GREE_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case GREE_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case GREE_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case GREE_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case GREE_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case 0:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case GREE_FAN_MAX:
|
||||
result += " (MAX)";
|
||||
break;
|
||||
}
|
||||
result += ", Turbo: ";
|
||||
if (getTurbo())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", XFan: ";
|
||||
if (getXFan())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Light: ";
|
||||
if (getLight())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Sleep: ";
|
||||
if (getSleep())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Swing Vertical Mode: ";
|
||||
if (getSwingVerticalAuto())
|
||||
result += "Auto";
|
||||
else
|
||||
result += "Manual";
|
||||
result += ", Swing Vertical Pos: " +
|
||||
uint64ToString(getSwingVerticalPosition());
|
||||
switch (getSwingVerticalPosition()) {
|
||||
case GREE_SWING_LAST_POS:
|
||||
result += " (Last Pos)";
|
||||
break;
|
||||
case GREE_SWING_AUTO:
|
||||
result += " (Auto)";
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_GREE
|
||||
// Decode the supplied Gree message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically GREE_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
bool IRrecv::decodeGree(decode_results *results, uint16_t nbits, bool strict) {
|
||||
if (results->rawlen < 2 * (nbits + GREE_BLOCK_FOOTER_BITS) +
|
||||
(HEADER + FOOTER + 1))
|
||||
return false; // Can't possibly be a valid Gree message.
|
||||
if (strict && nbits != GREE_BITS)
|
||||
return false; // Not strictly a Gree message.
|
||||
|
||||
uint32_t data;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// There are two blocks back-to-back in a full Gree IR message
|
||||
// sequence.
|
||||
int8_t state_pos = 0;
|
||||
match_result_t data_result;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], GREE_HDR_MARK)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], GREE_HDR_SPACE)) return false;
|
||||
// Data Block #1 (32 bits)
|
||||
data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK,
|
||||
GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Record Data Block #1 in the state.
|
||||
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8)
|
||||
results->state[i] = reverseBits(data & 0xFF, 8);
|
||||
state_pos += 4;
|
||||
|
||||
// Block #1 footer (3 bits, B010)
|
||||
data_result = matchData(&(results->rawbuf[offset]), GREE_BLOCK_FOOTER_BITS,
|
||||
GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK,
|
||||
GREE_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
if (data_result.data != GREE_BLOCK_FOOTER) return false;
|
||||
offset += data_result.used;
|
||||
|
||||
// Inter-block gap.
|
||||
if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], GREE_MSG_SPACE)) return false;
|
||||
|
||||
// Data Block #2 (32 bits)
|
||||
data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK,
|
||||
GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Record Data Block #2 in the state.
|
||||
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8)
|
||||
results->state[i] = reverseBits(data & 0xFF, 8);
|
||||
state_pos += 4;
|
||||
|
||||
// Footer.
|
||||
if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false;
|
||||
if (offset <= results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], GREE_MSG_SPACE))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Correct size/length)
|
||||
if (state_pos != GREE_STATE_LENGTH) return false;
|
||||
// Verify the message's checksum is correct.
|
||||
if (!IRGreeAC::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = GREE;
|
||||
results->bits = state_pos * 8;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GREE
|
||||
@@ -0,0 +1,110 @@
|
||||
// Kelvinator A/C
|
||||
//
|
||||
// Copyright 2016 David Conran
|
||||
|
||||
#ifndef IR_GREE_H_
|
||||
#define IR_GREE_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// GGGG RRRRRR EEEEEEE EEEEEEE
|
||||
// GG GG RR RR EE EE
|
||||
// GG RRRRRR EEEEE EEEEE
|
||||
// GG GG RR RR EE EE
|
||||
// GGGGGG RR RR EEEEEEE EEEEEEE
|
||||
|
||||
// Constants
|
||||
#define GREE_AUTO 0U
|
||||
#define GREE_COOL 1U
|
||||
#define GREE_DRY 2U
|
||||
#define GREE_FAN 3U
|
||||
#define GREE_HEAT 4U
|
||||
|
||||
// Byte 0
|
||||
#define GREE_MODE_MASK 0b00000111U
|
||||
#define GREE_POWER1_MASK 0b00001000U
|
||||
#define GREE_FAN_MASK 0b00110000U
|
||||
#define GREE_SWING_AUTO_MASK 0b01000000U
|
||||
#define GREE_SLEEP_MASK 0b10000000U
|
||||
// Byte 2
|
||||
#define GREE_TURBO_MASK 0b00010000U
|
||||
#define GREE_LIGHT_MASK 0b00100000U
|
||||
#define GREE_POWER2_MASK 0b01000000U
|
||||
#define GREE_XFAN_MASK 0b10000000U
|
||||
// Byte 4
|
||||
#define GREE_SWING_POS_MASK 0b00001111U
|
||||
|
||||
#define GREE_MIN_TEMP 16U // Celsius
|
||||
#define GREE_MAX_TEMP 30U // Celsius
|
||||
#define GREE_FAN_MAX 3U
|
||||
|
||||
#define GREE_SWING_LAST_POS 0b00000000U
|
||||
#define GREE_SWING_AUTO 0b00000001U
|
||||
#define GREE_SWING_UP 0b00000010U
|
||||
#define GREE_SWING_MIDDLE_UP 0b00000011U
|
||||
#define GREE_SWING_MIDDLE 0b00000100U
|
||||
#define GREE_SWING_MIDDLE_DOWN 0b00000101U
|
||||
#define GREE_SWING_DOWN 0b00000110U
|
||||
#define GREE_SWING_DOWN_AUTO 0b00000111U
|
||||
#define GREE_SWING_MIDDLE_AUTO 0b00001001U
|
||||
#define GREE_SWING_UP_AUTO 0b00001011U
|
||||
|
||||
// Classes
|
||||
class IRGreeAC {
|
||||
public:
|
||||
explicit IRGreeAC(uint16_t pin);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_GREE
|
||||
void send();
|
||||
#endif // SEND_GREE
|
||||
void begin();
|
||||
void on();
|
||||
void off();
|
||||
void setPower(const bool state);
|
||||
bool getPower();
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan();
|
||||
void setMode(const uint8_t new_mode);
|
||||
uint8_t getMode();
|
||||
void setLight(const bool state);
|
||||
bool getLight();
|
||||
void setXFan(const bool state);
|
||||
bool getXFan();
|
||||
void setSleep(const bool state);
|
||||
bool getSleep();
|
||||
void setTurbo(const bool state);
|
||||
bool getTurbo();
|
||||
void setSwingVertical(const bool automatic, const uint8_t position);
|
||||
bool getSwingVerticalAuto();
|
||||
uint8_t getSwingVerticalPosition();
|
||||
|
||||
uint8_t* getRaw();
|
||||
void setRaw(uint8_t new_code[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = GREE_STATE_LENGTH);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// The state of the IR remote in IR code form.
|
||||
uint8_t remote_state[GREE_STATE_LENGTH];
|
||||
void checksum(const uint16_t length = GREE_STATE_LENGTH);
|
||||
void fixup();
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_GREE_H_
|
||||
@@ -0,0 +1,921 @@
|
||||
// Copyright 2018 crankyoldgit
|
||||
// The specifics of reverse engineering the protocols details:
|
||||
// * HSU07-HEA03 by kuzin2006.
|
||||
// * YR-W02/HSU-09HMC203 by non7top.
|
||||
|
||||
#include "ir_Haier.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// HH HH AAA IIIII EEEEEEE RRRRRR
|
||||
// HH HH AAAAA III EE RR RR
|
||||
// HHHHHHH AA AA III EEEEE RRRRRR
|
||||
// HH HH AAAAAAA III EE RR RR
|
||||
// HH HH AA AA IIIII EEEEEEE RR RR
|
||||
|
||||
// Supported devices:
|
||||
// * Haier HSU07-HEA03 Remote control.
|
||||
// * Haier YR-W02 Remote control
|
||||
// * Haier HSU-09HMC203 A/C unit.
|
||||
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/404
|
||||
// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/485
|
||||
// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
|
||||
|
||||
// Constants
|
||||
#define HAIER_AC_HDR 3000U
|
||||
#define HAIER_AC_HDR_GAP 4300U
|
||||
|
||||
#define HAIER_AC_BIT_MARK 520U
|
||||
#define HAIER_AC_ONE_SPACE 1650U
|
||||
#define HAIER_AC_ZERO_SPACE 650U
|
||||
#define HAIER_AC_MIN_GAP 150000U // Completely made up value.
|
||||
|
||||
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
|
||||
// Send a Haier A/C message. (HSU07-HEA03 remote)
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=HAIER_AC_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: Beta / Probably working.
|
||||
//
|
||||
void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < HAIER_AC_STATE_LENGTH)
|
||||
return;
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
enableIROut(38000);
|
||||
mark(HAIER_AC_HDR);
|
||||
space(HAIER_AC_HDR);
|
||||
sendGeneric(HAIER_AC_HDR, HAIER_AC_HDR_GAP,
|
||||
HAIER_AC_BIT_MARK, HAIER_AC_ONE_SPACE,
|
||||
HAIER_AC_BIT_MARK, HAIER_AC_ZERO_SPACE,
|
||||
HAIER_AC_BIT_MARK, HAIER_AC_MIN_GAP,
|
||||
data, nbytes, 38, true, 0, // Repeats handled elsewhere
|
||||
50);
|
||||
}
|
||||
}
|
||||
#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
|
||||
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
// Send a Haier YR-W02 remote A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=HAIER_AC_YRW02_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: Alpha / Untested on a real device.
|
||||
//
|
||||
void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes >= HAIER_AC_YRW02_STATE_LENGTH)
|
||||
sendHaierAC(data, nbytes, repeat);
|
||||
}
|
||||
#endif // SEND_HAIER_AC_YRW02
|
||||
|
||||
// Class for emulating a Haier HSU07-HEA03 remote
|
||||
IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRHaierAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_HAIER_AC
|
||||
void IRHaierAC::send() {
|
||||
checksum();
|
||||
_irsend.sendHaierAC(remote_state);
|
||||
}
|
||||
#endif // SEND_HAIER_AC
|
||||
|
||||
void IRHaierAC::checksum() {
|
||||
remote_state[8] = sumBytes(remote_state, HAIER_AC_STATE_LENGTH - 1);
|
||||
}
|
||||
|
||||
bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) {
|
||||
if (length < 2) return false; // 1 byte of data can't have a checksum.
|
||||
return (state[length - 1] == sumBytes(state, length - 1));
|
||||
}
|
||||
|
||||
void IRHaierAC::stateReset() {
|
||||
for (uint8_t i = 1; i < HAIER_AC_STATE_LENGTH; i++)
|
||||
remote_state[i] = 0x0;
|
||||
remote_state[0] = HAIER_AC_PREFIX;
|
||||
remote_state[2] = 0b00100000;
|
||||
|
||||
setTemp(HAIER_AC_DEF_TEMP);
|
||||
setFan(HAIER_AC_FAN_AUTO);
|
||||
setMode(HAIER_AC_AUTO);
|
||||
setCommand(HAIER_AC_CMD_ON);
|
||||
}
|
||||
|
||||
uint8_t* IRHaierAC::getRaw() {
|
||||
checksum();
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
void IRHaierAC::setRaw(uint8_t new_code[]) {
|
||||
for (uint8_t i = 0; i < HAIER_AC_STATE_LENGTH; i++) {
|
||||
remote_state[i] = new_code[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IRHaierAC::setCommand(uint8_t state) {
|
||||
remote_state[1] &= 0b11110000;
|
||||
switch (state) {
|
||||
case HAIER_AC_CMD_OFF:
|
||||
case HAIER_AC_CMD_ON:
|
||||
case HAIER_AC_CMD_MODE:
|
||||
case HAIER_AC_CMD_FAN:
|
||||
case HAIER_AC_CMD_TEMP_UP:
|
||||
case HAIER_AC_CMD_TEMP_DOWN:
|
||||
case HAIER_AC_CMD_SLEEP:
|
||||
case HAIER_AC_CMD_TIMER_SET:
|
||||
case HAIER_AC_CMD_TIMER_CANCEL:
|
||||
case HAIER_AC_CMD_HEALTH:
|
||||
case HAIER_AC_CMD_SWING:
|
||||
remote_state[1] |= (state & 0b00001111);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRHaierAC::getCommand() {
|
||||
return remote_state[1] & (0b00001111);
|
||||
}
|
||||
|
||||
void IRHaierAC::setFan(uint8_t speed) {
|
||||
uint8_t new_speed = HAIER_AC_FAN_AUTO;
|
||||
switch (speed) {
|
||||
case HAIER_AC_FAN_LOW:
|
||||
new_speed = 3;
|
||||
break;
|
||||
case HAIER_AC_FAN_MED:
|
||||
new_speed = 1;
|
||||
break;
|
||||
case HAIER_AC_FAN_HIGH:
|
||||
new_speed = 2;
|
||||
break;
|
||||
default:
|
||||
new_speed = HAIER_AC_FAN_AUTO; // Default to auto for anything else.
|
||||
}
|
||||
|
||||
if (speed != getFan()) setCommand(HAIER_AC_CMD_FAN);
|
||||
remote_state[5] &= 0b11111100;
|
||||
remote_state[5] |= new_speed;
|
||||
}
|
||||
|
||||
uint8_t IRHaierAC::getFan() {
|
||||
switch (remote_state[5] & 0b00000011) {
|
||||
case 1:
|
||||
return HAIER_AC_FAN_MED;
|
||||
case 2:
|
||||
return HAIER_AC_FAN_HIGH;
|
||||
case 3:
|
||||
return HAIER_AC_FAN_LOW;
|
||||
default:
|
||||
return HAIER_AC_FAN_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
void IRHaierAC::setMode(uint8_t mode) {
|
||||
uint8_t new_mode = mode;
|
||||
setCommand(HAIER_AC_CMD_MODE);
|
||||
if (mode > HAIER_AC_FAN) // If out of range, default to auto mode.
|
||||
new_mode = HAIER_AC_AUTO;
|
||||
remote_state[7] &= 0b00011111;
|
||||
remote_state[7] |= (new_mode << 5);
|
||||
}
|
||||
|
||||
uint8_t IRHaierAC::getMode() {
|
||||
return (remote_state[7] & 0b11100000) >> 5;
|
||||
}
|
||||
|
||||
|
||||
void IRHaierAC::setTemp(const uint8_t celsius) {
|
||||
uint8_t temp = celsius;
|
||||
if (temp < HAIER_AC_MIN_TEMP)
|
||||
temp = HAIER_AC_MIN_TEMP;
|
||||
else if (temp > HAIER_AC_MAX_TEMP)
|
||||
temp = HAIER_AC_MAX_TEMP;
|
||||
|
||||
uint8_t old_temp = getTemp();
|
||||
if (old_temp == temp) return;
|
||||
if (old_temp > temp)
|
||||
setCommand(HAIER_AC_CMD_TEMP_DOWN);
|
||||
else
|
||||
setCommand(HAIER_AC_CMD_TEMP_UP);
|
||||
|
||||
remote_state[1] &= 0b00001111; // Clear the previous temp.
|
||||
remote_state[1] |= ((temp - HAIER_AC_MIN_TEMP) << 4);
|
||||
}
|
||||
|
||||
uint8_t IRHaierAC::getTemp() {
|
||||
return ((remote_state[1] & 0b11110000) >> 4) + HAIER_AC_MIN_TEMP;
|
||||
}
|
||||
|
||||
void IRHaierAC::setHealth(bool state) {
|
||||
setCommand(HAIER_AC_CMD_HEALTH);
|
||||
remote_state[4] &= 0b11011111;
|
||||
remote_state[4] |= (state << 5);
|
||||
}
|
||||
|
||||
bool IRHaierAC::getHealth(void) {
|
||||
return remote_state[4] & (1 << 5);
|
||||
}
|
||||
|
||||
void IRHaierAC::setSleep(bool state) {
|
||||
setCommand(HAIER_AC_CMD_SLEEP);
|
||||
remote_state[7] &= 0b10111111;
|
||||
remote_state[7] |= (state << 6);
|
||||
}
|
||||
|
||||
bool IRHaierAC::getSleep(void) {
|
||||
return remote_state[7] & 0b01000000;
|
||||
}
|
||||
|
||||
uint16_t IRHaierAC::getTime(const uint8_t ptr[]) {
|
||||
return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111);
|
||||
}
|
||||
|
||||
int16_t IRHaierAC::getOnTimer() {
|
||||
if (remote_state[3] & 0b10000000) // Check if the timer is turned on.
|
||||
return getTime(remote_state + 6);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int16_t IRHaierAC::getOffTimer() {
|
||||
if (remote_state[3] & 0b01000000) // Check if the timer is turned on.
|
||||
return getTime(remote_state + 4);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t IRHaierAC::getCurrTime() {
|
||||
return getTime(remote_state + 2);
|
||||
}
|
||||
|
||||
void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) {
|
||||
uint16_t mins = nr_mins;
|
||||
if (nr_mins > HAIER_AC_MAX_TIME)
|
||||
mins = HAIER_AC_MAX_TIME;
|
||||
|
||||
// Hours
|
||||
ptr[0] &= 0b11100000;
|
||||
ptr[0] |= (mins / 60);
|
||||
// Minutes
|
||||
ptr[1] &= 0b11000000;
|
||||
ptr[1] |= (mins % 60);
|
||||
}
|
||||
|
||||
void IRHaierAC::setOnTimer(const uint16_t nr_mins) {
|
||||
setCommand(HAIER_AC_CMD_TIMER_SET);
|
||||
remote_state[3] |= 0b10000000;
|
||||
setTime(remote_state + 6, nr_mins);
|
||||
}
|
||||
|
||||
void IRHaierAC::setOffTimer(const uint16_t nr_mins) {
|
||||
setCommand(HAIER_AC_CMD_TIMER_SET);
|
||||
remote_state[3] |= 0b01000000;
|
||||
setTime(remote_state + 4, nr_mins);
|
||||
}
|
||||
|
||||
void IRHaierAC::cancelTimers() {
|
||||
setCommand(HAIER_AC_CMD_TIMER_CANCEL);
|
||||
remote_state[3] &= 0b00111111;
|
||||
}
|
||||
|
||||
void IRHaierAC::setCurrTime(const uint16_t nr_mins) {
|
||||
setTime(remote_state + 2, nr_mins);
|
||||
}
|
||||
|
||||
uint8_t IRHaierAC::getSwing() {
|
||||
return (remote_state[2] & 0b11000000) >> 6;
|
||||
}
|
||||
|
||||
void IRHaierAC::setSwing(const uint8_t state) {
|
||||
if (state == getSwing()) return; // Nothing to do.
|
||||
setCommand(HAIER_AC_CMD_SWING);
|
||||
switch (state) {
|
||||
case HAIER_AC_SWING_OFF:
|
||||
case HAIER_AC_SWING_UP:
|
||||
case HAIER_AC_SWING_DOWN:
|
||||
case HAIER_AC_SWING_CHG:
|
||||
remote_state[2] &= 0b00111111;
|
||||
remote_state[2] |= (state << 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a Haier time into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRHaierAC::timeToString(const uint16_t nr_mins) {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRHaierAC::timeToString(const uint16_t nr_mins) {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
|
||||
if (nr_mins / 24 < 10) result += "0"; // Zero pad.
|
||||
result += uint64ToString(nr_mins / 60);
|
||||
result += ":";
|
||||
if (nr_mins % 60 < 10) result += "0"; // Zero pad.
|
||||
result += uint64ToString(nr_mins % 60);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRHaierAC::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRHaierAC::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
uint8_t cmd = getCommand();
|
||||
result += "Command: " + uint64ToString(cmd) +" (";
|
||||
switch (cmd) {
|
||||
case HAIER_AC_CMD_OFF:
|
||||
result += "Off";
|
||||
break;
|
||||
case HAIER_AC_CMD_ON:
|
||||
result += "On";
|
||||
break;
|
||||
case HAIER_AC_CMD_MODE:
|
||||
result += "Mode";
|
||||
break;
|
||||
case HAIER_AC_CMD_FAN:
|
||||
result += "Fan";
|
||||
break;
|
||||
case HAIER_AC_CMD_TEMP_UP:
|
||||
result += "Temp Up";
|
||||
break;
|
||||
case HAIER_AC_CMD_TEMP_DOWN:
|
||||
result += "Temp Down";
|
||||
break;
|
||||
case HAIER_AC_CMD_SLEEP:
|
||||
result += "Sleep";
|
||||
break;
|
||||
case HAIER_AC_CMD_TIMER_SET:
|
||||
result += "Timer Set";
|
||||
break;
|
||||
case HAIER_AC_CMD_TIMER_CANCEL:
|
||||
result += "Timer Cancel";
|
||||
break;
|
||||
case HAIER_AC_CMD_HEALTH:
|
||||
result += "Health";
|
||||
break;
|
||||
case HAIER_AC_CMD_SWING:
|
||||
result += "Swing";
|
||||
break;
|
||||
default:
|
||||
result += "Unknown";
|
||||
}
|
||||
result += ")";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case HAIER_AC_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case HAIER_AC_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case HAIER_AC_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case HAIER_AC_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case HAIER_AC_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case HAIER_AC_FAN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case HAIER_AC_FAN_HIGH:
|
||||
result += " (MAX)";
|
||||
break;
|
||||
}
|
||||
result += ", Swing: " + uint64ToString(getSwing()) + " (";
|
||||
switch (getSwing()) {
|
||||
case HAIER_AC_SWING_OFF:
|
||||
result += "Off";
|
||||
break;
|
||||
case HAIER_AC_SWING_UP:
|
||||
result += "Up";
|
||||
break;
|
||||
case HAIER_AC_SWING_DOWN:
|
||||
result += "Down";
|
||||
break;
|
||||
case HAIER_AC_SWING_CHG:
|
||||
result += "Chg";
|
||||
break;
|
||||
default:
|
||||
result += "Unknown";
|
||||
}
|
||||
result += ")";
|
||||
result += ", Sleep: ";
|
||||
if (getSleep())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Health: ";
|
||||
if (getHealth())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Current Time: " + timeToString(getCurrTime());
|
||||
result += ", On Timer: ";
|
||||
if (getOnTimer() >= 0)
|
||||
result += timeToString(getOnTimer());
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Off Timer: ";
|
||||
if (getOffTimer() >= 0)
|
||||
result += timeToString(getOffTimer());
|
||||
else
|
||||
result += "Off";
|
||||
|
||||
return result;
|
||||
}
|
||||
// End of IRHaierAC class.
|
||||
|
||||
// Class for emulating a Haier YRW02 remote
|
||||
IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void IRHaierACYRW02::send() {
|
||||
checksum();
|
||||
_irsend.sendHaierACYRW02(remote_state);
|
||||
}
|
||||
#endif // SEND_HAIER_AC_YRW02
|
||||
|
||||
void IRHaierACYRW02::checksum() {
|
||||
remote_state[HAIER_AC_YRW02_STATE_LENGTH - 1] = sumBytes(
|
||||
remote_state, HAIER_AC_YRW02_STATE_LENGTH - 1);
|
||||
}
|
||||
|
||||
bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) {
|
||||
if (length < 2) return false; // 1 byte of data can't have a checksum.
|
||||
return (state[length - 1] == sumBytes(state, length - 1));
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::stateReset() {
|
||||
for (uint8_t i = 1; i < HAIER_AC_YRW02_STATE_LENGTH; i++)
|
||||
remote_state[i] = 0x0;
|
||||
remote_state[0] = HAIER_AC_YRW02_PREFIX;
|
||||
|
||||
setTemp(HAIER_AC_DEF_TEMP);
|
||||
setHealth(true);
|
||||
setTurbo(HAIER_AC_YRW02_TURBO_OFF);
|
||||
setSleep(false);
|
||||
setFan(HAIER_AC_YRW02_FAN_AUTO);
|
||||
setSwing(HAIER_AC_YRW02_SWING_OFF);
|
||||
setMode(HAIER_AC_YRW02_AUTO);
|
||||
setPower(true);
|
||||
}
|
||||
|
||||
uint8_t* IRHaierACYRW02::getRaw() {
|
||||
checksum();
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setRaw(uint8_t new_code[]) {
|
||||
for (uint8_t i = 0; i < HAIER_AC_YRW02_STATE_LENGTH; i++) {
|
||||
remote_state[i] = new_code[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setButton(uint8_t button) {
|
||||
switch (button) {
|
||||
case HAIER_AC_YRW02_BUTTON_TEMP_UP:
|
||||
case HAIER_AC_YRW02_BUTTON_TEMP_DOWN:
|
||||
case HAIER_AC_YRW02_BUTTON_SWING:
|
||||
case HAIER_AC_YRW02_BUTTON_FAN:
|
||||
case HAIER_AC_YRW02_BUTTON_POWER:
|
||||
case HAIER_AC_YRW02_BUTTON_MODE:
|
||||
case HAIER_AC_YRW02_BUTTON_HEALTH:
|
||||
case HAIER_AC_YRW02_BUTTON_TURBO:
|
||||
case HAIER_AC_YRW02_BUTTON_SLEEP:
|
||||
remote_state[12] &= 0b11110000;
|
||||
remote_state[12] |= (button & 0b00001111);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getButton() {
|
||||
return remote_state[12] & (0b00001111);
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setMode(uint8_t mode) {
|
||||
uint8_t new_mode = mode;
|
||||
setButton(HAIER_AC_YRW02_BUTTON_MODE);
|
||||
switch (mode) {
|
||||
case HAIER_AC_YRW02_AUTO:
|
||||
case HAIER_AC_YRW02_COOL:
|
||||
case HAIER_AC_YRW02_DRY:
|
||||
case HAIER_AC_YRW02_HEAT:
|
||||
case HAIER_AC_YRW02_FAN:
|
||||
break;
|
||||
default: // If unexpected, default to auto mode.
|
||||
new_mode = HAIER_AC_YRW02_AUTO;
|
||||
}
|
||||
remote_state[7] &= 0b0001111;
|
||||
remote_state[7] |= (new_mode << 4);
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getMode() {
|
||||
return remote_state[7] >> 4;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setTemp(const uint8_t celcius) {
|
||||
uint8_t temp = celcius;
|
||||
if (temp < HAIER_AC_MIN_TEMP)
|
||||
temp = HAIER_AC_MIN_TEMP;
|
||||
else if (temp > HAIER_AC_MAX_TEMP)
|
||||
temp = HAIER_AC_MAX_TEMP;
|
||||
|
||||
uint8_t old_temp = getTemp();
|
||||
if (old_temp == temp) return;
|
||||
if (old_temp > temp)
|
||||
setButton(HAIER_AC_YRW02_BUTTON_TEMP_DOWN);
|
||||
else
|
||||
setButton(HAIER_AC_YRW02_BUTTON_TEMP_UP);
|
||||
|
||||
remote_state[1] &= 0b00001111; // Clear the previous temp.
|
||||
remote_state[1] |= ((temp - HAIER_AC_MIN_TEMP) << 4);
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getTemp() {
|
||||
return ((remote_state[1] & 0b11110000) >> 4) + HAIER_AC_MIN_TEMP;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setHealth(bool state) {
|
||||
setButton(HAIER_AC_YRW02_BUTTON_HEALTH);
|
||||
remote_state[3] &= 0b11111101;
|
||||
remote_state[3] |= (state << 1);
|
||||
}
|
||||
|
||||
bool IRHaierACYRW02::getHealth(void) {
|
||||
return remote_state[3] & 0b00000010;
|
||||
}
|
||||
|
||||
bool IRHaierACYRW02::getPower() {
|
||||
return remote_state[4] & HAIER_AC_YRW02_POWER;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setPower(bool state) {
|
||||
setButton(HAIER_AC_YRW02_BUTTON_POWER);
|
||||
if (state)
|
||||
remote_state[4] |= HAIER_AC_YRW02_POWER;
|
||||
else
|
||||
remote_state[4] &= ~HAIER_AC_YRW02_POWER;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::on() {
|
||||
setPower(true);
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::off() {
|
||||
setPower(false);
|
||||
}
|
||||
|
||||
bool IRHaierACYRW02::getSleep() {
|
||||
return remote_state[8] & HAIER_AC_YRW02_SLEEP;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setSleep(bool state) {
|
||||
setButton(HAIER_AC_YRW02_BUTTON_SLEEP);
|
||||
if (state)
|
||||
remote_state[8] |= HAIER_AC_YRW02_SLEEP;
|
||||
else
|
||||
remote_state[8] &= ~HAIER_AC_YRW02_SLEEP;
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getTurbo() {
|
||||
return remote_state[6] >> 6;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setTurbo(uint8_t speed) {
|
||||
switch (speed) {
|
||||
case HAIER_AC_YRW02_TURBO_OFF:
|
||||
case HAIER_AC_YRW02_TURBO_LOW:
|
||||
case HAIER_AC_YRW02_TURBO_HIGH:
|
||||
remote_state[6] &= 0b00111111;
|
||||
remote_state[6] |= (speed << 6);
|
||||
setButton(HAIER_AC_YRW02_BUTTON_TURBO);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getFan() {
|
||||
return remote_state[5] >> 4;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setFan(uint8_t speed) {
|
||||
switch (speed) {
|
||||
case HAIER_AC_YRW02_FAN_LOW:
|
||||
case HAIER_AC_YRW02_FAN_MED:
|
||||
case HAIER_AC_YRW02_FAN_HIGH:
|
||||
case HAIER_AC_YRW02_FAN_AUTO:
|
||||
remote_state[5] &= 0b00001111;
|
||||
remote_state[5] |= (speed << 4);
|
||||
setButton(HAIER_AC_YRW02_BUTTON_FAN);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRHaierACYRW02::getSwing() {
|
||||
return remote_state[1] & 0b00001111;
|
||||
}
|
||||
|
||||
void IRHaierACYRW02::setSwing(uint8_t state) {
|
||||
uint8_t newstate = state;
|
||||
switch (state) {
|
||||
case HAIER_AC_YRW02_SWING_OFF:
|
||||
case HAIER_AC_YRW02_SWING_AUTO:
|
||||
case HAIER_AC_YRW02_SWING_TOP:
|
||||
case HAIER_AC_YRW02_SWING_MIDDLE:
|
||||
case HAIER_AC_YRW02_SWING_BOTTOM:
|
||||
case HAIER_AC_YRW02_SWING_DOWN:
|
||||
setButton(HAIER_AC_YRW02_BUTTON_SWING);
|
||||
break;
|
||||
default:
|
||||
return; // Unexpected value so don't do anything.
|
||||
}
|
||||
|
||||
// Heat mode has no MIDDLE setting, use BOTTOM instead.
|
||||
if (state == HAIER_AC_YRW02_SWING_MIDDLE &&
|
||||
getMode() == HAIER_AC_YRW02_HEAT)
|
||||
newstate = HAIER_AC_YRW02_SWING_BOTTOM;
|
||||
|
||||
// BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE.
|
||||
if (state == HAIER_AC_YRW02_SWING_BOTTOM &&
|
||||
getMode() != HAIER_AC_YRW02_HEAT)
|
||||
newstate = HAIER_AC_YRW02_SWING_MIDDLE;
|
||||
|
||||
remote_state[1] &= 0b11110000;
|
||||
remote_state[1] |= newstate;
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRHaierACYRW02::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRHaierACYRW02::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
uint8_t cmd = getButton();
|
||||
result += ", Button: " + uint64ToString(cmd) +" (";
|
||||
switch (cmd) {
|
||||
case HAIER_AC_YRW02_BUTTON_POWER:
|
||||
result += "Power";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_MODE:
|
||||
result += "Mode";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_FAN:
|
||||
result += "Fan";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_TEMP_UP:
|
||||
result += "Temp Up";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_TEMP_DOWN:
|
||||
result += "Temp Down";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_SLEEP:
|
||||
result += "Sleep";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_HEALTH:
|
||||
result += "Health";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_SWING:
|
||||
result += "Swing";
|
||||
break;
|
||||
case HAIER_AC_YRW02_BUTTON_TURBO:
|
||||
result += "Turbo";
|
||||
break;
|
||||
default:
|
||||
result += "Unknown";
|
||||
}
|
||||
result += ")";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case HAIER_AC_YRW02_AUTO:
|
||||
result += " (Auto)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_COOL:
|
||||
result += " (Cool)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_HEAT:
|
||||
result += " (Heat)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_DRY:
|
||||
result += " (Dry)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_FAN:
|
||||
result += " (Fan)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case HAIER_AC_YRW02_FAN_AUTO:
|
||||
result += " (Auto)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_FAN_HIGH:
|
||||
result += " (High)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_FAN_LOW:
|
||||
result += " (Low)";
|
||||
break;
|
||||
case HAIER_AC_YRW02_FAN_MED:
|
||||
result += " (Med)";
|
||||
break;
|
||||
default:
|
||||
result += " (Unknown)";
|
||||
}
|
||||
result += ", Turbo: " + uint64ToString(getTurbo()) + " (";
|
||||
switch (getTurbo()) {
|
||||
case HAIER_AC_YRW02_TURBO_OFF:
|
||||
result += "Off";
|
||||
break;
|
||||
case HAIER_AC_YRW02_TURBO_LOW:
|
||||
result += "Low";
|
||||
break;
|
||||
case HAIER_AC_YRW02_TURBO_HIGH:
|
||||
result += "High";
|
||||
break;
|
||||
default:
|
||||
result += "Unknown";
|
||||
}
|
||||
result += ")";
|
||||
result += ", Swing: " + uint64ToString(getSwing()) + " (";
|
||||
switch (getSwing()) {
|
||||
case HAIER_AC_YRW02_SWING_OFF:
|
||||
result += "Off";
|
||||
break;
|
||||
case HAIER_AC_YRW02_SWING_AUTO:
|
||||
result += "Auto";
|
||||
break;
|
||||
case HAIER_AC_YRW02_SWING_BOTTOM:
|
||||
result += "Bottom";
|
||||
break;
|
||||
case HAIER_AC_YRW02_SWING_DOWN:
|
||||
result += "Down";
|
||||
break;
|
||||
case HAIER_AC_YRW02_SWING_TOP:
|
||||
result += "Top";
|
||||
break;
|
||||
case HAIER_AC_YRW02_SWING_MIDDLE:
|
||||
result += "Middle";
|
||||
break;
|
||||
default:
|
||||
result += "Unknown";
|
||||
}
|
||||
result += ")";
|
||||
result += ", Sleep: ";
|
||||
if (getSleep())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Health: ";
|
||||
if (getHealth())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
|
||||
return result;
|
||||
}
|
||||
// End of IRHaierACYRW02 class.
|
||||
|
||||
|
||||
#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02)
|
||||
// Decode the supplied Haier HSU07-HEA03 remote message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically HAIER_AC_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Appears to be working.
|
||||
//
|
||||
bool IRrecv::decodeHaierAC(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
|
||||
if (strict) {
|
||||
if (nbits != HAIER_AC_BITS)
|
||||
return false; // Not strictly a HAIER_AC message.
|
||||
}
|
||||
|
||||
if (results->rawlen < (2 * nbits + HEADER) + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid HAIER_AC message.
|
||||
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR_GAP)) return false;
|
||||
|
||||
// Data
|
||||
for (uint16_t i = 0; i < nbits / 8; i++) {
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), 8,
|
||||
HAIER_AC_BIT_MARK,
|
||||
HAIER_AC_ONE_SPACE,
|
||||
HAIER_AC_BIT_MARK,
|
||||
HAIER_AC_ZERO_SPACE);
|
||||
if (data_result.success == false) return false;
|
||||
offset += data_result.used;
|
||||
results->state[i] = (uint8_t) data_result.data;
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], HAIER_AC_BIT_MARK)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset++], HAIER_AC_MIN_GAP))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (results->state[0] != HAIER_AC_PREFIX) return false;
|
||||
if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = HAIER_AC;
|
||||
results->bits = nbits;
|
||||
return true;
|
||||
}
|
||||
#endif // (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02)
|
||||
|
||||
#if DECODE_HAIER_AC_YRW02
|
||||
// Decode the supplied Haier YR-W02 remote A/C message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically HAIER_AC_YRW02_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Appears to be working.
|
||||
//
|
||||
bool IRrecv::decodeHaierACYRW02(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (strict) {
|
||||
if (nbits != HAIER_AC_YRW02_BITS)
|
||||
return false; // Not strictly a HAIER_AC_YRW02 message.
|
||||
}
|
||||
|
||||
// The protocol is almost exactly the same as HAIER_AC
|
||||
if (!decodeHaierAC(results, nbits, false))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (results->state[0] != HAIER_AC_YRW02_PREFIX) return false;
|
||||
if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
// It looks correct, but we haven't check the checksum etc.
|
||||
results->decode_type = HAIER_AC_YRW02;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_HAIER_AC_YRW02
|
||||
@@ -0,0 +1,244 @@
|
||||
// Copyright 2018 crankyoldgit
|
||||
// The specifics of reverse engineering the protocol details by kuzin2006
|
||||
|
||||
#ifndef IR_HAIER_H_
|
||||
#define IR_HAIER_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// HH HH AAA IIIII EEEEEEE RRRRRR
|
||||
// HH HH AAAAA III EE RR RR
|
||||
// HHHHHHH AA AA III EEEEE RRRRRR
|
||||
// HH HH AAAAAAA III EE RR RR
|
||||
// HH HH AA AA IIIII EEEEEEE RR RR
|
||||
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/404
|
||||
// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/485
|
||||
// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
|
||||
|
||||
// Constants
|
||||
|
||||
// Haier HSU07-HEA03 remote
|
||||
// Byte 0
|
||||
#define HAIER_AC_PREFIX 0b10100101
|
||||
|
||||
// Byte 1
|
||||
#define HAIER_AC_MIN_TEMP 16
|
||||
#define HAIER_AC_MAX_TEMP 30
|
||||
#define HAIER_AC_DEF_TEMP 25
|
||||
|
||||
#define HAIER_AC_CMD_OFF 0b00000000
|
||||
#define HAIER_AC_CMD_ON 0b00000001
|
||||
#define HAIER_AC_CMD_MODE 0b00000010
|
||||
#define HAIER_AC_CMD_FAN 0b00000011
|
||||
#define HAIER_AC_CMD_TEMP_UP 0b00000110
|
||||
#define HAIER_AC_CMD_TEMP_DOWN 0b00000111
|
||||
#define HAIER_AC_CMD_SLEEP 0b00001000
|
||||
#define HAIER_AC_CMD_TIMER_SET 0b00001001
|
||||
#define HAIER_AC_CMD_TIMER_CANCEL 0b00001010
|
||||
#define HAIER_AC_CMD_HEALTH 0b00001100
|
||||
#define HAIER_AC_CMD_SWING 0b00001101
|
||||
|
||||
// Byte 2
|
||||
#define HAIER_AC_SWING_OFF 0b00000000
|
||||
#define HAIER_AC_SWING_UP 0b00000001
|
||||
#define HAIER_AC_SWING_DOWN 0b00000010
|
||||
#define HAIER_AC_SWING_CHG 0b00000011
|
||||
|
||||
// Byte 6
|
||||
#define HAIER_AC_AUTO 0
|
||||
#define HAIER_AC_COOL 1
|
||||
#define HAIER_AC_DRY 2
|
||||
#define HAIER_AC_HEAT 3
|
||||
#define HAIER_AC_FAN 4
|
||||
|
||||
#define HAIER_AC_FAN_AUTO 0
|
||||
#define HAIER_AC_FAN_LOW 1
|
||||
#define HAIER_AC_FAN_MED 2
|
||||
#define HAIER_AC_FAN_HIGH 3
|
||||
|
||||
#define HAIER_AC_MAX_TIME (23 * 60 + 59)
|
||||
|
||||
// Haier YRW02 remote
|
||||
// Byte 0
|
||||
#define HAIER_AC_YRW02_PREFIX 0xA6
|
||||
|
||||
// Byte 1
|
||||
// Bits 0-3
|
||||
// 0x0 = 16DegC, ... 0xE = 30DegC
|
||||
// Bits 4-7 - Swing
|
||||
#define HAIER_AC_YRW02_SWING_OFF 0x0
|
||||
#define HAIER_AC_YRW02_SWING_TOP 0x1
|
||||
#define HAIER_AC_YRW02_SWING_MIDDLE 0x2 // Not available in heat mode.
|
||||
#define HAIER_AC_YRW02_SWING_BOTTOM 0x3 // Only available in heat mode.
|
||||
#define HAIER_AC_YRW02_SWING_DOWN 0xA
|
||||
#define HAIER_AC_YRW02_SWING_AUTO 0xC // Airflow
|
||||
|
||||
|
||||
// Byte 3
|
||||
// Bit 7 - Health mode
|
||||
|
||||
// Byte 4
|
||||
#define HAIER_AC_YRW02_POWER 0b01000000
|
||||
|
||||
// Byte 5
|
||||
// Bits 0-3
|
||||
#define HAIER_AC_YRW02_FAN_HIGH 0x2
|
||||
#define HAIER_AC_YRW02_FAN_MED 0x4
|
||||
#define HAIER_AC_YRW02_FAN_LOW 0x6
|
||||
#define HAIER_AC_YRW02_FAN_AUTO 0xA
|
||||
|
||||
|
||||
// Byte 6
|
||||
// Bits 0-1
|
||||
#define HAIER_AC_YRW02_TURBO_OFF 0x0
|
||||
#define HAIER_AC_YRW02_TURBO_HIGH 0x1
|
||||
#define HAIER_AC_YRW02_TURBO_LOW 0x2
|
||||
|
||||
// Byte 7
|
||||
// Bits 0-3
|
||||
#define HAIER_AC_YRW02_AUTO 0x0
|
||||
#define HAIER_AC_YRW02_COOL 0x2
|
||||
#define HAIER_AC_YRW02_DRY 0x4
|
||||
#define HAIER_AC_YRW02_HEAT 0x8
|
||||
#define HAIER_AC_YRW02_FAN 0xC
|
||||
|
||||
// Byte 8
|
||||
#define HAIER_AC_YRW02_SLEEP 0b10000000
|
||||
|
||||
// Byte 12
|
||||
// Bits 4-7
|
||||
#define HAIER_AC_YRW02_BUTTON_TEMP_UP 0x0
|
||||
#define HAIER_AC_YRW02_BUTTON_TEMP_DOWN 0x1
|
||||
#define HAIER_AC_YRW02_BUTTON_SWING 0x2
|
||||
#define HAIER_AC_YRW02_BUTTON_FAN 0x4
|
||||
#define HAIER_AC_YRW02_BUTTON_POWER 0x5
|
||||
#define HAIER_AC_YRW02_BUTTON_MODE 0x6
|
||||
#define HAIER_AC_YRW02_BUTTON_HEALTH 0x7
|
||||
#define HAIER_AC_YRW02_BUTTON_TURBO 0x8
|
||||
#define HAIER_AC_YRW02_BUTTON_SLEEP 0xB
|
||||
|
||||
|
||||
class IRHaierAC {
|
||||
public:
|
||||
explicit IRHaierAC(uint16_t pin);
|
||||
|
||||
#if SEND_HAIER_AC
|
||||
void send();
|
||||
#endif // SEND_HAIER_AC
|
||||
void begin();
|
||||
|
||||
void setCommand(const uint8_t command);
|
||||
uint8_t getCommand();
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan();
|
||||
|
||||
uint8_t getMode();
|
||||
void setMode(const uint8_t mode);
|
||||
|
||||
bool getSleep();
|
||||
void setSleep(const bool state);
|
||||
bool getHealth();
|
||||
void setHealth(const bool state);
|
||||
|
||||
int16_t getOnTimer();
|
||||
void setOnTimer(const uint16_t mins);
|
||||
int16_t getOffTimer();
|
||||
void setOffTimer(const uint16_t mins);
|
||||
void cancelTimers();
|
||||
|
||||
uint16_t getCurrTime();
|
||||
void setCurrTime(const uint16_t mins);
|
||||
|
||||
uint8_t getSwing();
|
||||
void setSwing(const uint8_t state);
|
||||
|
||||
uint8_t* getRaw();
|
||||
void setRaw(uint8_t new_code[]);
|
||||
static bool validChecksum(uint8_t state[],
|
||||
const uint16_t length = HAIER_AC_STATE_LENGTH);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
static String timeToString(const uint16_t nr_mins);
|
||||
#else
|
||||
std::string toString();
|
||||
static std::string timeToString(const uint16_t nr_mins);
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t remote_state[HAIER_AC_STATE_LENGTH];
|
||||
void stateReset();
|
||||
void checksum();
|
||||
static uint16_t getTime(const uint8_t ptr[]);
|
||||
static void setTime(uint8_t ptr[], const uint16_t nr_mins);
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
|
||||
class IRHaierACYRW02 {
|
||||
public:
|
||||
explicit IRHaierACYRW02(uint16_t pin);
|
||||
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void send();
|
||||
#endif // SEND_HAIER_AC_YRW02
|
||||
void begin();
|
||||
|
||||
void setButton(const uint8_t button);
|
||||
uint8_t getButton();
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan();
|
||||
|
||||
uint8_t getMode();
|
||||
void setMode(const uint8_t mode);
|
||||
|
||||
bool getPower();
|
||||
void setPower(const bool state);
|
||||
void on();
|
||||
void off();
|
||||
|
||||
bool getSleep();
|
||||
void setSleep(const bool state);
|
||||
bool getHealth();
|
||||
void setHealth(const bool state);
|
||||
|
||||
uint8_t getTurbo();
|
||||
void setTurbo(const uint8_t speed);
|
||||
|
||||
uint8_t getSwing();
|
||||
void setSwing(const uint8_t state);
|
||||
|
||||
uint8_t* getRaw();
|
||||
void setRaw(uint8_t new_code[]);
|
||||
static bool validChecksum(uint8_t state[],
|
||||
const uint16_t length = HAIER_AC_YRW02_STATE_LENGTH);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t remote_state[HAIER_AC_YRW02_STATE_LENGTH];
|
||||
void stateReset();
|
||||
void checksum();
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_HAIER_H_
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2018 David Conran
|
||||
//
|
||||
// Code to emulate Hitachi protocol compatible devices.
|
||||
// Should be compatible with:
|
||||
// * Hitachi RAS-35THA6 remote
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII
|
||||
// HH HH III TTT AAAAA CC C HH HH III
|
||||
// HHHHHHH III TTT AA AA CC HHHHHHH III
|
||||
// HH HH III TTT AAAAAAA CC C HH HH III
|
||||
// HH HH IIIII TTT AA AA CCCCC HH HH IIIII
|
||||
|
||||
// Constants
|
||||
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417
|
||||
#define HITACHI_AC_HDR_MARK 3300U
|
||||
#define HITACHI_AC_HDR_SPACE 1700U
|
||||
#define HITACHI_AC1_HDR_MARK 3400U
|
||||
#define HITACHI_AC1_HDR_SPACE 3400U
|
||||
#define HITACHI_AC_BIT_MARK 400U
|
||||
#define HITACHI_AC_ONE_SPACE 1250U
|
||||
#define HITACHI_AC_ZERO_SPACE 500U
|
||||
#define HITACHI_AC_MIN_GAP 100000U // Completely made up value.
|
||||
|
||||
#if (SEND_HITACHI_AC || SEND_HITACHI_AC2)
|
||||
// Send a Hitachi A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/417
|
||||
void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < HITACHI_AC_STATE_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
sendGeneric(HITACHI_AC_HDR_MARK, HITACHI_AC_HDR_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP,
|
||||
data, nbytes, 38, true, repeat, 50);
|
||||
}
|
||||
#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2)
|
||||
|
||||
#if SEND_HITACHI_AC1
|
||||
// Send a Hitachi A/C 13-byte message.
|
||||
//
|
||||
// For devices:
|
||||
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC1_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: BETA / Appears to work.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/453
|
||||
// Basically the same as sendHitatchiAC() except different size and header.
|
||||
void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < HITACHI_AC1_STATE_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
sendGeneric(HITACHI_AC1_HDR_MARK, HITACHI_AC1_HDR_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE,
|
||||
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP,
|
||||
data, nbytes, 38, true, repeat, 50);
|
||||
}
|
||||
#endif // SEND_HITACHI_AC1
|
||||
|
||||
#if SEND_HITACHI_AC2
|
||||
// Send a Hitachi A/C 53-byte message.
|
||||
//
|
||||
// For devices:
|
||||
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC2_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: BETA / Appears to work.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/417
|
||||
// Basically the same as sendHitatchiAC() except different size.
|
||||
void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < HITACHI_AC2_STATE_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
sendHitachiAC(data, nbytes, repeat);
|
||||
}
|
||||
#endif // SEND_HITACHI_AC2
|
||||
|
||||
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2)
|
||||
// Decode the supplied Hitachi A/C message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect.
|
||||
// Typically HITACHI_AC_BITS, HITACHI_AC1_BITS, HITACHI_AC2_BITS
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
//
|
||||
// Supported devices:
|
||||
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/417
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/453
|
||||
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
const uint8_t kTolerance = 30;
|
||||
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid HitachiAC message.
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case HITACHI_AC_BITS:
|
||||
case HITACHI_AC1_BITS:
|
||||
case HITACHI_AC2_BITS:
|
||||
break; // Okay to continue.
|
||||
default:
|
||||
return false; // Not strictly a Hitachi message.
|
||||
}
|
||||
}
|
||||
uint16_t offset = OFFSET_START;
|
||||
uint16_t dataBitsSoFar = 0;
|
||||
match_result_t data_result;
|
||||
|
||||
// Header
|
||||
if (nbits == HITACHI_AC1_BITS) {
|
||||
if (!matchMark(results->rawbuf[offset++], HITACHI_AC1_HDR_MARK, kTolerance))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC1_HDR_SPACE,
|
||||
kTolerance))
|
||||
return false;
|
||||
} else { // Everything else.
|
||||
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_HDR_MARK, kTolerance))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC_HDR_SPACE,
|
||||
kTolerance))
|
||||
return false;
|
||||
}
|
||||
// Data
|
||||
// Keep reading bytes until we either run out of message or state to fill.
|
||||
for (uint16_t i = 0;
|
||||
offset <= results->rawlen - 16 && i < nbits / 8;
|
||||
i++, dataBitsSoFar += 8, offset += data_result.used) {
|
||||
data_result = matchData(&(results->rawbuf[offset]), 8,
|
||||
HITACHI_AC_BIT_MARK,
|
||||
HITACHI_AC_ONE_SPACE,
|
||||
HITACHI_AC_BIT_MARK,
|
||||
HITACHI_AC_ZERO_SPACE,
|
||||
kTolerance);
|
||||
if (data_result.success == false) break; // Fail
|
||||
results->state[i] = (uint8_t) data_result.data;
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK, kTolerance))
|
||||
return false;
|
||||
if (offset <= results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP, kTolerance))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Re-check we got the correct size/length due to the way we read the data.
|
||||
switch (dataBitsSoFar / 8) {
|
||||
case HITACHI_AC_STATE_LENGTH:
|
||||
case HITACHI_AC1_STATE_LENGTH:
|
||||
case HITACHI_AC2_STATE_LENGTH:
|
||||
break; // Continue
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
switch (dataBitsSoFar) {
|
||||
case HITACHI_AC1_BITS:
|
||||
results->decode_type = HITACHI_AC1;
|
||||
break;
|
||||
case HITACHI_AC2_BITS:
|
||||
results->decode_type = HITACHI_AC2;
|
||||
break;
|
||||
case HITACHI_AC_BITS:
|
||||
default:
|
||||
results->decode_type = HITACHI_AC;
|
||||
}
|
||||
results->bits = dataBitsSoFar;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2)
|
||||
@@ -0,0 +1,170 @@
|
||||
// Copyright 2015 Kristian Lauszus
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtimer.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// JJJJJ V V CCCC
|
||||
// J V V C
|
||||
// J V V C
|
||||
// J J V V C
|
||||
// J V CCCC
|
||||
|
||||
// JVC originally added by Kristian Lauszus
|
||||
// (Thanks to zenwheel and other people at the original blog post)
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/jvc.php
|
||||
#define JVC_TICK 75U
|
||||
#define JVC_HDR_MARK_TICKS 112U
|
||||
#define JVC_HDR_MARK (JVC_HDR_MARK_TICKS * JVC_TICK)
|
||||
#define JVC_HDR_SPACE_TICKS 56U
|
||||
#define JVC_HDR_SPACE (JVC_HDR_SPACE_TICKS * JVC_TICK)
|
||||
#define JVC_BIT_MARK_TICKS 7U
|
||||
#define JVC_BIT_MARK (JVC_BIT_MARK_TICKS * JVC_TICK)
|
||||
#define JVC_ONE_SPACE_TICKS 23U
|
||||
#define JVC_ONE_SPACE (JVC_ONE_SPACE_TICKS * JVC_TICK)
|
||||
#define JVC_ZERO_SPACE_TICKS 7U
|
||||
#define JVC_ZERO_SPACE (JVC_ZERO_SPACE_TICKS * JVC_TICK)
|
||||
#define JVC_RPT_LENGTH_TICKS 800U
|
||||
#define JVC_RPT_LENGTH (JVC_RPT_LENGTH_TICKS * JVC_TICK)
|
||||
#define JVC_MIN_GAP_TICKS (JVC_RPT_LENGTH_TICKS - \
|
||||
(JVC_HDR_MARK_TICKS + JVC_HDR_SPACE_TICKS + \
|
||||
JVC_BITS * (JVC_BIT_MARK_TICKS + JVC_ONE_SPACE_TICKS) + \
|
||||
JVC_BIT_MARK_TICKS))
|
||||
#define JVC_MIN_GAP (JVC_MIN_GAP_TICKS * JVC_TICK)
|
||||
|
||||
#if SEND_JVC
|
||||
// Send a JVC message.
|
||||
//
|
||||
// Args:
|
||||
// data: The contents of the command you want to send.
|
||||
// nbits: The bit size of the command being sent. (JVC_BITS)
|
||||
// repeat: The number of times you want the command to be repeated.
|
||||
//
|
||||
// Status: STABLE.
|
||||
//
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/jvc.php
|
||||
void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle.
|
||||
enableIROut(38, 33);
|
||||
|
||||
IRtimer usecs = IRtimer();
|
||||
// Header
|
||||
// Only sent for the first message.
|
||||
mark(JVC_HDR_MARK);
|
||||
space(JVC_HDR_SPACE);
|
||||
|
||||
// We always send the data & footer at least once, hence '<= repeat'.
|
||||
for (uint16_t i = 0; i <= repeat; i++) {
|
||||
sendGeneric(0, 0, // No Header
|
||||
JVC_BIT_MARK, JVC_ONE_SPACE,
|
||||
JVC_BIT_MARK, JVC_ZERO_SPACE,
|
||||
JVC_BIT_MARK, JVC_MIN_GAP,
|
||||
data, nbits, 38, true, 0, // Repeats are handles elsewhere.
|
||||
33);
|
||||
// Wait till the end of the repeat time window before we send another code.
|
||||
uint32_t elapsed = usecs.elapsed();
|
||||
// Avoid potential unsigned integer underflow.
|
||||
// e.g. when elapsed > JVC_RPT_LENGTH.
|
||||
if (elapsed < JVC_RPT_LENGTH)
|
||||
space(JVC_RPT_LENGTH - elapsed);
|
||||
usecs.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the raw JVC data based on address and command.
|
||||
//
|
||||
// Args:
|
||||
// address: An 8-bit address value.
|
||||
// command: An 8-bit command value.
|
||||
// Returns:
|
||||
// A raw JVC message.
|
||||
//
|
||||
// Status: BETA / Should work fine.
|
||||
//
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/jvc.php
|
||||
uint16_t IRsend::encodeJVC(uint8_t address, uint8_t command) {
|
||||
return reverseBits((command << 8) | address, 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_JVC
|
||||
// Decode the supplied JVC message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits of data to expect. Typically JVC_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: STABLE
|
||||
//
|
||||
// Note:
|
||||
// JVC repeat codes don't have a header.
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/jvc.php
|
||||
bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) {
|
||||
if (strict && nbits != JVC_BITS)
|
||||
return false; // Must be called with the correct nr. of bits.
|
||||
if (results->rawlen < 2 * nbits + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid JVC message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
bool isRepeat = true;
|
||||
|
||||
uint32_t m_tick;
|
||||
uint32_t s_tick;
|
||||
// Header
|
||||
// (Optional as repeat codes don't have the header)
|
||||
if (matchMark(results->rawbuf[offset], JVC_HDR_MARK)) {
|
||||
isRepeat = false;
|
||||
m_tick = results->rawbuf[offset++] * RAWTICK / JVC_HDR_MARK_TICKS;
|
||||
if (results->rawlen < 2 * nbits + 4)
|
||||
return false; // Can't possibly be a valid JVC message with a header.
|
||||
if (!matchSpace(results->rawbuf[offset], JVC_HDR_SPACE))
|
||||
return false;
|
||||
s_tick = results->rawbuf[offset++] * RAWTICK / JVC_HDR_SPACE_TICKS;
|
||||
} else {
|
||||
// We can't easily auto-calibrate as there is no header, so assume
|
||||
// the default tick time.
|
||||
m_tick = JVC_TICK;
|
||||
s_tick = JVC_TICK;
|
||||
}
|
||||
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
JVC_BIT_MARK_TICKS * m_tick,
|
||||
JVC_ONE_SPACE_TICKS * s_tick,
|
||||
JVC_BIT_MARK_TICKS * m_tick,
|
||||
JVC_ZERO_SPACE_TICKS * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], JVC_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], JVC_MIN_GAP_TICKS * s_tick))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = JVC;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
// command & address are transmitted LSB first, so we need to reverse them.
|
||||
results->address = reverseBits(data >> 8, 8); // The first 8 bits sent.
|
||||
results->command = reverseBits(data & 0xFF, 8); // The last 8 bits sent.
|
||||
results->repeat = isRepeat;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,562 @@
|
||||
// Copyright 2016 David Conran
|
||||
//
|
||||
// Code to emulate IR Kelvinator YALIF remote control unit, which should control
|
||||
// at least the following Kelvinator A/C units:
|
||||
// KSV26CRC, KSV26HRC, KSV35CRC, KSV35HRC, KSV53HRC, KSV62HRC, KSV70CRC,
|
||||
// KSV70HRC, KSV80HRC.
|
||||
//
|
||||
// Note:
|
||||
// * Unsupported:
|
||||
// - All Sleep modes.
|
||||
// - All Timer modes.
|
||||
// - "I Feel" button & mode.
|
||||
// - Energy Saving mode.
|
||||
// - Low Heat mode.
|
||||
// - Fahrenheit.
|
||||
|
||||
#include "ir_Kelvinator.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR
|
||||
// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR
|
||||
// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR
|
||||
// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR
|
||||
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
|
||||
|
||||
// Constants
|
||||
#define KELVINATOR_TICK 85U
|
||||
#define KELVINATOR_HDR_MARK_TICKS 106U
|
||||
#define KELVINATOR_HDR_MARK (KELVINATOR_HDR_MARK_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_HDR_SPACE_TICKS 53U
|
||||
#define KELVINATOR_HDR_SPACE (KELVINATOR_HDR_SPACE_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_BIT_MARK_TICKS 8U
|
||||
#define KELVINATOR_BIT_MARK (KELVINATOR_BIT_MARK_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_ONE_SPACE_TICKS 18U
|
||||
#define KELVINATOR_ONE_SPACE (KELVINATOR_ONE_SPACE_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_ZERO_SPACE_TICKS 6U
|
||||
#define KELVINATOR_ZERO_SPACE (KELVINATOR_ZERO_SPACE_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_GAP_SPACE_TICKS 235U
|
||||
#define KELVINATOR_GAP_SPACE (KELVINATOR_GAP_SPACE_TICKS * KELVINATOR_TICK)
|
||||
#define KELVINATOR_CMD_FOOTER 2U
|
||||
#define KELVINATOR_CMD_FOOTER_BITS 3U
|
||||
|
||||
#define KELVINATOR_POWER 8U
|
||||
#define KELVINATOR_MODE_MASK 0xF8U
|
||||
#define KELVINATOR_FAN_OFFSET 4U
|
||||
#define KELVINATOR_BASIC_FAN_MASK uint8_t(0xFFU ^ (3U << KELVINATOR_FAN_OFFSET))
|
||||
#define KELVINATOR_FAN_MASK uint8_t(0xFFU ^ (7U << KELVINATOR_FAN_OFFSET))
|
||||
#define KELVINATOR_CHECKSUM_START 10U
|
||||
#define KELVINATOR_VENT_SWING_OFFSET 6U
|
||||
#define KELVINATOR_VENT_SWING uint8_t(1U << KELVINATOR_VENT_SWING_OFFSET)
|
||||
#define KELVINATOR_VENT_SWING_V uint8_t(1U)
|
||||
#define KELVINATOR_VENT_SWING_H uint8_t(1U << 4)
|
||||
#define KELVINATOR_SLEEP_1_AND_3 uint8_t(1U << 7)
|
||||
#define KELVINATOR_QUIET_OFFSET 7U
|
||||
#define KELVINATOR_QUIET uint8_t(1U << KELVINATOR_QUIET_OFFSET)
|
||||
#define KELVINATOR_ION_FILTER_OFFSET 6U
|
||||
#define KELVINATOR_ION_FILTER uint8_t(1U << KELVINATOR_ION_FILTER_OFFSET)
|
||||
#define KELVINATOR_LIGHT_OFFSET 5U
|
||||
#define KELVINATOR_LIGHT uint8_t(1U << KELVINATOR_LIGHT_OFFSET)
|
||||
#define KELVINATOR_XFAN_OFFSET 7U
|
||||
#define KELVINATOR_XFAN uint8_t(1U << KELVINATOR_XFAN_OFFSET)
|
||||
#define KELVINATOR_TURBO_OFFSET 4U
|
||||
#define KELVINATOR_TURBO uint8_t(1U << KELVINATOR_TURBO_OFFSET)
|
||||
|
||||
#if SEND_KELVINATOR
|
||||
// Send a Kelvinator A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: An array of bytes containing the IR command.
|
||||
// nbytes: Nr. of bytes of data in the array. (>=KELVINATOR_STATE_LENGTH)
|
||||
// repeat: Nr. of times the message is to be repeated. (Default = 0).
|
||||
//
|
||||
// Status: STABLE / Known working.
|
||||
//
|
||||
void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat) {
|
||||
if (nbytes < KELVINATOR_STATE_LENGTH)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Command Block #1 (4 bytes)
|
||||
sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
0, 0, // No Footer yet.
|
||||
data, 4, 38, false, 0, 50);
|
||||
// Send Footer for the command block (3 bits (B010))
|
||||
sendGeneric(0, 0, // No Header
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE,
|
||||
KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS,
|
||||
38, false, 0, 50);
|
||||
// Data Block #1 (4 bytes)
|
||||
sendGeneric(0, 0, // No header
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2,
|
||||
data + 4, 4, 38, false, 0, 50);
|
||||
// Command Block #2 (4 bytes)
|
||||
sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
0, 0, // No Footer yet.
|
||||
data + 8, 4, 38, false, 0, 50);
|
||||
// Send Footer for the command block (3 bits (B010))
|
||||
sendGeneric(0, 0, // No Header
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE,
|
||||
KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS,
|
||||
38, false, 0, 50);
|
||||
// Data Block #2 (4 bytes)
|
||||
sendGeneric(0, 0, // No header
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
|
||||
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2,
|
||||
data + 12, 4, 38, false, 0, 50);
|
||||
}
|
||||
}
|
||||
#endif // SEND_KELVINATOR
|
||||
|
||||
IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::stateReset() {
|
||||
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++)
|
||||
remote_state[i] = 0x0;
|
||||
remote_state[3] = 0x50;
|
||||
remote_state[11] = 0x70;
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::fixup() {
|
||||
// X-Fan mode is only valid in COOL or DRY modes.
|
||||
if (getMode() != KELVINATOR_COOL && getMode() != KELVINATOR_DRY)
|
||||
setXFan(false);
|
||||
checksum(); // Calculate the checksums
|
||||
}
|
||||
|
||||
#if SEND_KELVINATOR
|
||||
void IRKelvinatorAC::send() {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
_irsend.sendKelvinator(remote_state);
|
||||
}
|
||||
#endif // SEND_KELVINATOR
|
||||
|
||||
uint8_t* IRKelvinatorAC::getRaw() {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setRaw(uint8_t new_code[]) {
|
||||
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++) {
|
||||
remote_state[i] = new_code[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block,
|
||||
const uint16_t length) {
|
||||
uint8_t sum = KELVINATOR_CHECKSUM_START;
|
||||
// Sum the lower half of the first 4 bytes of this block.
|
||||
for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++)
|
||||
sum += (*block & 0x0FU);
|
||||
// then sum the upper half of the next 3 bytes.
|
||||
for (uint8_t i = 4; i < length - 1; i++, block++)
|
||||
sum += (*block >> 4);
|
||||
// Trim it down to fit into the 4 bits allowed. i.e. Mod 16.
|
||||
return sum & 0x0FU;
|
||||
}
|
||||
|
||||
// Many Bothans died to bring us this information.
|
||||
void IRKelvinatorAC::checksum(const uint16_t length) {
|
||||
// For each command + options block.
|
||||
for (uint16_t offset = 0; offset + 7 < length; offset += 8) {
|
||||
uint8_t sum = calcBlockChecksum(remote_state + offset);
|
||||
remote_state[7 + offset] = (sum << 4) | (remote_state[7 + offset] & 0xFU);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the checksum is valid for a given state.
|
||||
// Args:
|
||||
// state: The array to verify the checksum of.
|
||||
// length: The size of the state.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRKelvinatorAC::validChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
for (uint16_t offset = 0; offset + 7 < length; offset += 8) {
|
||||
// Top 4 bits of the last byte in the block is the block's checksum.
|
||||
if (state[offset + 7] >> 4 != calcBlockChecksum(state + offset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::on() {
|
||||
remote_state[0] |= KELVINATOR_POWER;
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::off() {
|
||||
remote_state[0] &= ~KELVINATOR_POWER;
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setPower(bool state) {
|
||||
if (state)
|
||||
on();
|
||||
else
|
||||
off();
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getPower() {
|
||||
return ((remote_state[0] & KELVINATOR_POWER) != 0);
|
||||
}
|
||||
|
||||
// Set the temp. in deg C
|
||||
void IRKelvinatorAC::setTemp(uint8_t temp) {
|
||||
temp = std::max((uint8_t) KELVINATOR_MIN_TEMP, temp);
|
||||
temp = std::min((uint8_t) KELVINATOR_MAX_TEMP, temp);
|
||||
remote_state[1] = (remote_state[1] & 0xF0U) | (temp - KELVINATOR_MIN_TEMP);
|
||||
remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
// Return the set temp. in deg C
|
||||
uint8_t IRKelvinatorAC::getTemp() {
|
||||
return ((remote_state[1] & 0xFU) + KELVINATOR_MIN_TEMP);
|
||||
}
|
||||
|
||||
// Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed
|
||||
void IRKelvinatorAC::setFan(uint8_t fan) {
|
||||
fan = std::min((uint8_t) KELVINATOR_FAN_MAX, fan); // Bounds check
|
||||
|
||||
// Only change things if we need to.
|
||||
if (fan != getFan()) {
|
||||
// Set the basic fan values.
|
||||
uint8_t fan_basic = std::min((uint8_t) KELVINATOR_BASIC_FAN_MAX, fan);
|
||||
remote_state[0] = (remote_state[0] & KELVINATOR_BASIC_FAN_MASK) |
|
||||
(fan_basic << KELVINATOR_FAN_OFFSET);
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
// Set the advanced(?) fan value.
|
||||
remote_state[14] = (remote_state[14] & KELVINATOR_FAN_MASK) |
|
||||
(fan << KELVINATOR_FAN_OFFSET);
|
||||
setTurbo(false); // Turbo mode is turned off if we change the fan settings.
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRKelvinatorAC::getFan() {
|
||||
return ((remote_state[14] & ~KELVINATOR_FAN_MASK) >> KELVINATOR_FAN_OFFSET);
|
||||
}
|
||||
|
||||
uint8_t IRKelvinatorAC::getMode() {
|
||||
return (remote_state[0] & ~KELVINATOR_MODE_MASK);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setMode(uint8_t mode) {
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
if (mode > KELVINATOR_HEAT) mode = KELVINATOR_AUTO;
|
||||
remote_state[0] = (remote_state[0] & KELVINATOR_MODE_MASK) | mode;
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
if (mode == KELVINATOR_AUTO || KELVINATOR_DRY)
|
||||
// When the remote is set to Auto or Dry, it defaults to 25C and doesn't
|
||||
// show it.
|
||||
setTemp(KELVINATOR_AUTO_TEMP);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setSwingVertical(bool state) {
|
||||
if (state) {
|
||||
remote_state[0] |= KELVINATOR_VENT_SWING;
|
||||
remote_state[4] |= KELVINATOR_VENT_SWING_V;
|
||||
} else {
|
||||
remote_state[4] &= ~KELVINATOR_VENT_SWING_V;
|
||||
if (!getSwingHorizontal())
|
||||
remote_state[0] &= ~KELVINATOR_VENT_SWING;
|
||||
}
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getSwingVertical() {
|
||||
return ((remote_state[4] & KELVINATOR_VENT_SWING_V) != 0);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setSwingHorizontal(bool state) {
|
||||
if (state) {
|
||||
remote_state[0] |= KELVINATOR_VENT_SWING;
|
||||
remote_state[4] |= KELVINATOR_VENT_SWING_H;
|
||||
} else {
|
||||
remote_state[4] &= ~KELVINATOR_VENT_SWING_H;
|
||||
if (!getSwingVertical())
|
||||
remote_state[0] &= ~KELVINATOR_VENT_SWING;
|
||||
}
|
||||
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getSwingHorizontal() {
|
||||
return ((remote_state[4] & KELVINATOR_VENT_SWING_H) != 0);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setQuiet(bool state) {
|
||||
remote_state[12] &= ~KELVINATOR_QUIET;
|
||||
remote_state[12] |= (state << KELVINATOR_QUIET_OFFSET);
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getQuiet() {
|
||||
return ((remote_state[12] & KELVINATOR_QUIET) != 0);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setIonFilter(bool state) {
|
||||
remote_state[2] &= ~KELVINATOR_ION_FILTER;
|
||||
remote_state[2] |= (state << KELVINATOR_ION_FILTER_OFFSET);
|
||||
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getIonFilter() {
|
||||
return ((remote_state[2] & KELVINATOR_ION_FILTER) != 0);
|
||||
}
|
||||
|
||||
void IRKelvinatorAC::setLight(bool state) {
|
||||
remote_state[2] &= ~KELVINATOR_LIGHT;
|
||||
remote_state[2] |= (state << KELVINATOR_LIGHT_OFFSET);
|
||||
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getLight() {
|
||||
return ((remote_state[2] & KELVINATOR_LIGHT) != 0);
|
||||
}
|
||||
|
||||
// Note: XFan mode is only valid in Cool or Dry mode.
|
||||
void IRKelvinatorAC::setXFan(bool state) {
|
||||
remote_state[2] &= ~KELVINATOR_XFAN;
|
||||
remote_state[2] |= (state << KELVINATOR_XFAN_OFFSET);
|
||||
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getXFan() {
|
||||
return ((remote_state[2] & KELVINATOR_XFAN) != 0);
|
||||
}
|
||||
|
||||
// Note: Turbo mode is turned off if the fan speed is changed.
|
||||
void IRKelvinatorAC::setTurbo(bool state) {
|
||||
remote_state[2] &= ~KELVINATOR_TURBO;
|
||||
remote_state[2] |= (state << KELVINATOR_TURBO_OFFSET);
|
||||
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
|
||||
}
|
||||
|
||||
bool IRKelvinatorAC::getTurbo() {
|
||||
return ((remote_state[2] & KELVINATOR_TURBO) != 0);
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRKelvinatorAC::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRKelvinatorAC::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case KELVINATOR_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case KELVINATOR_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case KELVINATOR_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case KELVINATOR_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case KELVINATOR_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case KELVINATOR_FAN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case KELVINATOR_FAN_MAX:
|
||||
result += " (MAX)";
|
||||
break;
|
||||
}
|
||||
result += ", Turbo: ";
|
||||
if (getTurbo())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Quiet: ";
|
||||
if (getQuiet())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", XFan: ";
|
||||
if (getXFan())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", IonFilter: ";
|
||||
if (getIonFilter())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Light: ";
|
||||
if (getLight())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Swing (Horizontal): ";
|
||||
if (getSwingHorizontal())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Swing (Vertical): ";
|
||||
if (getSwingVertical())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_KELVINATOR
|
||||
// Decode the supplied Kelvinator message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically KELVINATOR_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: ALPHA / Untested.
|
||||
bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (results->rawlen < 2 * (nbits + KELVINATOR_CMD_FOOTER_BITS) +
|
||||
(HEADER + FOOTER + 1) * 2 - 1)
|
||||
return false; // Can't possibly be a valid Kelvinator message.
|
||||
if (strict && nbits != KELVINATOR_BITS)
|
||||
return false; // Not strictly a Kelvinator message.
|
||||
|
||||
uint32_t data;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// There are two messages back-to-back in a full Kelvinator IR message
|
||||
// sequence.
|
||||
int8_t state_pos = 0;
|
||||
for (uint8_t s = 0; s < 2; s++) {
|
||||
match_result_t data_result;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], KELVINATOR_HDR_MARK)) return false;
|
||||
// Calculate how long the lowest tick time is based on the header mark.
|
||||
uint32_t mark_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
KELVINATOR_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], KELVINATOR_HDR_SPACE))
|
||||
return false;
|
||||
// Calculate how long the common tick time is based on the header space.
|
||||
uint32_t space_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
KELVINATOR_HDR_SPACE_TICKS;
|
||||
|
||||
// Data (Command) (32 bits)
|
||||
data_result = matchData(&(results->rawbuf[offset]), 32,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ONE_SPACE_TICKS * space_tick,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ZERO_SPACE_TICKS * space_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Record command data in the state.
|
||||
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8)
|
||||
results->state[i] = reverseBits(data & 0xFF, 8);
|
||||
state_pos += 4;
|
||||
|
||||
// Command data footer (3 bits, B010)
|
||||
data_result = matchData(&(results->rawbuf[offset]),
|
||||
KELVINATOR_CMD_FOOTER_BITS,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ONE_SPACE_TICKS * space_tick,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ZERO_SPACE_TICKS * space_tick);
|
||||
if (data_result.success == false) return false;
|
||||
if (data_result.data != KELVINATOR_CMD_FOOTER) return false;
|
||||
offset += data_result.used;
|
||||
|
||||
// Interdata gap.
|
||||
if (!matchMark(results->rawbuf[offset++],
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++],
|
||||
KELVINATOR_GAP_SPACE_TICKS * space_tick))
|
||||
return false;
|
||||
|
||||
// Data (Options) (32 bits)
|
||||
data_result = matchData(&(results->rawbuf[offset]), 32,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ONE_SPACE_TICKS * space_tick,
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick,
|
||||
KELVINATOR_ZERO_SPACE_TICKS * space_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Record option data in the state.
|
||||
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8)
|
||||
results->state[i] = reverseBits(data & 0xFF, 8);
|
||||
state_pos += 4;
|
||||
|
||||
// Inter-sequence gap. (Double length gap)
|
||||
if (!matchMark(results->rawbuf[offset++],
|
||||
KELVINATOR_BIT_MARK_TICKS * mark_tick))
|
||||
return false;
|
||||
if (s == 0) {
|
||||
if (!matchSpace(results->rawbuf[offset++],
|
||||
KELVINATOR_GAP_SPACE_TICKS * space_tick * 2))
|
||||
return false;
|
||||
} else {
|
||||
if (offset <= results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset],
|
||||
KELVINATOR_GAP_SPACE_TICKS * 2 * space_tick))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Correct size/length)
|
||||
if (state_pos != KELVINATOR_STATE_LENGTH) return false;
|
||||
// Verify the message's checksum is correct.
|
||||
if (!IRKelvinatorAC::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = KELVINATOR;
|
||||
results->bits = state_pos * 8;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_KELVINATOR
|
||||
@@ -0,0 +1,168 @@
|
||||
// Kelvinator A/C
|
||||
//
|
||||
// Copyright 2016 David Conran
|
||||
|
||||
#ifndef IR_KELVINATOR_H_
|
||||
#define IR_KELVINATOR_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR
|
||||
// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR
|
||||
// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR
|
||||
// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR
|
||||
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
|
||||
|
||||
// Constants
|
||||
#define KELVINATOR_AUTO 0U
|
||||
#define KELVINATOR_COOL 1U
|
||||
#define KELVINATOR_DRY 2U
|
||||
#define KELVINATOR_FAN 3U
|
||||
#define KELVINATOR_HEAT 4U
|
||||
#define KELVINATOR_BASIC_FAN_MAX 3U
|
||||
#define KELVINATOR_FAN_AUTO 0U
|
||||
#define KELVINATOR_FAN_MAX 5U
|
||||
#define KELVINATOR_MIN_TEMP 16U // 16C
|
||||
#define KELVINATOR_MAX_TEMP 30U // 30C
|
||||
#define KELVINATOR_AUTO_TEMP 25U // 25C
|
||||
|
||||
/*
|
||||
Kelvinator AC map
|
||||
|
||||
(header mark and space)
|
||||
byte 0 = Basic Modes
|
||||
b2-0 = Modes
|
||||
Modes:
|
||||
000 = Auto (temp = 25C)
|
||||
001 = Cool
|
||||
010 = Dry (temp = 25C, but not shown)
|
||||
011 = Fan
|
||||
100 = Heat
|
||||
b3 = Power Status (1 = On, 0 = Off)
|
||||
b5-4 = Fan (Basic modes)
|
||||
Fan:
|
||||
00 = Auto
|
||||
01 = Fan 1
|
||||
10 = Fan 2
|
||||
11 = Fan 3 or higher (See byte 14)
|
||||
b6 = Vent swing (1 = On, 0 = Off) (See byte 4)
|
||||
b7 = Sleep Modes 1 & 3 (1 = On, 0 = Off)
|
||||
byte 1 = Temperature
|
||||
b3-0: Degrees C.
|
||||
0000 (0) = 16C
|
||||
0001 (1) = 17C
|
||||
0010 (2) = 18C
|
||||
...
|
||||
1101 (13) = 29C
|
||||
1110 (14) = 30C
|
||||
byte 2 = Extras
|
||||
b3-0 = UNKNOWN, typically 0.
|
||||
b4 = Turbo Fan (1 = On, 0 = Off)
|
||||
b5 = Light (Display) (1 = On, 0 = Off)
|
||||
b6 = Ion Filter (1 = On, 0 = Off)
|
||||
b7 = X-Fan (Fan runs for a while after power off) (1 = On, 0 = Off)
|
||||
byte 3 = Section Indicator
|
||||
b3-0 = Unused (Typically 0)
|
||||
b5-4 = Unknown (possibly timer related) (Typically 0b01)
|
||||
b7-6 = End of command block (B01)
|
||||
(B010 marker and a gap of 20ms)
|
||||
byte 4 = Extended options
|
||||
b0 = Swing Vent Vertical (1 = On, 0 = Off)
|
||||
b4 = Swing Vent Horizontal (1 = On, 0 = Off)
|
||||
byte 5-6 = Timer related. Typically 0 except when timer in use.
|
||||
byte 7 = checksum
|
||||
b3-0 = Unknown (Used in Timer mode)
|
||||
b7-4 = checksum of the previous bytes (0-6)
|
||||
(gap of 40ms)
|
||||
(header mark and space)
|
||||
byte 8 = Repeat of byte 0
|
||||
byte 9 = Repeat of byte 1
|
||||
byte 10 = Repeat of byte 2
|
||||
byte 11 = Section Indicator
|
||||
b3-0 = Unused (Typically 0)
|
||||
b5-4 = Unknown (possibly timer related) (Typically 0b11)
|
||||
b7-6 = End of command block (B01)
|
||||
(B010 marker and a gap of 20ms)
|
||||
byte 12 = Extended options
|
||||
b0 = Sleep mode 2 (1 = On, 0=Off)
|
||||
b6-1 = Unknown (Used in Sleep Mode 3, Typically 0b000000)
|
||||
b7 = Quiet Mode (1 = On, 0=Off)
|
||||
byte 13 = Unknown (Sleep Mode 3 related, Typically 0x00)
|
||||
byte 14 = Fan control
|
||||
b3-0 = Unknown (Sleep Mode 3 related, Typically 0b0000)
|
||||
b6-4 = Fan speed
|
||||
0b000 (0) = Automatic
|
||||
0b001 (1) = Fan 1
|
||||
0b010 (2) = Fan 2
|
||||
0b011 (3) = Fan 3
|
||||
0b100 (4) = Fan 4
|
||||
0b101 (5) = Fan 5
|
||||
byte 15 = checksum
|
||||
b3-0 = Unknown (Typically 0b0000)
|
||||
b7-4 = checksum of the previous bytes (8-14)
|
||||
*/
|
||||
|
||||
// Classes
|
||||
class IRKelvinatorAC {
|
||||
public:
|
||||
explicit IRKelvinatorAC(uint16_t pin);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_KELVINATOR
|
||||
void send();
|
||||
#endif // SEND_KELVINATOR
|
||||
void begin();
|
||||
void on();
|
||||
void off();
|
||||
void setPower(bool state);
|
||||
bool getPower();
|
||||
void setTemp(uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
void setFan(uint8_t fan);
|
||||
uint8_t getFan();
|
||||
void setMode(uint8_t mode);
|
||||
uint8_t getMode();
|
||||
void setSwingVertical(bool state);
|
||||
bool getSwingVertical();
|
||||
void setSwingHorizontal(bool state);
|
||||
bool getSwingHorizontal();
|
||||
void setQuiet(bool state);
|
||||
bool getQuiet();
|
||||
void setIonFilter(bool state);
|
||||
bool getIonFilter();
|
||||
void setLight(bool state);
|
||||
bool getLight();
|
||||
void setXFan(bool state);
|
||||
bool getXFan();
|
||||
void setTurbo(bool state);
|
||||
bool getTurbo();
|
||||
uint8_t* getRaw();
|
||||
void setRaw(uint8_t new_code[]);
|
||||
static uint8_t calcBlockChecksum(
|
||||
const uint8_t *block,
|
||||
const uint16_t length = KELVINATOR_STATE_LENGTH / 2);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = KELVINATOR_STATE_LENGTH);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// The state of the IR remote in IR code form.
|
||||
uint8_t remote_state[KELVINATOR_STATE_LENGTH];
|
||||
void checksum(const uint16_t length = KELVINATOR_STATE_LENGTH);
|
||||
void fixup();
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_KELVINATOR_H_
|
||||
@@ -0,0 +1,218 @@
|
||||
// Copyright 2015 Darryl Smith
|
||||
// Copyright 2015 cheaplin
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "ir_LG.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// L GGGG
|
||||
// L G
|
||||
// L G GG
|
||||
// L G G
|
||||
// LLLLL GGG
|
||||
|
||||
// LG decode originally added by Darryl Smith (based on the JVC protocol)
|
||||
// LG send originally added by https://github.com/chaeplin
|
||||
|
||||
// Constants
|
||||
#define LG_TICK 50U
|
||||
#define LG_HDR_MARK_TICKS 170U // 8500
|
||||
#define LG_HDR_MARK (LG_HDR_MARK_TICKS * LG_TICK)
|
||||
#define LG_HDR_SPACE_TICKS 85U // 4250
|
||||
#define LG_HDR_SPACE (LG_HDR_SPACE_TICKS * LG_TICK)
|
||||
#define LG_BIT_MARK_TICKS 11U
|
||||
#define LG_BIT_MARK (LG_BIT_MARK_TICKS * LG_TICK)
|
||||
#define LG_ONE_SPACE_TICKS 32U
|
||||
#define LG_ONE_SPACE (LG_ONE_SPACE_TICKS * LG_TICK)
|
||||
#define LG_ZERO_SPACE_TICKS 11U
|
||||
#define LG_ZERO_SPACE (LG_ZERO_SPACE_TICKS * LG_TICK)
|
||||
#define LG_RPT_SPACE_TICKS 45U
|
||||
#define LG_RPT_SPACE (LG_RPT_SPACE_TICKS * LG_TICK)
|
||||
#define LG_MIN_GAP_TICKS 795U
|
||||
#define LG_MIN_GAP (LG_MIN_GAP_TICKS * LG_TICK)
|
||||
#define LG_MIN_MESSAGE_LENGTH_TICKS 2161U
|
||||
#define LG_MIN_MESSAGE_LENGTH (LG_MIN_MESSAGE_LENGTH_TICKS * LG_TICK)
|
||||
#define LG32_HDR_MARK_TICKS 90U
|
||||
#define LG32_HDR_MARK (LG32_HDR_MARK_TICKS * LG_TICK)
|
||||
#define LG32_HDR_SPACE_TICKS 89U
|
||||
#define LG32_HDR_SPACE (LG32_HDR_SPACE_TICKS * LG_TICK)
|
||||
#define LG32_RPT_HDR_MARK_TICKS 179U
|
||||
#define LG32_RPT_HDR_MARK (LG32_RPT_HDR_MARK_TICKS * LG_TICK)
|
||||
|
||||
#if (SEND_LG || DECODE_LG)
|
||||
// Calculate the rolling 4-bit wide checksum over all of the data.
|
||||
// Args:
|
||||
// data: The value to be checksum'ed.
|
||||
// Returns:
|
||||
// A 4-bit checksum.
|
||||
uint8_t calcLGChecksum(uint16_t data) {
|
||||
return(((data >> 12) + ((data >> 8) & 0xF) + ((data >> 4) & 0xF) +
|
||||
(data & 0xF)) & 0xF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SEND_LG
|
||||
// Send an LG formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The contents of the message you want to send.
|
||||
// nbits: The bit size of the message being sent.
|
||||
// Typically LG_BITS or LG32_BITS.
|
||||
// repeat: The number of times you want the message to be repeated.
|
||||
//
|
||||
// Status: Beta / Should be working.
|
||||
//
|
||||
// Notes:
|
||||
// LG has a separate message to indicate a repeat, like NEC does.
|
||||
void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
uint16_t repeatHeaderMark = 0;
|
||||
|
||||
if (nbits >= LG32_BITS) {
|
||||
// LG 32bit protocol is near identical to Samsung except for repeats.
|
||||
sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message.
|
||||
repeatHeaderMark = LG32_RPT_HDR_MARK;
|
||||
repeat++;
|
||||
} else {
|
||||
// LG (28-bit) protocol.
|
||||
repeatHeaderMark = LG_HDR_MARK;
|
||||
sendGeneric(LG_HDR_MARK, LG_HDR_SPACE,
|
||||
LG_BIT_MARK, LG_ONE_SPACE,
|
||||
LG_BIT_MARK, LG_ZERO_SPACE,
|
||||
LG_BIT_MARK,
|
||||
LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH,
|
||||
data, nbits, 38, true, 0, // Repeats are handled later.
|
||||
50);
|
||||
}
|
||||
|
||||
// Repeat
|
||||
// Protocol has a mandatory repeat-specific code sent after every command.
|
||||
if (repeat)
|
||||
sendGeneric(repeatHeaderMark, LG_RPT_SPACE,
|
||||
0, 0, 0, 0, // No data is sent.
|
||||
LG_BIT_MARK, LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH,
|
||||
0, 0, // No data.
|
||||
38, true, repeat - 1, 50);
|
||||
}
|
||||
|
||||
// Construct a raw 28-bit LG message from the supplied address & command.
|
||||
//
|
||||
// Args:
|
||||
// address: The address code.
|
||||
// command: The command code.
|
||||
// Returns:
|
||||
// A raw 28-bit LG message suitable for sendLG().
|
||||
//
|
||||
// Status: BETA / Should work.
|
||||
//
|
||||
// Notes:
|
||||
// e.g. Sequence of bits = address + command + checksum.
|
||||
uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) {
|
||||
return ((address << 20) | (command << 4) | calcLGChecksum(command));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_LG
|
||||
// Decode the supplied LG message.
|
||||
// LG protocol has a repeat code which is 4 items long.
|
||||
// Even though the protocol has 28/32 bits of data, only 24/28 bits are
|
||||
// distinct.
|
||||
// In transmission order, the 28/32 bits are constructed as follows:
|
||||
// 8/12 bits of address + 16 bits of command + 4 bits of checksum.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion.
|
||||
// Typically LG_BITS or LG32_BITS.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Should work.
|
||||
//
|
||||
// Note:
|
||||
// LG 32bit protocol appears near identical to the Samsung protocol.
|
||||
// They possibly differ on how they repeat and initial HDR mark.
|
||||
|
||||
// Ref:
|
||||
// https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/
|
||||
bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {
|
||||
if (nbits >= LG32_BITS) {
|
||||
if (results->rawlen < 2 * nbits + 2 * (HEADER + FOOTER) - 1)
|
||||
return false; // Can't possibly be a valid LG32 message.
|
||||
} else {
|
||||
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
|
||||
return false; // Can't possibly be a valid LG message.
|
||||
}
|
||||
if (strict && nbits != LG_BITS && nbits != LG32_BITS)
|
||||
return false; // Doesn't comply with expected LG protocol.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], LG_HDR_MARK) &&
|
||||
!matchMark(results->rawbuf[offset], LG32_HDR_MARK)) return false;
|
||||
uint32_t m_tick;
|
||||
if (matchMark(results->rawbuf[offset], LG_HDR_MARK))
|
||||
m_tick = results->rawbuf[offset++] * RAWTICK / LG_HDR_MARK_TICKS;
|
||||
else
|
||||
m_tick = results->rawbuf[offset++] * RAWTICK / LG32_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], LG_HDR_SPACE) &&
|
||||
!matchSpace(results->rawbuf[offset], LG32_HDR_SPACE)) return false;
|
||||
uint32_t s_tick;
|
||||
if (matchSpace(results->rawbuf[offset], LG_HDR_SPACE))
|
||||
s_tick = results->rawbuf[offset++] * RAWTICK / LG_HDR_SPACE_TICKS;
|
||||
else
|
||||
s_tick = results->rawbuf[offset++] * RAWTICK / LG32_HDR_SPACE_TICKS;
|
||||
|
||||
// Data
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
LG_BIT_MARK_TICKS * m_tick,
|
||||
LG_ONE_SPACE_TICKS * s_tick,
|
||||
LG_BIT_MARK_TICKS * m_tick,
|
||||
LG_ZERO_SPACE_TICKS * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], LG_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick))
|
||||
return false;
|
||||
|
||||
// Repeat
|
||||
if (nbits >= LG32_BITS) {
|
||||
// If we are expecting the LG 32-bit protocol, there is always
|
||||
// a repeat message. So, check for it.
|
||||
offset++;
|
||||
if (!matchMark(results->rawbuf[offset++], LG32_RPT_HDR_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], LG_RPT_SPACE_TICKS * s_tick))
|
||||
return false;
|
||||
if (!matchMark(results->rawbuf[offset++], LG_BIT_MARK_TICKS * m_tick))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
uint16_t command = (data >> 4) & 0xFFFF; // The 16 bits before the checksum.
|
||||
|
||||
if (strict && (data & 0xF) != calcLGChecksum(command))
|
||||
return false; // The last 4 bits sent are the expected checksum.
|
||||
|
||||
// Success
|
||||
results->decode_type = LG;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->command = command;
|
||||
results->address = data >> 20; // The bits before the command.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef IR_LG_H_
|
||||
#define IR_LG_H_
|
||||
|
||||
// L GGGG
|
||||
// L G
|
||||
// L G GG
|
||||
// L G G
|
||||
// LLLLL GGG
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
|
||||
uint8_t calcLGChecksum(uint16_t data);
|
||||
|
||||
#endif // IR_LG_H_
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG
|
||||
// LL AAAAA SS EE RR RR TTT AAAAA GG GG
|
||||
// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG
|
||||
// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG
|
||||
// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG
|
||||
|
||||
// Constants
|
||||
#define MIN_LASERTAG_SAMPLES 13U
|
||||
#define LASERTAG_TICK 333U
|
||||
#define LASERTAG_MIN_GAP 100000UL // Completely made up amount.
|
||||
#define LASERTAG_TOLERANCE 0U // Percentage error margin
|
||||
#define LASERTAG_EXCESS 0U // See MARK_EXCESS
|
||||
#define LASERTAG_DELTA 150U // Use instead of EXCESS and TOLERANCE.
|
||||
const int16_t kSPACE = 1;
|
||||
const int16_t kMARK = 0;
|
||||
|
||||
#if SEND_LASERTAG
|
||||
// Send a Lasertag packet.
|
||||
// This protocol is pretty much just raw Manchester encoding.
|
||||
//
|
||||
// Args:
|
||||
// data: The message you wish to send.
|
||||
// nbits: Bit size of the protocol you want to send.
|
||||
// repeat: Nr. of extra times the data will be sent.
|
||||
//
|
||||
// Status: STABLE / Working.
|
||||
//
|
||||
|
||||
void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits > sizeof(data) * 8)
|
||||
return; // We can't send something that big.
|
||||
|
||||
// Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle.
|
||||
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
|
||||
enableIROut(36, 25);
|
||||
|
||||
for (uint16_t i = 0; i <= repeat; i++) {
|
||||
// Data
|
||||
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
|
||||
if (data & mask) { // 1
|
||||
space(LASERTAG_TICK); // 1 is space, then mark.
|
||||
mark(LASERTAG_TICK);
|
||||
} else { // 0
|
||||
mark(LASERTAG_TICK); // 0 is mark, then space.
|
||||
space(LASERTAG_TICK);
|
||||
}
|
||||
// Footer
|
||||
space(LASERTAG_MIN_GAP);
|
||||
}
|
||||
}
|
||||
#endif // SEND_LASERTAG
|
||||
|
||||
#if DECODE_LASERTAG
|
||||
// Decode the supplied Lasertag message.
|
||||
// This protocol is pretty much just raw Manchester encoding.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Appears to be working 90% of the time.
|
||||
//
|
||||
// Ref:
|
||||
// http://www.sbprojects.com/knowledge/ir/rc5.php
|
||||
// https://en.wikipedia.org/wiki/RC-5
|
||||
// https://en.wikipedia.org/wiki/Manchester_code
|
||||
bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (results->rawlen < MIN_LASERTAG_SAMPLES) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != LASERTAG_BITS) return false;
|
||||
|
||||
uint16_t offset = OFFSET_START;
|
||||
uint16_t used = 0;
|
||||
uint64_t data = 0;
|
||||
uint16_t actual_bits = 0;
|
||||
|
||||
// No Header
|
||||
|
||||
// Data
|
||||
for (; offset <= results->rawlen; actual_bits++) {
|
||||
int16_t levelA = getRClevel(results, &offset, &used, LASERTAG_TICK,
|
||||
LASERTAG_TOLERANCE, LASERTAG_EXCESS,
|
||||
LASERTAG_DELTA);
|
||||
int16_t levelB = getRClevel(results, &offset, &used, LASERTAG_TICK,
|
||||
LASERTAG_TOLERANCE, LASERTAG_EXCESS,
|
||||
LASERTAG_DELTA);
|
||||
if (levelA == kSPACE && levelB == kMARK) {
|
||||
data = (data << 1) | 1; // 1
|
||||
} else {
|
||||
if (levelA == kMARK && levelB == kSPACE) {
|
||||
data <<= 1; // 0
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Footer (None)
|
||||
|
||||
// Compliance
|
||||
if (actual_bits < nbits) return false; // Less data than we expected.
|
||||
if (strict && actual_bits != LASERTAG_BITS) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = LASERTAG;
|
||||
results->value = data;
|
||||
results->address = data & 0xF; // Unit
|
||||
results->command = data >> 4; // Team
|
||||
results->repeat = false;
|
||||
results->bits = actual_bits;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_LASERTAG
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright 2013 mpflaga
|
||||
// Copyright 2015 kitlaan
|
||||
// Copyright 2017 Jason kendall, David Conran
|
||||
|
||||
#include "ir_Magiquest.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
#define IS_ZERO(m, s) (((m) * 100 / ((m) + (s))) <= MAGIQUEST_ZERO_RATIO)
|
||||
#define IS_ONE(m, s) (((m) * 100 / ((m) + (s))) >= MAGIQUEST_ONE_RATIO)
|
||||
|
||||
// Strips taken from:
|
||||
// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
// and
|
||||
// https://github.com/mpflaga/Arduino-IRremote
|
||||
|
||||
// Source: https://github.com/mpflaga/Arduino-IRremote
|
||||
|
||||
#if SEND_MAGIQUEST
|
||||
// Send a MagiQuest formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The contents of the message you want to send.
|
||||
// nbits: The bit size of the message being sent.
|
||||
// Typically MAGIQUEST_BITS.
|
||||
// repeat: The number of times you want the message to be repeated.
|
||||
//
|
||||
// Status: Alpha / Should be working.
|
||||
//
|
||||
void IRsend::sendMagiQuest(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(0, 0, // No Headers - Technically it's included in the data.
|
||||
// i.e. 8 zeros.
|
||||
MAGIQUEST_MARK_ONE, MAGIQUEST_SPACE_ONE,
|
||||
MAGIQUEST_MARK_ZERO, MAGIQUEST_SPACE_ZERO,
|
||||
0, // No footer mark.
|
||||
MAGIQUEST_GAP,
|
||||
data, nbits, 36, true, repeat, 50);
|
||||
}
|
||||
|
||||
// Encode a MagiQuest wand_id, and a magnitude into a single 64bit value.
|
||||
// (Only 48 bits of real data + 8 leading zero bits)
|
||||
// This is suitable for calling sendMagiQuest() with.
|
||||
// e.g. sendMagiQuest(encodeMagiQuest(wand_id, magnitude));
|
||||
uint64_t IRsend::encodeMagiQuest(uint32_t wand_id, uint16_t magnitude) {
|
||||
uint64_t result = 0;
|
||||
result = wand_id;
|
||||
result <<= 16;
|
||||
result |= magnitude;
|
||||
// Shouldn't be needed, but ensure top 8/16 bit are zero.
|
||||
result &= 0xFFFFFFFFFFFFULL;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Source: https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
|
||||
#if DECODE_MAGIQUEST
|
||||
// Decode the supplied MagiQuest message.
|
||||
// MagiQuest protocol appears to be a header of 8 'zero' bits, followed
|
||||
// by 32 bits of "wand ID" and finally 16 bits of "magnitude".
|
||||
// Even though we describe this protocol as 56 bits, it really only has
|
||||
// 48 bits of data that matter.
|
||||
//
|
||||
// In transmission order, 8 zeros + 32 wand_id + 16 magnitude.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion, inc. the 8 bit header.
|
||||
// Typically MAGIQUEST_BITS.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: Alpha / Should work.
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
uint16_t bits = 0;
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
if (results->rawlen < (2 * MAGIQUEST_BITS)) {
|
||||
DPRINT("Not enough bits to be Magiquest - Rawlen: ");
|
||||
DPRINT(results->rawlen);
|
||||
DPRINT(" Expected: ");
|
||||
DPRINTLN((2 * MAGIQUEST_BITS));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != MAGIQUEST_BITS) return false;
|
||||
|
||||
// Of six wands as datapoints, so far they all start with 8 ZEROs.
|
||||
// For example, here is the data from two wands
|
||||
// 00000000 00100011 01001100 00100110 00000010 00000010 00010111
|
||||
// 00000000 00100000 10001000 00110001 00000010 00000010 10110100
|
||||
|
||||
// Decode the (MARK + SPACE) bits
|
||||
while (offset + 1 < results->rawlen && bits < nbits - 1) {
|
||||
uint16_t mark = results->rawbuf[offset];
|
||||
uint16_t space = results->rawbuf[offset + 1];
|
||||
if (!matchMark(mark + space, MAGIQUEST_TOTAL_USEC)) {
|
||||
DPRINT("Not enough time to be Magiquest - Mark: ");
|
||||
DPRINT(mark);
|
||||
DPRINT(" Space: ");
|
||||
DPRINT(space);
|
||||
DPRINT(" Total: ");
|
||||
DPRINT(mark+space);
|
||||
DPRINT("Expected: ");
|
||||
DPRINTLN(MAGIQUEST_TOTAL_USEC);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_ZERO(mark, space)) data = (data << 1) | 0;
|
||||
else if (IS_ONE( mark, space)) data = (data << 1) | 1;
|
||||
else return false;
|
||||
|
||||
bits++;
|
||||
offset += 2;
|
||||
|
||||
// Compliance
|
||||
// The first 8 bits of this protocol are supposed to all be 0.
|
||||
// Exit out early as it is never going to match.
|
||||
if (strict && bits == 8 && data != 0) return false;
|
||||
}
|
||||
|
||||
// Last bit is special as the protocol ends with a SPACE, not a MARK.
|
||||
// Grab the last MARK bit, assuming a good SPACE after it
|
||||
if (offset < results->rawlen) {
|
||||
uint16_t mark = results->rawbuf[offset];
|
||||
uint16_t space = (MAGIQUEST_TOTAL_USEC / RAWTICK) - mark;
|
||||
|
||||
if (IS_ZERO(mark, space)) data = (data << 1) | 0;
|
||||
else if (IS_ONE( mark, space)) data = (data << 1) | 1;
|
||||
else return false;
|
||||
|
||||
bits++;
|
||||
}
|
||||
|
||||
if (bits != nbits) return false;
|
||||
|
||||
if (strict) {
|
||||
// The top 8 bits of the 56 bits needs to be 0x00 to be valid.
|
||||
// i.e. bits 56 to 49 are all zero.
|
||||
if ((data >> (nbits - 8)) != 0) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = MAGIQUEST;
|
||||
results->bits = bits;
|
||||
results->value = data;
|
||||
results->address = data >> 16; // Wand ID
|
||||
results->command = data & 0xFFFF; // Magnitude
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2013 mpflaga
|
||||
// Copyright 2015 kitlaan
|
||||
// Copyright 2017 Jason kendall, David Conran
|
||||
|
||||
#ifndef IR_MAGIQUEST_H_
|
||||
#define IR_MAGIQUEST_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// MagiQuest packet is both Wand ID and magnitude of swish and flick
|
||||
union magiquest {
|
||||
uint64_t llword;
|
||||
uint8_t byte[8];
|
||||
// uint16_t word[4];
|
||||
uint32_t lword[2];
|
||||
struct {
|
||||
uint16_t magnitude;
|
||||
uint32_t wand_id;
|
||||
uint8_t padding;
|
||||
uint8_t scrap;
|
||||
} cmd;
|
||||
};
|
||||
|
||||
#define MAGIQUEST_TOTAL_USEC 1150U
|
||||
#define MAGIQUEST_ZERO_RATIO 30U // usually <= ~25%
|
||||
#define MAGIQUEST_ONE_RATIO 38U // usually >= ~50%
|
||||
|
||||
#define MAGIQUEST_PERIOD 1150U
|
||||
#define MAGIQUEST_MARK_ZERO 280U
|
||||
#define MAGIQUEST_SPACE_ZERO 850U
|
||||
#define MAGIQUEST_MARK_ONE 580U
|
||||
#define MAGIQUEST_SPACE_ONE 600U
|
||||
#define MAGIQUEST_GAP 100000UL // A guess of the gap between messages
|
||||
#endif // IR_MAGIQUEST_H_
|
||||
@@ -0,0 +1,426 @@
|
||||
// Copyright 2017 bwze, crankyoldgit
|
||||
|
||||
#include "ir_Midea.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// MM MM IIIII DDDDD EEEEEEE AAA
|
||||
// MMM MMM III DD DD EE AAAAA
|
||||
// MM MM MM III DD DD EEEEE AA AA
|
||||
// MM MM III DD DD EE AAAAAAA
|
||||
// MM MM IIIII DDDDDD EEEEEEE AA AA
|
||||
|
||||
// Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit
|
||||
//
|
||||
// Equipment it seems compatible with:
|
||||
// * Pioneer System Model RYBO12GMFILCAD (12K BTU)
|
||||
// * Pioneer System Model RUBO18GMFILCAD (18K BTU)
|
||||
// * <Add models (A/C & remotes) you've gotten it working with here>
|
||||
|
||||
// Ref:
|
||||
// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
|
||||
|
||||
// Constants
|
||||
#define MIDEA_TICK 80U
|
||||
#define MIDEA_BIT_MARK_TICKS 7U
|
||||
#define MIDEA_BIT_MARK (MIDEA_BIT_MARK_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_ONE_SPACE_TICKS 21U
|
||||
#define MIDEA_ONE_SPACE (MIDEA_ONE_SPACE_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_ZERO_SPACE_TICKS 7U
|
||||
#define MIDEA_ZERO_SPACE (MIDEA_ZERO_SPACE_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_HDR_MARK_TICKS 56U
|
||||
#define MIDEA_HDR_MARK (MIDEA_HDR_MARK_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_HDR_SPACE_TICKS 56U
|
||||
#define MIDEA_HDR_SPACE (MIDEA_HDR_SPACE_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_MIN_GAP_TICKS (MIDEA_HDR_MARK_TICKS + MIDEA_ZERO_SPACE_TICKS \
|
||||
+ MIDEA_BIT_MARK_TICKS)
|
||||
#define MIDEA_MIN_GAP (MIDEA_MIN_GAP_TICKS * MIDEA_TICK)
|
||||
#define MIDEA_TOLERANCE 30U // Percent
|
||||
|
||||
#if SEND_MIDEA
|
||||
// Send a Midea message
|
||||
//
|
||||
// Args:
|
||||
// data: Contents of the message to be sent.
|
||||
// nbits: Nr. of bits of data to be sent. Typically MIDEA_BITS.
|
||||
// repeat: Nr. of additional times the message is to be sent.
|
||||
//
|
||||
// Status: Alpha / Needs testing against a real device.
|
||||
//
|
||||
void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits % 8 != 0)
|
||||
return; // nbits is required to be a multiple of 8.
|
||||
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// The protcol sends the message, then follows up with an entirely
|
||||
// inverted payload.
|
||||
for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) {
|
||||
// Header
|
||||
mark(MIDEA_HDR_MARK);
|
||||
space(MIDEA_HDR_SPACE);
|
||||
// Data
|
||||
// Break data into byte segments, starting at the Most Significant
|
||||
// Byte. Each byte then being sent normal, then followed inverted.
|
||||
for (uint16_t i = 8; i <= nbits; i += 8) {
|
||||
// Grab a bytes worth of data.
|
||||
uint8_t segment = (data >> (nbits - i)) & 0xFF;
|
||||
sendData(MIDEA_BIT_MARK, MIDEA_ONE_SPACE,
|
||||
MIDEA_BIT_MARK, MIDEA_ZERO_SPACE,
|
||||
segment, 8, true);
|
||||
}
|
||||
// Footer
|
||||
mark(MIDEA_BIT_MARK);
|
||||
space(MIDEA_MIN_GAP); // Pause before repeating
|
||||
|
||||
// Invert the data for the 2nd phase of the message.
|
||||
// As we get called twice in the inner loop, we will always revert
|
||||
// to the original 'data' state.
|
||||
data = ~data;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Code to emulate Midea A/C IR remote control unit.
|
||||
// Warning: Consider this very alpha code.
|
||||
|
||||
// Initialise the object.
|
||||
IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
// Reset the state of the remote to a known good state/sequence.
|
||||
void IRMideaAC::stateReset() {
|
||||
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
|
||||
remote_state = 0xA1826FFFFF62;
|
||||
}
|
||||
|
||||
// Configure the pin for output.
|
||||
void IRMideaAC::begin() {
|
||||
_irsend.begin();
|
||||
}
|
||||
|
||||
#if SEND_MIDEA
|
||||
// Send the current desired state to the IR LED.
|
||||
void IRMideaAC::send() {
|
||||
checksum(); // Ensure correct checksum before sending.
|
||||
_irsend.sendMidea(remote_state);
|
||||
}
|
||||
#endif // SEND_MIDEA
|
||||
|
||||
// Return a pointer to the internal state date of the remote.
|
||||
uint64_t IRMideaAC::getRaw() {
|
||||
checksum();
|
||||
return remote_state & MIDEA_AC_STATE_MASK;
|
||||
}
|
||||
|
||||
// Override the internal state with the new state.
|
||||
void IRMideaAC::setRaw(uint64_t newState) {
|
||||
remote_state = newState & MIDEA_AC_STATE_MASK;
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C to off.
|
||||
void IRMideaAC::on() {
|
||||
remote_state |= MIDEA_AC_POWER;
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C to off.
|
||||
void IRMideaAC::off() {
|
||||
remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_POWER);
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C.
|
||||
void IRMideaAC::setPower(const bool state) {
|
||||
if (state)
|
||||
on();
|
||||
else
|
||||
off();
|
||||
}
|
||||
|
||||
// Return the requested power state of the A/C.
|
||||
bool IRMideaAC::getPower() {
|
||||
return (remote_state & MIDEA_AC_POWER);
|
||||
}
|
||||
|
||||
// Set the temperature.
|
||||
// Args:
|
||||
// temp: Temp. in degrees.
|
||||
// useCelsius: Degree type to use. celsius (true) or fahrenheit (false)
|
||||
void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) {
|
||||
uint8_t new_temp = temp;
|
||||
if (useCelsius) {
|
||||
new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_C, new_temp);
|
||||
new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_C, new_temp);
|
||||
new_temp = (uint8_t) ((new_temp * 1.8) + 32.5); // 0.5 so we rounding.
|
||||
}
|
||||
new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_F, new_temp);
|
||||
new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_F, new_temp);
|
||||
new_temp -= MIDEA_AC_MIN_TEMP_F;
|
||||
remote_state &= MIDEA_AC_TEMP_MASK;
|
||||
remote_state |= ((uint64_t) new_temp << 24);
|
||||
}
|
||||
|
||||
// Return the set temp.
|
||||
// Args:
|
||||
// useCelsius: Flag indicating if the results are in celsius or fahrenheit.
|
||||
// Returns:
|
||||
// A uint8_t containing the temperature.
|
||||
uint8_t IRMideaAC::getTemp(const bool useCelsius) {
|
||||
uint8_t temp = ((remote_state >> 24) & 0x1F) + MIDEA_AC_MIN_TEMP_F;
|
||||
if (useCelsius) {
|
||||
temp = (uint8_t) ((temp - 32) / 1.8);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
// Set the speed of the fan,
|
||||
// 1-3 set the speed, 0 or anything else set it to auto.
|
||||
void IRMideaAC::setFan(const uint8_t fan) {
|
||||
uint64_t new_fan;
|
||||
switch (fan) {
|
||||
case MIDEA_AC_FAN_LOW:
|
||||
case MIDEA_AC_FAN_MED:
|
||||
case MIDEA_AC_FAN_HI:
|
||||
new_fan = fan;
|
||||
break;
|
||||
default:
|
||||
new_fan = MIDEA_AC_FAN_AUTO;
|
||||
}
|
||||
remote_state &= MIDEA_AC_FAN_MASK;
|
||||
remote_state |= (new_fan << 35);
|
||||
}
|
||||
|
||||
// Return the requested state of the unit's fan.
|
||||
uint8_t IRMideaAC::getFan() {
|
||||
return (remote_state >> 35) & 0b111;
|
||||
}
|
||||
|
||||
// Get the requested climate operation mode of the a/c unit.
|
||||
// Returns:
|
||||
// A uint8_t containing the A/C mode.
|
||||
uint8_t IRMideaAC::getMode() {
|
||||
return ((remote_state >> 32) & 0b111);
|
||||
}
|
||||
|
||||
// Set the requested climate operation mode of the a/c unit.
|
||||
void IRMideaAC::setMode(const uint8_t mode) {
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
uint64_t new_mode;
|
||||
switch (mode) {
|
||||
case MIDEA_AC_AUTO:
|
||||
case MIDEA_AC_COOL:
|
||||
case MIDEA_AC_HEAT:
|
||||
case MIDEA_AC_DRY:
|
||||
case MIDEA_AC_FAN:
|
||||
new_mode = mode;
|
||||
break;
|
||||
default:
|
||||
new_mode = MIDEA_AC_AUTO;
|
||||
}
|
||||
remote_state &= MIDEA_AC_MODE_MASK;
|
||||
remote_state |= (new_mode << 32);
|
||||
}
|
||||
|
||||
// Set the Sleep state of the A/C.
|
||||
void IRMideaAC::setSleep(const bool state) {
|
||||
if (state)
|
||||
remote_state |= MIDEA_AC_SLEEP;
|
||||
else
|
||||
remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_SLEEP);
|
||||
}
|
||||
|
||||
// Return the Sleep state of the A/C.
|
||||
bool IRMideaAC::getSleep() {
|
||||
return (remote_state & MIDEA_AC_SLEEP);
|
||||
}
|
||||
|
||||
// Calculate the checksum for a given array.
|
||||
// Args:
|
||||
// state: The state to calculate the checksum over.
|
||||
// Returns:
|
||||
// The 8 bit checksum value.
|
||||
uint8_t IRMideaAC::calcChecksum(const uint64_t state) {
|
||||
uint8_t sum = 0;
|
||||
uint64_t temp_state = state;
|
||||
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
temp_state >>= 8;
|
||||
sum += reverseBits((temp_state & 0xFF), 8);
|
||||
}
|
||||
sum = 256 - sum;
|
||||
return reverseBits(sum, 8);
|
||||
}
|
||||
|
||||
// Verify the checksum is valid for a given state.
|
||||
// Args:
|
||||
// state: The state to verify the checksum of.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRMideaAC::validChecksum(const uint64_t state) {
|
||||
return ((state & 0xFF) == calcChecksum(state));
|
||||
}
|
||||
|
||||
// Calculate & set the checksum for the current internal state of the remote.
|
||||
void IRMideaAC::checksum() {
|
||||
// Stored the checksum value in the last byte.
|
||||
remote_state &= MIDEA_AC_CHECKSUM_MASK;
|
||||
remote_state |= calcChecksum(remote_state);
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRMideaAC::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRMideaAC::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
if (getPower())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case MIDEA_AC_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case MIDEA_AC_COOL:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case MIDEA_AC_HEAT:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case MIDEA_AC_DRY:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case MIDEA_AC_FAN:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" +
|
||||
uint64ToString(getTemp(false)) + "F";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case MIDEA_AC_FAN_AUTO:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case MIDEA_AC_FAN_LOW:
|
||||
result += " (LOW)";
|
||||
break;
|
||||
case MIDEA_AC_FAN_MED:
|
||||
result += " (MED)";
|
||||
break;
|
||||
case MIDEA_AC_FAN_HI:
|
||||
result += " (HI)";
|
||||
break;
|
||||
}
|
||||
result += ", Sleep: ";
|
||||
if (getSleep())
|
||||
result += "On";
|
||||
else
|
||||
result += "Off";
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_MIDEA
|
||||
// Decode the supplied Midea message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: The number of data bits to expect. Typically MIDEA_BITS.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: Alpha / Needs testing against a real device.
|
||||
//
|
||||
bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
|
||||
uint8_t min_nr_of_messages = 1;
|
||||
if (strict) {
|
||||
if (nbits != MIDEA_BITS)
|
||||
return false; // Not strictly a MIDEA message.
|
||||
min_nr_of_messages = 2;
|
||||
}
|
||||
|
||||
// The protocol sends the data normal + inverted, alternating on
|
||||
// each byte. Hence twice the number of expected data bits.
|
||||
if (results->rawlen < min_nr_of_messages * (2 * nbits + HEADER + FOOTER) - 1)
|
||||
return false; // Can't possibly be a valid MIDEA message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t inverted = 0;
|
||||
uint16_t offset = OFFSET_START;
|
||||
|
||||
if (nbits > sizeof(data) * 8)
|
||||
return false; // We can't possibly capture a Midea packet that big.
|
||||
|
||||
for (uint8_t i = 0; i < min_nr_of_messages; i++) {
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], MIDEA_HDR_MARK)) return false;
|
||||
// Calculate how long the common tick time is based on the header mark.
|
||||
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
MIDEA_HDR_MARK_TICKS;
|
||||
if (!matchSpace(results->rawbuf[offset], MIDEA_HDR_SPACE)) return false;
|
||||
// Calculate how long the common tick time is based on the header space.
|
||||
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK /
|
||||
MIDEA_HDR_SPACE_TICKS;
|
||||
|
||||
// Data (Normal)
|
||||
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
|
||||
MIDEA_BIT_MARK_TICKS * m_tick,
|
||||
MIDEA_ONE_SPACE_TICKS * s_tick,
|
||||
MIDEA_BIT_MARK_TICKS * m_tick,
|
||||
MIDEA_ZERO_SPACE_TICKS * s_tick,
|
||||
MIDEA_TOLERANCE);
|
||||
if (data_result.success == false) return false;
|
||||
offset += data_result.used;
|
||||
if (i % 2 == 0)
|
||||
data = data_result.data;
|
||||
else
|
||||
inverted = data_result.data;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], MIDEA_BIT_MARK_TICKS * m_tick,
|
||||
MIDEA_TOLERANCE))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset++], MIDEA_MIN_GAP_TICKS * s_tick,
|
||||
MIDEA_TOLERANCE))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Protocol requires a second message with all the data bits inverted.
|
||||
// We should have checked we got a second message in the previous loop.
|
||||
// Just need to check it's value is an inverted copy of the first message.
|
||||
uint64_t mask = (1ULL << MIDEA_BITS) - 1;
|
||||
if ((data & mask) != ((inverted ^ mask) & mask)) return false;
|
||||
if (!IRMideaAC::validChecksum(data)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = MIDEA;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MIDEA
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user