331 Commits

Author SHA1 Message Date
Kuba Szczodrzyński
8f447a4a72 [release] v0.12.6
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-02-02 22:28:52 +01:00
Kuba Szczodrzyński
8ace11e462 [core] Fix Python lint 2023-02-02 22:28:10 +01:00
Kuba Szczodrzyński
949dfe7266 [core] Use PIO repackages of vendor frameworks 2023-02-02 22:21:12 +01:00
Kuba Szczodrzyński
f0d490aef9 [docs] Fix ESPHome sample config indent 2023-02-02 21:59:14 +01:00
Kuba Szczodrzyński
bdedae981a [beken-72xx] Fix UF2 RBL relative offset 2023-01-28 16:38:28 +01:00
Kuba Szczodrzyński
9e0750c9d3 [release] v0.12.5
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-01-28 13:00:10 +01:00
Kuba Szczodrzyński
9a3e512b1b [boards] Add LSC LMA35 T variant 2023-01-28 13:00:10 +01:00
Kuba Szczodrzyński
861e741030 [beken-72xx] Disable OTA encryption 2023-01-28 12:53:57 +01:00
Kuba Szczodrzyński
a3e7e21d45 [core] Migrate to ltchiptool v3.0.0 2023-01-28 12:53:57 +01:00
Matt Shepcar
86db2fcf61 [beken-72xx] Fix for flicker when PWM lights turn on/off on bk7231n (#68) 2023-01-28 12:52:01 +01:00
Kuba Szczodrzyński
fcd72e013a [docs] Add ESPHome Dashboard usage docs 2023-01-26 20:41:57 +01:00
Kuba Szczodrzyński
2450f8c64b [release] v0.12.4
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-01-26 11:28:25 +01:00
Kuba Szczodrzyński
676a9b1686 [core] Fix reinstalling ltchiptool 2023-01-26 11:28:09 +01:00
Kuba Szczodrzyński
b10db63c74 [realtek-ambz] Fix WiFi station BSSID retrieval 2023-01-19 17:38:45 +01:00
Kuba Szczodrzyński
4b25ef7516 [realtek-ambz] Fix resetting OTA switch when it reaches 0 2023-01-19 16:53:27 +01:00
Kuba Szczodrzyński
de0d19cda6 [docs] Update ltchiptool installation info 2023-01-19 12:29:59 +01:00
Kuba Szczodrzyński
c55386af0e [docs] Add icons to external links 2023-01-18 19:06:18 +01:00
Kuba Szczodrzyński
33fe1c7a54 Merge branch 'docs-refactor' 2023-01-18 18:39:11 +01:00
Kuba Szczodrzyński
bf6d05e92a [docs] Remove AmbZ doxygen build 2023-01-18 18:38:52 +01:00
Kuba Szczodrzyński
b6113d8d2b [docs] Configure MkDocs Material 2023-01-18 18:32:34 +01:00
Kuba Szczodrzyński
bc31154035 [docs] Add Tuya Pinout Config to resources 2023-01-18 17:59:51 +01:00
Kuba Szczodrzyński
edfe0329bf [docs] Update links in board docs 2023-01-18 17:59:19 +01:00
Kuba Szczodrzyński
1e11dd776a [docs] Move resources to chip family docs 2023-01-18 17:56:09 +01:00
Kuba Szczodrzyński
028526d22d [docs] Add ltchiptool GUI download button 2023-01-18 15:00:01 +01:00
Kuba Szczodrzyński
7ac2d854f2 [docs] Move tools docs to a subdirectory 2023-01-18 14:59:36 +01:00
Kuba Szczodrzyński
279c81e431 [docs] Update code block styles 2023-01-18 14:02:12 +01:00
Kuba Szczodrzyński
4e319b40ed [docs] Fix ltchiptool docs badges 2023-01-18 13:30:13 +01:00
Kuba Szczodrzyński
d082b39980 [docs] Add ltchiptool screenshot 2023-01-17 22:45:00 +01:00
Kuba Szczodrzyński
277402f577 [docs] Update chip flashing guides, fix mkdocs building 2023-01-17 22:33:57 +01:00
Kuba Szczodrzyński
9e95c507b1 [docs] Refactor docs structure, add project upload guides 2023-01-17 18:56:15 +01:00
Kuba Szczodrzyński
7440fc6f46 [core] Add parent_description key to families 2023-01-16 10:42:30 +01:00
Kuba Szczodrzyński
a24028f674 [beken-72xx] Fix RBL app size typo 2023-01-16 10:41:17 +01:00
Stroe Andrei Catalin
00913b013f [beken-72xx] Fix compilation on ARM systems (#58)
* Fix toolchain for BK7231 on ARM cpus

* Fix format

* PR comments

* PR Comments, part 2
2023-01-03 21:15:03 +01:00
Mihai Ambrosie
447d08d613 [realtek-ambz] Fix 4M/980k linker script name (#56) 2023-01-03 10:56:48 +01:00
Kuba Szczodrzyński
d4f7736b2d [release] v0.12.3
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-01-02 20:32:46 +01:00
Kuba Szczodrzyński
60f72fffdf [core] Fix installing ltchiptool, again 2023-01-02 20:32:29 +01:00
Kuba Szczodrzyński
77af9c1cba [release] v0.12.2
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-01-02 19:41:39 +01:00
Kuba Szczodrzyński
1b2414337f [core] Fix installing ltchiptool 2023-01-02 19:40:55 +01:00
Kuba Szczodrzyński
d41f1f2a4d [release] v0.12.1
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2023-01-02 17:06:16 +01:00
Kuba Szczodrzyński
f9967ff990 [core] Fix importing ltchiptool after updating 2023-01-02 17:05:35 +01:00
Kuba Szczodrzyński
db0ab41ea4 [realtek-ambz] Check SoftwareSerial before stopping 2023-01-02 16:03:49 +01:00
Kuba Szczodrzyński
da95cc30d3 [realtek-ambz] Move to GNU++11, update TODO.md 2023-01-02 16:02:57 +01:00
Ross Reedstrom
6b3001573e [docs] Fix link to supported boards (#53) 2023-01-01 14:05:57 +01:00
Kuba Szczodrzyński
b85f6f8c3f [release] v0.12.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-12-30 15:52:14 +01:00
Kuba Szczodrzyński
d036ac9fba [core] Migrate to ltchiptool v2.0.0 2022-12-30 15:51:32 +01:00
Kuba Szczodrzyński
f63a114371 [beken-72xx] Support compilation for BK7251 family 2022-12-08 20:16:44 +01:00
Kuba Szczodrzyński
8cb59661ac [boards] Add initial support for BK7252 2022-12-08 20:04:17 +01:00
Kuba Szczodrzyński
fc1da609f2 [beken-72xx] Add BL2028N to supported chips 2022-12-08 17:48:04 +01:00
Kuba Szczodrzyński
c6566323fd [boards] Move IC pinouts to separate directory 2022-12-08 17:26:46 +01:00
Kuba Szczodrzyński
30e1533ad9 [core] Rename BK7231T family to BK7231U 2022-12-08 17:19:42 +01:00
Kuba Szczodrzyński
71dfea1904 [release] v0.11.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-12-07 17:43:19 +01:00
Kuba Szczodrzyński
4fa7457423 [boards] Update boardgen and generated board files 2022-12-07 17:03:41 +01:00
kolos
9583bb35f5 [core] Include SNTP app from lwIP (#38) 2022-12-06 22:47:22 +01:00
Kuba Szczodrzyński
e806c4db3a [docs] Output boards.json to site/ directory 2022-12-06 21:37:26 +01:00
Kuba Szczodrzyński
665ab22de5 [docs] Generate JSON file with supported boards 2022-12-06 21:34:46 +01:00
Kuba Szczodrzyński
ec4e946643 [core] Add restartDownloadMode() method 2022-11-26 19:36:10 +01:00
Kuba Szczodrzyński
9e4bbe3e36 [realtek-ambz] Fix software rebooting 2022-11-26 19:35:40 +01:00
Kuba Szczodrzyński
1172c1a852 [realtek-ambz] Update lwIP to v2.1.3 2022-11-26 15:32:33 +01:00
Stroe Andrei Catalin
8a4392bb22 [beken-72xx] Add support for CHANGE interrupts (#34)
* [beken-72xx] Added support for ANY_EDGE interrupts

* clang format

* PR comments

* Update wiring_irq.c

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>
2022-10-21 22:19:10 +02:00
Stroe Andrei Catalin
a79a2d60d3 [core] Use mDNS hostname as instance name by default (#32) 2022-10-15 16:39:54 +02:00
blakadder
9553ff66a3 [docs] Update docs URL in basic README (#31) 2022-10-08 22:35:08 +02:00
blakadder
79c6f05291 [docs] Update site_url (#30)
* update site url to new one

* Update mkdocs.yml

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>
2022-10-08 22:34:50 +02:00
David Buezas
3e691de24a [beken-72xx] Fix micros() usage with disabled interrupts (#28)
* Workaround for micros when used inside interruptLock blocks

* Fix comment formatting

* Cover case when millis overflows

* Improve variable names.
2022-10-05 12:51:20 +02:00
David Buezas
303def49cb [beken-72xx] Implement accurate delayMicroseconds() (#26)
* Implements accurate delayMicroseconds

* renamed to cammelCase, moved #defines and clang-formatted
2022-10-03 15:14:31 +02:00
Kuba Szczodrzyński
18653781d5 [beken-72xx] Replace delay() define with inline function 2022-10-03 15:12:15 +02:00
Kuba Szczodrzyński
2a75bdbeda [beken-72xx] Update TLV and calibration flash layout 2022-10-01 15:01:38 +02:00
Kuba Szczodrzyński
4a6926eb6c [beken-72xx] Fix restarting AP with shorter password 2022-10-01 15:00:08 +02:00
Kuba Szczodrzyński
3dc4007fa7 [core] Update ltchiptool to fix realtek-ambz OTA issue 2022-09-30 18:42:09 +02:00
Kuba Szczodrzyński
9c3833d66c [realtek-ambz] Provide unknown reset reason 2022-09-27 12:53:08 +02:00
Kuba Szczodrzyński
41966f33c2 [github] Add FUNDING.yml 2022-09-25 22:26:25 +02:00
Kuba Szczodrzyński
50fea2a8f0 [boards] Add RTL8710BX board with 4 MiB flash 2022-09-23 12:25:30 +02:00
Kuba Szczodrzyński
05cb7f7947 [realtek-ambz] Implement watchdog API 2022-09-21 21:46:11 +02:00
Kuba Szczodrzyński
5b6b80e3cd [tools] Migrate to bk7231tools from PyPI 2022-09-21 21:41:08 +02:00
Kuba Szczodrzyński
2c59af8399 [docs] Add Beken flash support list 2022-09-21 21:24:59 +02:00
Kuba Szczodrzyński
b404f9fa42 [core] Append revision info to version string 2022-09-15 19:59:34 +02:00
Kuba Szczodrzyński
8dc8340ce4 [realtek-ambz] Use GCC 5.2.1 to fix ranlib missing 2022-09-15 18:37:19 +02:00
Kuba Szczodrzyński
30f08a145e [realtek-ambz] Export SoftwareSerial availability macro 2022-09-15 18:31:20 +02:00
Kuba Szczodrzyński
d3d140fa60 [builder] Fix Python lint 2022-09-15 17:38:08 +02:00
Kuba Szczodrzyński
5f8e687e68 [core] Fix getResetReasonName() API 2022-09-15 14:51:14 +02:00
Kuba Szczodrzyński
fab640901d [core] Add textual reset reason API 2022-09-15 13:55:16 +02:00
Kuba Szczodrzyński
9a053fb6ff [core] Export LT class as ESP alias 2022-09-15 13:53:12 +02:00
Kuba Szczodrzyński
24e4b0b376 [builder] Use ranlib from the toolchain 2022-09-15 12:16:22 +02:00
Kuba Szczodrzyński
b8eee999e2 [docs] Add remaining config options, add warning about latest version 2022-09-15 11:47:25 +02:00
Kuba Szczodrzyński
7fe9a114b7 [beken-72xx] Auto-reboot after detecting download-mode commands 2022-09-14 19:57:23 +02:00
Kuba Szczodrzyński
05b2fb4fcb [beken-72xx] Implement micros() properly; use high-res timer for OS 2022-09-14 19:56:51 +02:00
Kuba Szczodrzyński
830d8762cd [beken-72xx] Disable watchdog feeding in BDK 2022-09-14 19:55:10 +02:00
Kuba Szczodrzyński
3f81666369 [beken-72xx] Implement watchdog & reset reason APIs 2022-09-13 17:42:13 +02:00
Kuba Szczodrzyński
d40f4003b5 [boards] Add FCC ID to board docs 2022-09-11 16:39:15 +02:00
Kuba Szczodrzyński
1585e425ce [boards] Add CB2L board 2022-09-11 16:20:48 +02:00
Kuba Szczodrzyński
bbc7613079 [boards] Add remaining CB3x series boards 2022-09-11 14:26:02 +02:00
Kuba Szczodrzyński
ec54745174 [docs] Update Tuya all boards list 2022-09-11 14:24:51 +02:00
Kuba Szczodrzyński
a29453de12 [docs] Add 3-rd party libs used in ESPHome 2022-09-10 19:08:57 +02:00
Kuba Szczodrzyński
db5bf4fc46 [docs] Add dumping instructions 2022-09-10 18:24:10 +02:00
Kuba Szczodrzyński
9495e3418c [beken-72xx] Fake enabling AP to fix connection issues, set default IP 2022-09-10 16:44:45 +02:00
Kuba Szczodrzyński
83573a743b [core] Implement WiFiUDP based on lwIP 2022-09-10 16:15:28 +02:00
Kuba Szczodrzyński
f5dcd82747 [core] Add printf format attribute to lt_log() 2022-09-10 16:14:38 +02:00
Kuba Szczodrzyński
070ec2f48f [core] Update ArduinoCore-API to support Print::printf() 2022-09-10 16:14:10 +02:00
Kuba Szczodrzyński
96c7fdc02c [beken-72xx] Reset scan state after timeout 2022-09-09 20:21:33 +02:00
Kuba Szczodrzyński
dfde2d8a62 [release] v0.10.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-09-03 21:40:04 +02:00
Kuba Szczodrzyński
a3f57114da [core] Update ltchiptool to v1.5.2 2022-09-03 21:38:46 +02:00
Kuba Szczodrzyński
904af10914 [core] Implement POSIX gettimeofday() and settimeofday() 2022-09-03 21:26:07 +02:00
Kuba Szczodrzyński
9b5013a694 [beken-72xx] Move periodic heap logging to core 2022-09-03 21:25:39 +02:00
Kuba Szczodrzyński
922adfd3d4 [core] Print full error details in Update library 2022-09-03 21:24:46 +02:00
Kuba Szczodrzyński
362144033b [core] Move main.cpp to core directory 2022-09-03 20:01:24 +02:00
Kuba Szczodrzyński
caf9a579d3 [beken-72xx] Fix writing 0% and 100% with PWM 2022-09-03 19:01:17 +02:00
Kuba Szczodrzyński
cb40fdcdbe [beken-72xx] Unprotect flash before writing 2022-09-03 17:50:36 +02:00
Kuba Szczodrzyński
ef15e754c9 [core] Make LT API usable from C 2022-09-03 17:49:19 +02:00
Kuba Szczodrzyński
d55568c146 [beken-72xx] Fix PWM on BK7231N 2022-09-02 23:58:38 +02:00
Kuba Szczodrzyński
e134863db1 [realtek-ambz] Fix mDNS compilation problem on lwIP < 2.1.0 2022-09-02 21:11:12 +02:00
Kuba Szczodrzyński
c37ae51dd3 [core] Update default logging options, fix disabling logger 2022-09-02 14:35:21 +02:00
Kuba Szczodrzyński
4fc2ff43c1 [core] Make info, warning and error logging per-module configurable 2022-09-02 14:12:05 +02:00
Kuba Szczodrzyński
607f13d935 [core] Refactor per-module logger macros 2022-09-02 14:02:00 +02:00
Kuba Szczodrzyński
44c1a3f695 [beken-72xx] Fix digitalRead() setting wrong pin mode 2022-09-01 21:13:04 +02:00
Kuba Szczodrzyński
d30decfbc8 [core] Fix lwipopts.h invalid include 2022-09-01 19:09:19 +02:00
Kuba Szczodrzyński
705b2f794e [beken-72xx] Increase TCP/IP stack size to fix mDNS stack overflow 2022-08-31 22:39:49 +02:00
Kuba Szczodrzyński
4958690d9e [core] Make lwIP debugging configurable 2022-08-31 22:38:07 +02:00
Kuba Szczodrzyński
d7749d3a24 [core] Export Update combined error code 2022-08-31 22:28:47 +02:00
Kuba Szczodrzyński
73e07a594f [core] Add mDNS debug logging, start IGMP if not enabled 2022-08-31 22:28:13 +02:00
Kuba Szczodrzyński
755c2ef400 [core] Add periodical free heap logging 2022-08-31 22:26:12 +02:00
Kuba Szczodrzyński
3f588e970a [core] Rework mDNS responder to fix TXT records 2022-08-27 00:00:45 +02:00
Kuba Szczodrzyński
3264807e77 [core] Fix StreamString clearing data using println() 2022-08-26 20:03:01 +02:00
Kuba Szczodrzyński
01225d4648 [beken-72xx] Update to external lwIP v2.1.3 2022-08-26 20:02:57 +02:00
Kuba Szczodrzyński
48aa809c98 [beken-72xx] Move to external lwIP v2.0.2 2022-08-25 23:34:54 +02:00
Kuba Szczodrzyński
4a722d4069 [core] Add option to debug FlashDB 2022-08-25 13:45:01 +02:00
Kuba Szczodrzyński
07aca2a0e7 [beken-72xx] Unprotect flash after initializing 2022-08-25 13:44:24 +02:00
Kuba Szczodrzyński
10000d9b6c [beken-72xx] Fix enabling WiFi AP mode only 2022-08-18 14:15:41 +02:00
Kuba Szczodrzyński
f0e247f31e [core] Print error if WiFi mode changing failed 2022-08-18 14:15:05 +02:00
Kuba Szczodrzyński
593dec5e88 [beken-72xx] Update ltchiptool to fix corrupted output binaries 2022-08-17 22:40:56 +02:00
Kuba Szczodrzyński
c45b86c993 [beken-72xx] Fix enabling AP mode 2022-08-17 15:17:14 +02:00
Kuba Szczodrzyński
02f01f2199 [beken-72xx] Initialize WiFiData when needed, null-terminate SSIDs 2022-08-17 11:24:32 +02:00
Kuba Szczodrzyński
88b1adc2d6 [github] Fix docs custom domain deployment 2022-08-16 23:24:20 +02:00
Kuba Szczodrzyński
a0a0e6775b [beken-72xx] Improve WiFi status recognition, fix null pointer access 2022-08-16 22:52:05 +02:00
Kuba Szczodrzyński
4096253694 [github] Add custom domain for docs workflow 2022-08-16 18:22:29 +02:00
Kuba Szczodrzyński
33ba44ebda [core] Move running OTA detection to family code 2022-08-16 18:01:38 +02:00
Kuba Szczodrzyński
9b4cf2a92b [core] Pin GitHub packages to known-good versions 2022-08-07 19:48:54 +02:00
Kuba Szczodrzyński
e6b915d8e3 [tools] Move dumptool to ltchiptool 2022-08-06 19:33:21 +02:00
Kuba Szczodrzyński
bb73fb5f55 [core] Force updating ltchiptool to fix linking problems 2022-08-06 17:07:28 +02:00
Kuba Szczodrzyński
10c5945afb [core] Allow exporting UF2 under a custom filename 2022-08-06 17:06:08 +02:00
Kuba Szczodrzyński
0bd613d556 [core] Support upload_flags property 2022-08-06 16:50:12 +02:00
Kuba Szczodrzyński
aae2f65b9e [release] v0.9.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-08-06 13:42:37 +02:00
Kuba Szczodrzyński
d02197474e [docs] Update ESPHome docs, add cloudcutter guide 2022-08-06 13:37:47 +02:00
Kuba Szczodrzyński
41b37e9c24 [core] Update ltchiptool to v1.4.0 2022-08-06 13:06:44 +02:00
Kuba Szczodrzyński
b03400fac2 [beken-72xx] Implement OTA API, add Update debugging 2022-08-06 11:36:53 +02:00
Kuba Szczodrzyński
3e808f7b6b [beken-72xx] Write OTA package to UF2 image 2022-08-05 20:29:48 +02:00
Kuba Szczodrzyński
1e3a8971fb [core] Migrate tools to ltchiptool 2022-08-05 20:29:45 +02:00
Kuba Szczodrzyński
d8c0105b97 [examples] Fix PinScan compilation on realtek-ambz 2022-08-03 12:48:50 +02:00
Kuba Szczodrzyński
b3689cbac8 [beken-72xx] Specify keys for OTA packaging 2022-08-01 19:56:34 +02:00
Kuba Szczodrzyński
1b5d6472f7 [examples] Fix PinScan telnet functionality 2022-08-01 11:28:56 +02:00
Kuba Szczodrzyński
e9511c507a [core] Allow disabling LT logger programmatically 2022-08-01 11:28:16 +02:00
Kuba Szczodrzyński
365f64ded5 [beken-72xx] Make Serial.end() disable hardware UART 2022-08-01 11:27:57 +02:00
Kuba Szczodrzyński
3601fa63d8 [examples] Add PinScan example 2022-08-01 10:52:58 +02:00
Kuba Szczodrzyński
5d00ddf516 [beken-72xx] Fix detaching interrupt handler 2022-07-31 23:25:12 +02:00
Kuba Szczodrzyński
0051453cad [beken-72xx] Implement interrupts 2022-07-31 21:35:02 +02:00
Kuba Szczodrzyński
7920ea2dda [beken-72xx] Fix SerialClass character reading 2022-07-31 14:52:09 +02:00
Kuba Szczodrzyński
9416e45a75 [docs] Restructure documentation, clarify some parts 2022-07-31 13:05:24 +02:00
Kuba Szczodrzyński
98a65c81af [boards] Add usage info to board README 2022-07-31 12:47:24 +02:00
Kuba Szczodrzyński
8e1f06e79b [boards] Add LSC LMA35 board, update boardgen 2022-07-30 19:48:58 +02:00
Kuba Szczodrzyński
58a09f453d [realtek-ambz] Fix errors related to lwIP 2022-07-30 17:18:46 +02:00
Kuba Szczodrzyński
7dc69982b6 [beken-72xx] Fix putchar_p UART index 2022-07-30 15:42:30 +02:00
Kuba Szczodrzyński
46e4041ed8 [beken-72xx] Mark mDNS as broken 2022-07-28 22:18:04 +02:00
Kuba Szczodrzyński
0e129130b1 [core] Print errors about starting main task 2022-07-28 20:29:53 +02:00
Kuba Szczodrzyński
33c9868f90 [core] Remove duplicated WMath functions 2022-07-28 13:46:42 +02:00
Kuba Szczodrzyński
fb04b1830e [release] v0.8.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-07-26 11:11:52 +02:00
Kuba Szczodrzyński
9509194bd0 [beken-72xx] Correct sys_config.h path 2022-07-26 11:11:14 +02:00
Kuba Szczodrzyński
60322a243a [boards] Fix missing vendor field in generic boards 2022-07-26 11:10:45 +02:00
Kuba Szczodrzyński
4ed7067537 [boards] Add generic definitions for each family 2022-07-25 19:59:53 +02:00
Kuba Szczodrzyński
69086e8fba [boards] Rename small/large base boards to show size 2022-07-25 19:58:16 +02:00
Kuba Szczodrzyński
a4b63bb037 [tools] Make util/markdown.py pre-Python 3.10 compatible 2022-07-21 23:08:53 +02:00
Kuba Szczodrzyński
5ffb2f6619 [boards] Add BW15 board definition, update boardgen 2022-07-21 23:08:49 +02:00
Kuba Szczodrzyński
41eaf9b9e4 [realtek-ambz2] Add initial AmebaZ2 support 2022-07-21 23:08:45 +02:00
Kuba Szczodrzyński
28bb777399 [core] Move family config to separate dir, define Realtek parent 2022-07-21 23:08:39 +02:00
Kuba Szczodrzyński
f375a35cc8 [core] Move library include wrappers to common 2022-07-21 23:08:39 +02:00
Kuba Szczodrzyński
1d41d84083 [boards] Move common partitions to base JSON 2022-07-21 23:08:28 +02:00
Kuba Szczodrzyński
357be177fc [core] Make WiFi sleep & TX power methods weak 2022-07-12 21:12:54 +02:00
Kuba Szczodrzyński
23c3335de8 [beken-72xx] Use Hostapd MD5 implementation 2022-07-12 13:05:02 +02:00
Kuba Szczodrzyński
963b164783 [beken-72xx] Define struct ip_addr as IPv4 2022-07-12 13:01:46 +02:00
Kuba Szczodrzyński
0c22a02641 [beken-72xx] Use mbedTLS MD5 implementation 2022-07-12 12:48:27 +02:00
Kuba Szczodrzyński
10cb5c2c76 [core] Add missing C++ stdlib includes 2022-07-12 12:48:02 +02:00
Kuba Szczodrzyński
6d36c9ef7b [boards] Add remaining WB2x and WB3x boards 2022-07-11 16:42:45 +02:00
Kuba Szczodrzyński
aed97a5e92 [boards] Add remaining WR2x and WR3x boards 2022-07-11 14:23:10 +02:00
Kuba Szczodrzyński
b6008fc9bb [docs] Add family flashing guides 2022-07-11 12:03:28 +02:00
Kuba Szczodrzyński
f9359679ad [docs] Add Getting started guide 2022-07-11 10:54:16 +02:00
Kuba Szczodrzyński
052d7be1a9 [beken-72xx] Move to GNU++11 2022-07-10 20:18:12 +02:00
Kuba Szczodrzyński
ead577a116 [release] v0.7.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-07-10 19:50:07 +02:00
Kuba Szczodrzyński
ccf7d972ce [beken-72xx] Implement WiFi events 2022-07-10 19:49:14 +02:00
Kuba Szczodrzyński
b2df9e6a8b [beken-72xx] Use chip ID based on MAC address 2022-07-07 18:01:35 +02:00
Kuba Szczodrzyński
e37014ebd6 [beken-72xx] Wrap BkDriverFlash for correct partition offsets 2022-07-07 17:45:58 +02:00
Kuba Szczodrzyński
c96ef2ff7a [beken-72xx] Add draft BK7231N support 2022-07-07 17:18:25 +02:00
Kuba Szczodrzyński
c19dd0d87e [beken-72xx] Fix WiFi using DHCP 2022-07-07 17:15:52 +02:00
Kuba Szczodrzyński
02f972bd5f [boards] Add CB2S board definition 2022-07-07 14:57:35 +02:00
Kuba Szczodrzyński
15facd8866 [realtek-ambz] Fix reading MAC address without WiFi enabled 2022-07-05 13:16:51 +02:00
Kuba Szczodrzyński
cf584c3bd6 [tools] Fix link2bin with long command line 2022-07-04 22:37:25 +02:00
Kuba Szczodrzyński
4744351964 [core] Allow changing SDK, logger & Serial output ports 2022-07-04 13:56:03 +02:00
Kuba Szczodrzyński
31b753ab5a [core] Allow configuring silencing of SDK output 2022-07-04 13:55:58 +02:00
Kuba Szczodrzyński
3cb944dde2 [realtek-ambz] Implement SoftwareSerial class 2022-07-03 22:54:18 +02:00
Kuba Szczodrzyński
1b4265f522 [docs] Add TODO list 2022-07-03 19:50:27 +02:00
Kuba Szczodrzyński
5d16338faa [realtek-ambz] Add missing #define 2022-07-03 19:48:24 +02:00
Kuba Szczodrzyński
673b8233e5 [realtek-ambz] Refactor Serial class 2022-07-03 19:15:33 +02:00
Kuba Szczodrzyński
0dd2e2583b [core] Rename init() to initArduino() 2022-07-03 16:49:46 +02:00
Kuba Szczodrzyński
c19f24cbfd [beken-72xx] Implement WiFi Access Point mode 2022-07-01 19:16:48 +02:00
Kuba Szczodrzyński
cfb6830d95 [beken-72xx] Revert scanning before connecting 2022-07-01 19:15:14 +02:00
Kuba Szczodrzyński
27dea356cf [beken-72xx] Fix station with static IP 2022-07-01 19:14:41 +02:00
Kuba Szczodrzyński
352a425c4f [beken-72xx] Wait for init_thread to start TCP/IP 2022-07-01 18:49:14 +02:00
Kuba Szczodrzyński
4beaa79d02 [beken-72xx] Revert to standard station config struct 2022-07-01 18:48:11 +02:00
Kuba Szczodrzyński
e54eab83f2 [beken-72xx] Use station data structs in WiFiData 2022-07-01 16:17:48 +02:00
Kuba Szczodrzyński
e74a491b4a [core] Move SSID & passphrase validation to common 2022-07-01 15:36:49 +02:00
Kuba Szczodrzyński
a9415c1db7 [core] Fix compilation and linking on Linux 2022-07-01 12:47:32 +02:00
Kuba Szczodrzyński
a83959ab5f [release] v0.6.1
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-06-30 21:22:12 +02:00
Kuba Szczodrzyński
b1896002e5 [boards] Add missing URL field in generic-native.json 2022-06-30 21:21:29 +02:00
Kuba Szczodrzyński
cc39de9b04 [core] Fix beken-72xx-arduino.py formatting 2022-06-30 20:47:54 +02:00
Kuba Szczodrzyński
a286b5d067 [release] v0.6.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-06-30 20:34:38 +02:00
Kuba Szczodrzyński
f071f57abf [beken-72xx] Enable mDNS responder, set IGMP flag on netif 2022-06-30 20:33:08 +02:00
Kuba Szczodrzyński
0e0fe334cd [beken-72xx] Scan networks before connecting, if needed 2022-06-30 20:30:52 +02:00
Kuba Szczodrzyński
23410c618d [beken-72xx] Mark SSL client as broken 2022-06-30 19:35:43 +02:00
Kuba Szczodrzyński
b44898f08d [core] Update verbose messages in MbedTLSClient 2022-06-30 19:34:45 +02:00
Kuba Szczodrzyński
165f356439 [core] Fix MbedTLSClient memory leak 2022-06-30 19:33:56 +02:00
Kuba Szczodrzyński
1d0df2936b [beken-72xx] Increase main task stack size 2022-06-30 19:32:37 +02:00
Kuba Szczodrzyński
1684d63e04 [beken-72xx] Fix lwip_ioctl() given wrong argument size 2022-06-30 16:28:29 +02:00
Kuba Szczodrzyński
cf5dfb43d6 [beken-72xx] Fix WiFi station connection 2022-06-29 19:00:15 +02:00
Kuba Szczodrzyński
ded6638ef6 [beken-72xx] Implement broken WiFi station 2022-06-28 22:49:28 +02:00
Kuba Szczodrzyński
a8228851d9 [realtek-ambz] Make WiFi SDK code less verbose 2022-06-27 21:04:22 +02:00
Kuba Szczodrzyński
633289def6 [realtek-ambz] Fix reporting invalid networks after scanning 2022-06-27 21:04:19 +02:00
Kuba Szczodrzyński
f4ff13d6b7 [realtek-ambz] Move WiFiClient and WiFiServer to NetUtils library 2022-06-27 21:04:15 +02:00
Kuba Szczodrzyński
0493ec2245 [beken-72xx] Implement WiFi scanning 2022-06-27 13:03:52 +02:00
Kuba Szczodrzyński
4c6bbb8aae [realtek-ambz] Simplify WiFi scan result handler 2022-06-27 13:01:06 +02:00
Kuba Szczodrzyński
b625f636be [libs] Fix WiFiMulti compiling with debugging enabled 2022-06-27 12:57:33 +02:00
Kuba Szczodrzyński
3bfd62d075 [boards] Update BK7231T partition layout 2022-06-26 19:12:12 +02:00
Kuba Szczodrzyński
b405adb359 [realtek-ambz] Add a wrapper around WiFi::mode() 2022-06-26 18:10:13 +02:00
Kuba Szczodrzyński
accf37de39 [core] Specify toolchain prefix in board.json 2022-06-26 16:04:49 +02:00
Kuba Szczodrzyński
582eed3be8 [core] Fix return types for some methods, check feature flags 2022-06-26 16:03:30 +02:00
Kuba Szczodrzyński
35d5ec03a3 [core] Fix platform.py for frameworks without package 2022-06-26 16:00:55 +02:00
Kuba Szczodrzyński
737c627d44 [boards] Add generic-native board template 2022-06-26 14:42:43 +02:00
Kuba Szczodrzyński
84a3120c7f [docs] Support generic boards in update_docs.py 2022-06-26 14:41:55 +02:00
Kuba Szczodrzyński
99b9ed1181 [docs] Make update_docs.py errors clearer, check for build.variant 2022-06-26 14:41:22 +02:00
Kuba Szczodrzyński
0d636a3285 [docs] Clarify family configuration variables 2022-06-25 10:19:43 +02:00
Kuba Szczodrzyński
79ccd47f71 [beken-72xx] Fix compilation without WiFi implemented 2022-06-25 10:19:20 +02:00
Kuba Szczodrzyński
3ffc70abe5 [realtek-ambz] Fix reading chip MAC address from eFuse 2022-06-24 21:47:00 +02:00
Kuba Szczodrzyński
f4f2eda481 [docs] Fix RTL8710BX clock speed & chip ID 2022-06-24 21:13:58 +02:00
Kuba Szczodrzyński
f4b2c74b0e [realtek-ambz] Fix issues without ADC2, use correct 468k linker script 2022-06-24 21:13:35 +02:00
Kuba Szczodrzyński
111e5a66b6 [core] Fix import errors on latest PIO Core 2022-06-24 21:11:19 +02:00
Kuba Szczodrzyński
b90723130d [boards] Add BW12 board, update boardgen to v0.4.2 2022-06-24 20:05:44 +02:00
Kuba Szczodrzyński
e6179761d4 [boards] Remove unneeded linker scripts 2022-06-24 20:04:16 +02:00
Kuba Szczodrzyński
b371fd3468 [tools] Fix merge_dicts for d2 immutability 2022-06-24 12:18:36 +02:00
Kuba Szczodrzyński
c8fccdbb47 [realtek-ambz] Move common WiFi code to LT API 2022-06-23 23:30:28 +02:00
Kuba Szczodrzyński
4876401809 [beken-72xx] Implement Serial classes 2022-06-23 17:06:44 +02:00
Kuba Szczodrzyński
45af7c188a [beken-72xx] Add UF2 uploading based on bk7231tools 2022-06-23 14:23:22 +02:00
Kuba Szczodrzyński
236e9ccda6 [beken-72xx] Generate UF2 firmware images 2022-06-23 13:32:31 +02:00
Kuba Szczodrzyński
94e8b9f87e [beken-72xx] Fix suppressing SDK output during init 2022-06-22 23:37:12 +02:00
Kuba Szczodrzyński
c9580d6627 [core] Fix hexdump() line formatting 2022-06-22 23:36:36 +02:00
Kuba Szczodrzyński
87929373e2 [realtek-ambz] Declare all wrapped printf() methods 2022-06-21 23:42:02 +02:00
Kuba Szczodrzyński
4e2aec9ced [tools] Fix link2bin issues when having spaces in project name 2022-06-21 19:25:44 +02:00
Kuba Szczodrzyński
fa7957f5e2 [beken-72xx] Fix linking with SDK framework 2022-06-21 18:53:02 +02:00
Kuba Szczodrzyński
4f15805246 [beken-72xx] Implement analogRead() 2022-06-21 18:49:12 +02:00
Kuba Szczodrzyński
cf67c815e9 [docs] Update LT API main page 2022-06-20 21:43:06 +02:00
Kuba Szczodrzyński
5533dc807f [beken-72xx] Implement Wiring core, digital I/O and PWM 2022-06-20 21:34:11 +02:00
Kuba Szczodrzyński
ac2aabe6e3 [beken-72xx] Implement API reboot() and getCpuFreq() 2022-06-20 21:29:02 +02:00
Kuba Szczodrzyński
c6a428ddd4 [core] Rename PinInfo features fields 2022-06-20 21:28:32 +02:00
Kuba Szczodrzyński
a83a845d5a [realtek-ambz] Move common digital/analog utils into core 2022-06-20 21:28:24 +02:00
Kuba Szczodrzyński
c91b188f96 [beken-72xx] Fix Arduino delay(), fix reading heap size 2022-06-20 15:54:20 +02:00
Kuba Szczodrzyński
53a6f4593e [core] Add macros for wrapping and disabling SDK printf() 2022-06-20 15:54:13 +02:00
Kuba Szczodrzyński
0dda769d0f [beken-72xx] Add preliminary Arduino framework core 2022-06-19 23:53:15 +02:00
Kuba Szczodrzyński
848df066c4 [boards] Add WB2L variant definition 2022-06-19 23:42:40 +02:00
Kuba Szczodrzyński
b4005d8bad [realtek-ambz] Standardize all stdio output with printf library 2022-06-19 23:30:40 +02:00
Kuba Szczodrzyński
34c0df2761 [core] Disable FAL info logging 2022-06-19 18:38:22 +02:00
Kuba Szczodrzyński
cedfc2c8db [core] Fix compilation warnings 2022-06-19 17:33:02 +02:00
Kuba Szczodrzyński
c9fe63ec7b [core] Add defines for enabling implemented features 2022-06-19 17:31:14 +02:00
Kuba Szczodrzyński
2fedcb61b5 [core] Migrate Flash library to common code based on FAL 2022-06-19 17:23:16 +02:00
Kuba Szczodrzyński
272b033253 [core] Provide entire flash as "root" partition 2022-06-19 14:18:13 +02:00
Kuba Szczodrzyński
cae03d54d6 [core] Move startup code to libretuya/common 2022-06-19 14:13:51 +02:00
Kuba Szczodrzyński
e70f1a591f [core] Migrate to auto-generated variant definitions 2022-06-18 23:19:28 +02:00
Kuba Szczodrzyński
0e768ac6e4 [core] Move Arduino flags to arduino-common 2022-06-18 21:57:51 +02:00
Kuba Szczodrzyński
bc056eaca2 [docs] Add auto-generated module support page 2022-06-18 21:08:29 +02:00
Kuba Szczodrzyński
a6ad84a3b8 [docs] Use note tag and include-markdown 2022-06-18 21:05:31 +02:00
Kuba Szczodrzyński
a5b953dc80 [core] Add a prefix to ChipFamily enum 2022-06-18 21:04:17 +02:00
Kuba Szczodrzyński
d446b05387 [boards] Add WB2L pinout 2022-06-15 23:23:45 +02:00
Kuba Szczodrzyński
e864e5ef24 [boards] Move PCB definitions to subdir, use base IC pinouts 2022-06-15 23:20:59 +02:00
Kuba Szczodrzyński
e669507759 [beken-72xx] Support C++11 2022-06-14 18:54:31 +02:00
Kuba Szczodrzyński
e50c04cbe1 [beken-72xx] Don't init everything by default, make it quiet 2022-06-14 18:18:49 +02:00
Kuba Szczodrzyński
56dd57f4a0 [beken-72xx] Remove unused components 2022-06-14 17:28:00 +02:00
Kuba Szczodrzyński
f2abb564c7 [beken-72xx] Fix BK7231S 1.0.5 bootloader compatibility 2022-06-14 16:33:13 +02:00
Kuba Szczodrzyński
4e7abb2350 [beken-72xx] Move family fixups to parent 2022-06-14 16:11:25 +02:00
Kuba Szczodrzyński
540e499389 [beken-72xx] Implement SDK binary building 2022-06-14 15:39:28 +02:00
Kuba Szczodrzyński
191a2bdd2a [docs] Add debugging info for RPi 4, fix GPIO numbers 2022-06-14 11:43:07 +02:00
Kuba Szczodrzyński
4d1c8357b4 [realtek-ambz] Move C++ flags to SDK builder 2022-06-13 17:08:20 +02:00
Kuba Szczodrzyński
85b5b1df91 [beken-72xx] Add base BK7231 support config 2022-06-13 16:58:46 +02:00
Kuba Szczodrzyński
c3e2c105d4 [core] Build with properties from Family object 2022-06-12 12:49:04 +02:00
Kuba Szczodrzyński
dba602a081 [tools] Add Beken binary crypto utility 2022-06-11 23:00:00 +02:00
Kuba Szczodrzyński
c3f2ce57f0 [tools] Move functions to utilities, add universal CRC16 class 2022-06-11 18:25:10 +02:00
Kuba Szczodrzyński
bbf8e0b6b6 [realtek-ambz] Better handle WiFi events 2022-06-06 21:45:18 +02:00
Kuba Szczodrzyński
03ab020dc8 [realtek-ambz] Fix ADC reading 2022-06-06 21:44:54 +02:00
Kuba Szczodrzyński
9c1f3225a6 [core] Refactor LibreTuyaAPI into separate units 2022-06-06 16:17:02 +02:00
Kuba Szczodrzyński
f72dd3b7d7 [realtek-ambz] Fix PWM output 2022-06-06 14:52:11 +02:00
Kuba Szczodrzyński
48f6210607 [realtek-ambz] Add gpioRecover() to disable SWD 2022-06-04 22:32:36 +02:00
Kuba Szczodrzyński
6f9be2b139 [realtek-ambz] Fix float abi linking errors 2022-06-04 20:40:09 +02:00
Kuba Szczodrzyński
3217525873 [realtek-ambz] Implement attachInterruptParam 2022-06-04 20:39:24 +02:00
Kuba Szczodrzyński
e26972bd2f [core] Fix OTA not resetting buffer after partial writes 2022-06-04 16:36:37 +02:00
Kuba Szczodrzyński
bf31533306 [core] Add LT.getCpuFreq() method 2022-06-04 16:36:13 +02:00
Kuba Szczodrzyński
0ca0577667 [core] Add cbuf and StreamString libraries 2022-06-04 14:12:36 +02:00
Kuba Szczodrzyński
0e797e7e04 [core] Fix logger color formatting 2022-06-04 13:37:29 +02:00
Kuba Szczodrzyński
a661ef3a3f [core] Add more compat code 2022-06-04 13:37:15 +02:00
Kuba Szczodrzyński
17dc1dcf8c [core] Allow specifying custom UF2 firmware name and version 2022-06-03 12:11:12 +02:00
Kuba Szczodrzyński
4c27aa57b1 [core] Add MD5 library 2022-06-03 12:08:41 +02:00
Kuba Szczodrzyński
a21c07fa03 [release] v0.5.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-06-02 23:06:11 +02:00
Kuba Szczodrzyński
bdffa7ef53 [core] Add UF2-based uploader 2022-06-02 23:05:35 +02:00
Kuba Szczodrzyński
22d40825bb [tools] Extract common utilities to separate modules 2022-06-02 22:22:23 +02:00
Kuba Szczodrzyński
50f26f546c [core] Use term "family" instead of "platform" 2022-06-02 22:22:19 +02:00
Kuba Szczodrzyński
9c7ea46ec3 [core] Build UF2 OTA image after linking 2022-06-02 22:21:51 +02:00
Kuba Szczodrzyński
5b4cf53d8a [tools] uf2ota: embed build date in extension tags 2022-06-02 14:14:16 +02:00
Kuba Szczodrzyński
79a701a4d4 [tools] uf2ota: fix for Python 3.7, fix Windows path compatibility 2022-06-02 14:12:03 +02:00
Kuba Szczodrzyński
81897e634c [realtek-ambz] Export both OTA images after linking 2022-06-01 21:41:04 +02:00
Kuba Szczodrzyński
3e11da4dd4 [docs] Add resources page 2022-05-31 12:06:24 +02:00
Kuba Szczodrzyński
12aa7fef04 [docs] Move sections from README to docs, add uf2families.json 2022-05-31 11:54:05 +02:00
Kuba Szczodrzyński
1ea6420bbc [core] Add Update library 2022-05-30 22:31:04 +02:00
Kuba Szczodrzyński
f3f1f36525 [core] Add uf2ota library source 2022-05-30 22:23:40 +02:00
Kuba Szczodrzyński
dee9a98cc3 [core] Update OTA API 2022-05-30 21:59:22 +02:00
Kuba Szczodrzyński
3345ce3fb9 [docs] Add missing documents to SUMMARY.md 2022-05-28 20:13:01 +02:00
Kuba Szczodrzyński
de70583838 [core] Put full UF2 Family ID in ChipFamily 2022-05-28 20:09:10 +02:00
Kuba Szczodrzyński
a43a737004 [realtek-ambz] Implement API class methods, fix CPU clock 2022-05-28 19:41:44 +02:00
Kuba Szczodrzyński
1f6899354f [core] Add LT class API methods 2022-05-28 19:39:43 +02:00
Kuba Szczodrzyński
9110a0c47e [docs] Document the UF2 OTA format 2022-05-28 14:48:07 +02:00
Kuba Szczodrzyński
b549790798 [tools] Implement UF2 binary patching, add dumping images 2022-05-28 14:47:45 +02:00
Kuba Szczodrzyński
5df430f3be [tools] Add UF2 OTA writer tool 2022-05-27 20:53:08 +02:00
Kuba Szczodrzyński
f3e8bcd74a [realtek-ambz] Add KVS partition, update boardgen 2022-05-27 15:29:46 +02:00
Kuba Szczodrzyński
4b050e11cf [core] Add dynamic FAL_PART_TABLE generation 2022-05-27 15:29:21 +02:00
Kuba Szczodrzyński
7ddbc09564 [core] Workaround LwIPmDNS compilation 2022-05-26 14:24:51 +02:00
Kuba Szczodrzyński
d3d62f80fd [realtek-ambz] Fix WiFi encryption type conversion 2022-05-26 14:24:29 +02:00
Kuba Szczodrzyński
bc7dbe6eec [api] Fix hexdump() default parameters 2022-05-25 12:27:52 +02:00
Kuba Szczodrzyński
e625f55353 [api] Add hexdump() utility 2022-05-24 17:55:12 +02:00
Kuba Szczodrzyński
aeebff9d5d [realtek-ambz] Fix WiFiEvents linking when not needed 2022-05-24 17:47:45 +02:00
Kuba Szczodrzyński
9a3c077ef1 [core] Add FlashDB KVS library 2022-05-24 17:43:30 +02:00
Kuba Szczodrzyński
91ae692058 [core] Fix gathering external library dependencies 2022-05-24 17:30:14 +02:00
505 changed files with 33450 additions and 6059 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
ko_fi: kuba2k2
custom:
- "https://paypal.me/kuba2k2"

View File

@@ -13,10 +13,30 @@ jobs:
- name: Checkout main
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install ltchiptool
run: pip install ltchiptool
- name: Generate static JSON files
run: |
mkdir -p site/
python docs/build_json.py
cp *.json site/
- name: Set custom domain
run: |
mkdir -p site/
echo docs.libretuya.ml > site/CNAME
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
uses: libretuya/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: mkdocs.yml
EXTRA_PACKAGES: build-base doxygen
REQUIREMENTS: docs/requirements.txt
CUSTOM_DOMAIN: docs.libretuya.ml

2
.gitignore vendored
View File

@@ -256,5 +256,5 @@ cython_debug/
# mkdocs
xml/
ltapi/
ltambz/
hashChanges.yaml
.piopm

198
README.md
View File

@@ -12,16 +12,17 @@
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/kuba2k2/platform/libretuya.svg)](https://registry.platformio.org/platforms/kuba2k2/libretuya)
![RTL8710BN](https://img.shields.io/badge/-rtl8710bn-blue)
![BK7231](https://img.shields.io/badge/-bk7231-blue)
</div>
PlatformIO development platform for IoT modules manufactured by Tuya Inc.
The main goal of this project is to provide a usable build environment for IoT developers. While also providing vendor SDKs as PlatformIO cores,
the project focuses on developing working Arduino-compatible cores for supported platforms. The cores are inspired by Espressif's official core for ESP32,
which should make it easier to port/run existing ESP apps on Tuya IoT (and 3-rd party) platforms.
the project focuses on developing working Arduino-compatible cores for supported families. The cores are inspired by Espressif's official core for ESP32,
which should make it easier to port/run existing ESP apps on Tuya IoT (and 3-rd party) modules.
LibreTuya also provides a common interface for all platform implementations. The interface is based on ESP32 official libraries.
**There's an [ESPHome port](https://docs.libretuya.ml/docs/projects/esphome/) based on LibreTuya, which supports BK7231 and RTL8710B chips.**
**Note:** this project is work-in-progress.
@@ -30,173 +31,40 @@ LibreTuya also provides a common interface for all platform implementations. The
1. [Install PlatformIO](https://platformio.org/platformio-ide)
2. `platformio platform install https://github.com/kuba2k2/libretuya`
3. Create a project, build it and upload!
4. See the [docs](https://kuba2k2.github.io/libretuya/) for any questions/problems.
## Board List
A (mostly) complete* list of Tuya wireless module boards.
&nbsp; | Module Name | MCU | Flash | RAM | Pins** | Wi-Fi | BLE | Platform name
------------------------------|------------------------------------------------------------------------------------------------|-------------------------|-------|----------|-------------|-------|-----|---------------
❌ | [WB1S](https://developer.tuya.com/en/docs/iot/wb1s?id=K9duevbj3ol4x) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ✔️ | -
❌ | [WB2L](https://developer.tuya.com/en/docs/iot/wb2l-datasheet?id=K9duegc9bualu) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ✔️ | -
❌ | [WB2S](https://developer.tuya.com/en/docs/iot/wb2s-module-datasheet?id=K9ghecl7kc479) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | -
❌ | [WB3L](https://developer.tuya.com/en/docs/iot/wb3l-module-datasheet?id=K9duiggw2v8sp) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ✔️ | -
❌ | [WB3S](https://developer.tuya.com/en/docs/iot/wb3s-module-datasheet?id=K9dx20n6hz5n4) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 16 (11 I/O) | ✔️ | ✔️ | -
❌ | [WB3S-IPEX](https://developer.tuya.com/en/docs/iot/wb3sipex-module-datasheet?id=K9irq0laun21z) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 16 (11 I/O) | ✔️ | ✔️ | -
❌ | [WB8P](https://developer.tuya.com/en/docs/iot/wb8p-module-datasheet?id=K9fwx4f89tvzd) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 10 (8 I/O) | ✔️ | ✔️ | -
❌ | [WBLC5](https://developer.tuya.com/en/docs/iot/wblc5-module-datasheet?id=K9duilns1f3gi) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 6 (3 I/O) | ✔️ | ✔️ | -
❌ | [WBLC9](https://developer.tuya.com/en/docs/iot/wblc9-module-datasheet?id=K9hgglry2jp5h) | BK7231T @ 120 MHz | 2 MiB | 256 KiB | 8 (6 I/O) | ✔️ | ✔️ | -
❌ | [CB1S](https://developer.tuya.com/en/docs/iot/cb1s-module-datasheet?id=Kaij1abmwyjq2) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ✔️ | -
❌ | [CB2L](https://developer.tuya.com/en/docs/iot/cb2l-module-datasheet?id=Kai2eku1m3pyl) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ✔️ | -
❌ | [CB2S](https://developer.tuya.com/en/docs/iot/cb2s-module-datasheet?id=Kafgfsa2aaypq) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | -
❌ | [CB3L](https://developer.tuya.com/en/docs/iot/cb3l-module-datasheet?id=Kai51ngmrh3qm) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 16 (11 I/O) | ✔️ | ✔️ | -
❌ | [CB3S](https://developer.tuya.com/en/docs/iot/cb3s?id=Kai94mec0s076) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 22 (14 I/O) | ✔️ | ✔️ | -
❌ | [CB3S-NL](https://developer.tuya.com/en/docs/iot/CB3S-NL-module-datasheet?id=Kbaesan0vyoms) | BK7231NL (?) @ 120 MHz | 2 MiB | 256 KiB | 22 (14 I/O) | ✔️ | ✔️ | -
❌ | [CB3SE](https://developer.tuya.com/en/docs/iot/CB3SE-Module-Datasheet?id=Kanoiluul7nl2) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 22 (17 I/O) | ✔️ | ✔️ | -
❌ | [CB8P](https://developer.tuya.com/en/docs/iot/cb8p-module-datasheet?id=Kahvig14r1yk9) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 10 (8 I/O) | ✔️ | ✔️ | -
❌ | [CBLC5](https://developer.tuya.com/en/docs/iot/cblc5-module-datasheet?id=Ka07iqyusq1wm) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 6 (3 I/O) | ✔️ | ✔️ | -
❌ | [CBLC9](https://developer.tuya.com/en/docs/iot/cblc9-module-datasheet?id=Ka42cqnj9r0i5) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 8 (6 I/O) | ✔️ | ✔️ | -
❌ | [CBU](https://developer.tuya.com/en/docs/iot/cbu-module-datasheet?id=Ka07pykl5dk4u) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 21 (18 I/O) | ✔️ | ✔️ | -
❌ | [CBU-IPEX](https://developer.tuya.com/en/docs/iot/cbuipex-module-datasheet?id=Kaedsyurckrhu) | BK7231N @ 120 MHz | 2 MiB | 256 KiB | 21 (18 I/O) | ✔️ | ✔️ | -
❌ | [CBU-NL](https://developer.tuya.com/en/docs/iot/CBU-NL-module-datasheet?id=Kbaeq6j53y0yg) | BK7231N (?) @ 120 MHz | 2 MiB | 256 KiB | 21 (18 I/O) | ✔️ | ✔️ | -
❌ | [WR1](https://developer.tuya.com/en/docs/iot/wifiwr1module?id=K9605tc0k90t3) | RTL8710BN @ 125 MHz | 1 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [WR1-IPEX](https://developer.tuya.com/en/docs/iot/wifiwr1ipexmodule?id=K9605t977tx5u) | RTL8710BN @ 125 MHz | 1 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [WR1E](https://developer.tuya.com/en/docs/iot/wr1e?id=K96smbbeycxtf) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [WR2](https://developer.tuya.com/en/docs/iot/wifiwr2module?id=K9605tko0juc3) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | -
❌ | [WR2E](https://developer.tuya.com/en/docs/iot/wr2e?id=K97scnsjhue4h) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | -
❌ | [WR2L](https://developer.tuya.com/en/docs/iot/wifiwr2lmodule?id=K9605tnbj7gva) | RTL8710BX @ 62.5 MHz | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | -
❌ | [WR2LE](https://developer.tuya.com/en/docs/iot/wr2le?id=K9eio9y9e8i8c) | RTL8710BX @ 62.5 MHz | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | -
[`wr3`](boards/wr3/README.md) | [WR3](https://developer.tuya.com/en/docs/iot/wr3-module-datasheet?id=K9g3ainzbj9z1) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | `realtek-ambz`
❌ | [WR3E](https://developer.tuya.com/en/docs/iot/wr3e-module-datasheet?id=K9elwlqbfosbc) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WR3L](https://developer.tuya.com/en/docs/iot/wifiwr3lmodule?id=K9605tt0kveqm) | RTL8710BX @ 125 MHz (?) | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WR3LE](https://developer.tuya.com/en/docs/iot/wr3le?id=K986l7a1ha8tm) | RTL8710BX @ 62.5 MHz | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WR3N](https://developer.tuya.com/en/docs/iot/wr3n-datasheet?id=K98zdx31ztdge) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 16 (10 I/O) | ✔️ | ❌ | -
❌ | [WR4](https://developer.tuya.com/en/docs/iot/wifiwr4module?id=K9605tvu78p3e) | RTL8710BN @ 125 MHz | 1 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WR5E](https://developer.tuya.com/en/docs/iot/wr5e?id=K986r9pxqxa8i) | RTL8710BX @ 62.5 MHz | 2 MiB | 256 KiB | 15 (12 I/O) | ✔️ | ❌ | -
❌ | [WR6-H](https://developer.tuya.com/en/docs/iot/wr6h-module-datasheet?id=K9pork8eeowgl) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 18 (12 I/O) | ✔️ | ❌ | -
❌ | [WR6](https://developer.tuya.com/en/docs/iot/wr6-datasheet?id=K97rp45u6gff9) | RTL8710BN @ 125 MHz | 2 MiB | 256 KiB | 18 (12 I/O) | ✔️ | ❌ | -
❌ | [WRG1](https://developer.tuya.com/en/docs/iot/wr6-datasheet?id=K97rp45u6gff9) | RTL8711AM @ 166 MHz | 4 MiB | 2048 KiB | 25 (20 I/O) | ✔️ | ❌ | -
❌ | [XR1](https://developer.tuya.com/en/docs/iot/xr1?id=K9lq3y9xo0zkx) | XR809 @ 160 MHz | 2 MiB | 384 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [XR1-IPEX](https://developer.tuya.com/en/docs/iot/xr1ipex-module-datasheet?id=K9razqu9gqele) | XR809 @ 160 MHz | 2 MiB | 384 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [XR3](https://developer.tuya.com/en/docs/iot/xr3-datasheet?id=K98s9168qi49g) | XR809 @ 160 MHz | 2 MiB | 384 KiB | 22 (17 I/O) | ✔️ | ❌ | -
❌ | [WE1S](https://developer.tuya.com/en/docs/iot/wifie1smodule?id=K9605thnvg3e7) | ESP8266EX @ 80/160 MHz | 2 MiB | 36 KiB | 18 (11 I/O) | ✔️ | ❌ | -
❌ | [WE2L](https://developer.tuya.com/en/docs/iot/wifie2lmodule?id=K9605ud0gkjmh) | ESP8285 @ 80/160 MHz | 1 MiB | 50 KiB | 7 (5 I/O) | ✔️ | ❌ | -
❌ | [WE2S](https://developer.tuya.com/en/docs/iot/wifie2smodule?id=K9605u79tgxug) | ESP8285 @ 80/160 MHz | 1 MiB | 50 KiB | 11 (8 I/O) | ✔️ | ❌ | -
❌ | [WE3L](https://developer.tuya.com/en/docs/iot/wifie3lpinmodule?id=K9605uj1ar87n) | ESP8266 @ 80/160 MHz | 2 MiB | 50 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WE3S](https://developer.tuya.com/en/docs/iot/wifie3smodule?id=K9605ua1cx9tv) | ESP8266 @ 80/160 MHz | 2 MiB | 50 KiB | 16 (12 I/O) | ✔️ | ❌ | -
❌ | [WE3SE](https://developer.tuya.com/en/docs/iot/we3se?id=K97qv6ab7oh8d) | ESP32 @ 240 MHz | 4 MiB | 520 KiB | 22 (17 I/O) | ✔️ | ✔️ | -
❌ | [WE5P](https://developer.tuya.com/en/docs/iot/wifie5pmodule?id=K9605um3dtjbx) | ESP8266 @ 80/160 MHz | 1 MiB | 50 KiB | 15 (11 I/O) | ✔️ | ❌ | -
\* Only modules featuring at least Wi-Fi. WBR, JWBR, CR, (TY)JW and (TY)LC Series are not included here
** I/O count includes GPIOs, ADCs, PWM outputs and UART, but doesn't count CEN/RST and power pins.
## Project structure
```
arduino/
├─ <platform name>/ Arduino Core for specific SoC
│ ├─ cores/ Wiring core files
│ ├─ libraries/ Supported built-in platform libraries
├─ libretuya/
│ ├─ api/ Library interfaces
│ ├─ common/ Units common to all platforms
│ ├─ compat/ Fixes for compatibility with ESP32 framework
│ ├─ core/ LibreTuya API for Arduino cores
│ ├─ libraries/ Built-in platform-independent libraries
boards/
├─ <board name>/ Board-specific code
│ ├─ variant.cpp Arduino variant initialization
│ ├─ variant.h Arduino variant pin configs
├─ <board name>.json PlatformIO board description
builder/
├─ frameworks/ Framework builders for PlatformIO
│ ├─ <platform name>-sdk.py Vanilla SDK build system
│ ├─ <platform name>-arduino.py Arduino Core build system
├─ arduino-common.py Builder to provide ArduinoCore-API and LibreTuya APIs
├─ main.py Main PlatformIO builder
├─ utils.py SCons utils used during the build
docs/ Project documentation, guides, tips, etc.
platform/
├─ <platform name>/ Platform-specific configurations
│ ├─ bin/ Binary blobs (bootloaders, etc.)
│ ├─ fixups/ Code fix-ups to replace SDK parts
│ ├─ ld/ Linker scripts
│ ├─ openocd/ OpenOCD configuration files
tools/
├─ <tool name>/ Tools used during the build
platform.json PlatformIO manifest
platform.py Custom PlatformIO script
```
## Platforms
A list of platforms currently available in this project.
Platform name | Supported MCU(s) | Arduino Core | Source SDK (PIO framework)
---------------|------------------------------------------------------------------------|--------------|--------------------------------------------------------------------------
`realtek-ambz` | Realtek [AmebaZ](https://www.amebaiot.com/en/amebaz/) SoC (`RTL87xxB`) | ✔️ | `framework-realtek-amb1` ([amb1_sdk](https://github.com/ambiot/amb1_sdk))
### Realtek Ameba
The logic behind naming of Realtek chips and their series took me some time to figure out:
- RTL8xxxA - Ameba1/Ameba Series
- RTL8xxxB - AmebaZ Series
- RTL8xxxC - AmebaZ2/ZII Series
- RTL8xxxD - AmebaD Series
As such, there are numerous CPUs with the same numbers but different series, which makes them require different code and SDKs.
- [RTL8195AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8195am)
- RTL8710AF (found in amb1_arduino)
- [RTL8711AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8711am)
- [RTL8710BN](https://www.realtek.com/en/products/communications-network-ics/item/rtl8710bn)
- RTL8710BX (found in Tuya product pages)
- RTL8710B? (found in amb1_sdk)
- RTL8711B? (found in amb1_sdk)
- [RTL8710CM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8710cm)
- RTL8722CSM (found in ambd_arduino)
- RTL8720DN (found in ambd_arduino)
- [RTL8721DM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8721dm)
- RTL8722DM (found in ambd_arduino)
- and probably many more
Different Ameba series are not compatible with each other. Apparently, there isn't an official public SDK for AmebaZ that can support C++ properly.
4. See the [docs](https://docs.libretuya.ml/) for any questions/problems.
## Arduino Core support status
Note: this list will probably change with each functionality update.
&nbsp; | `realtek-ambz`
--------------------|---------------
Core functions | ✔️
GPIO/PWM/IRQ | ✔️//✔️
Analog input |
UART I/O | ✔️
Flash I/O | ❓
**CORE LIBRARIES** |
SoftwareSerial |
SPI | ❌
Wire |
**OTHER LIBRARIES** |
Wi-Fi STA/AP/Mixed | ✔️
Wi-Fi Client (SSL) | ✔️ (✔️)
Wi-Fi Server | ✔️
Wi-Fi Events | ✔️
IPv6 | ❌
HTTP Client (SSL) | ✔️ (✔️)
HTTP Server | ✔️
NVS / Preferences | ❌
SPIFFS | ❌
BLE | -
HTTP | ❌
NTP | ❌
OTA |
MDNS |
MQTT | ✅
SD | ❌
&nbsp; | `realtek-ambz` | `beken-72xx`
--------------------|----------------|-------------
Core functions | ✔️ | ✔️
GPIO/PWM/IRQ | ✔️/✔️/✔️ | ✔️/✔️/✔️
Analog input (ADC) | ✔️ | ✔️
Serial | ✔️ | ✔️
Serial (extra) | 0, 1, 2 | 1, 2
Flash I/O | ✔️ | ✔️
**CORE LIBRARIES** | |
SoftwareSerial | ✔️ | ❌
SPI | ❌ |
Wire | ❗ | ❌
**OTHER LIBRARIES** | |
Wi-Fi STA/AP/Mixed | ✔️ | ✔️
Wi-Fi Events | ✔️ | ✔️
TCP Client (SSL) | ✔️ (✔️) | ✔️ (❗)
TCP Server | ✔️ | ✔️
IPv6 | ❌ | ❌
HTTP Client (SSL) | ✔️ (✔️) |
HTTP Server | ✔️ | ✔️
NVS / Preferences | ❌ | ❌
SPIFFS | ❌ |
BLE | - | ❌
NTP | ❌ | ❌
OTA | ✔️ | ✔️
MDNS | ✔️ | ✔️
MQTT | ✅ | ❌
SD | ❌ | ❌
Symbols:

View File

@@ -1,28 +1,47 @@
* [Home](README.md)
* [Configuration](docs/config.md)
* Reference
* [LibreTuya API](docs/reference/lt-api.md)
* [Class reference](ltapi/class_libre_tuya.md)
* [Static functions](ltapi/_libre_tuya_a_p_i_8cpp.md)
* [😊 Getting started](docs/getting-started/README.md)
* [ESPHome](docs/projects/esphome.md)
* [📲 Flashing/dumping](docs/flashing/)
* [💻 Supported boards & chips](docs/status/supported.md)
* 📖 Reference
* Chip families
* [Beken BK72xx](docs/platform/beken-72xx/README.md)
* [Realtek Ameba - info](docs/platform/realtek-amb/README.md)
* [Realtek AmebaZ](docs/platform/realtek-ambz/README.md)
* [Debugging](docs/platform/realtek-ambz/debugging.md)
* [Exception decoder](docs/platform/realtek-ambz/exception-decoder.md)
* C library
* [Built-in functions](docs/platform/realtek-ambz/stdlib.md)
* [Memory management](docs/platform/realtek-ambz/memory-management.md)
* [🔧 LT configuration](docs/reference/config.md)
* [✔️ Implementation status](docs/status/arduino.md)
* [🔌 Boards documentation](boards/)
* [🔋 Examples](examples/)
* [📖 LibreTuya API](docs/reference/lt-api.md)
* [LT class reference](ltapi/class_libre_tuya.md)
* [Common methods](ltapi/_libre_tuya_a_p_i_8h.md)
* [Wiring custom methods](ltapi/_libre_tuya_custom_8h.md)
* [Logger](ltapi/lt__logger_8h.md)
* Common API
* [Flash](ltapi/class_i_flash_class.md)
* [Chip & family IDs](ltapi/_chip_type_8h_source.md)
* [POSIX utilities](ltapi/lt__posix__api_8h.md)
* 📖 Common API
* [FS](ltapi/classfs_1_1_f_s.md)
* [Preferences](ltapi/class_i_preferences.md)
* [WiFi API](ltapi/class_i_wi_fi_generic_class.md)
* [Station](ltapi/class_i_wi_fi_s_t_a_class.md)
* [Access Point](ltapi/class_i_wi_fi_a_p_class.md)
* [Scanning](ltapi/class_i_wi_fi_scan_class.md)
* [SoftwareSerial](ltapi/class_software_serial.md)
* [WiFi API](ltapi/class_wi_fi_class.md)
* [TCP Client](ltapi/class_i_wi_fi_client.md)
* [SSL Client](ltapi/class_i_wi_fi_client_secure.md)
* [TCP Server](ltapi/class_i_wi_fi_server.md)
* [LibreTuya libraries](docs/libs-built-in.md)
* [📖 LibreTuya libraries](docs/libs-built-in.md)
* [base64](ltapi/classbase64.md)
* [Flash](ltapi/class_flash_class.md)
* [HTTPClient](ltapi/class_h_t_t_p_client.md)
* [mDNS](ltapi/classm_d_n_s.md)
* NetUtils
* [ssl/MbedTLSClient](ltapi/class_mbed_t_l_s_client.md)
* [IPv6Address](ltapi/classarduino_1_1_i_pv6_address.md)
* [LwIPRxBuffer](ltapi/class_lw_i_p_rx_buffer.md)
* [Update](ltapi/class_update_class.md)
* [WebServer](ltapi/class_web_server.md)
* [WiFiMulti](ltapi/class_wi_fi_multi.md)
* [Third party libraries](docs/libs-3rd-party.md)
@@ -31,12 +50,9 @@
* [Functions](ltapi/functions.md)
* [Macros](ltapi/macros.md)
* [File list](ltapi/files.md)
* Platforms
* Realtek AmebaZ Series
* Boards
* [WR3](boards/wr3/README.md)
* C library
* [Built-in functions](docs/platform/realtek-ambz/stdlib.md)
* [Memory management](docs/platform/realtek-ambz/memory-management.md)
* [Debugging](docs/platform/realtek/debugging.md)
* [Exception decoder](docs/platform/realtek/exception-decoder.md)
* [📁 Project structure](docs/reference/project-structure.md)
* [✈️ OTA format](docs/ota/README.md)
* [uf2ota.py tool](docs/ota/uf2ota.md)
* [uf2ota.h library](docs/ota/library.md)
* [📓 TODO](TODO.md)
* [🔗 Resources](docs/resources/)

53
TODO.md Normal file
View File

@@ -0,0 +1,53 @@
# TODO list
## General
### Environment stability
Do not publish *any* SDK functions, macros, defines and includes. Define only what's needed in LT's public headers (like `Arduino.h`). Everything else is taken from `sdk_extern.h` or `WVariant.h` (TODO decide whether to keep WV public / make both private / get rid of WV and use sdk_extern only). Private headers are included by LT's .cpp units (maybe a dedicated private header that would include sdk_extern + Arduino.h).
Developers wanting to use SDK functions need to include them.
Explicit is better than implicit.
- consider moving to C++17 (GNU)? or any newer than C++11
### New families
- BL602
- RTL8710A
- RTL8720C
- RTL8720D
- W600 and/or W800
- LN8825
- BK7231Q
- host-native family
### Tools
- write OpenOCD flashers, using uf2ota library + FAL for partitions (in ltchiptool repository)
### Serial
- configuration of RX/TX pins
- SoftwareSerial library - receiving + Beken family
### Other
- watchdog API
- `Preferences` library
- test/fix IPv6 on different families
- what is `PowerManagement` at all? probably useless -> remove
## BK7231
- fix WiFi on BK7231N, test other functionality
- fix SSL (mbedTLS)
- I2C (Wire)
- SPI
- BLE
## RTL8710B
- take all stdio functions from stdio.h
- rewrite most of Wiring (it was copied from `ambd_arduino`, and is ugly)

View File

@@ -0,0 +1,32 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-14. */
#pragma once
#ifdef __cplusplus
#include "WCharacterFixup.h"
#endif
#define delay delayMilliseconds // change delay()'s signature - it's defined as static inline in WVariant.h
#include <api/ArduinoAPI.h>
#include <core/LibreTuyaAPI.h>
#undef delay
// Include family-specific code
#include "WVariant.h"
// Include board variant
#include "variant.h"
// Choose the main UART output port
#ifndef LT_UART_DEFAULT_PORT
#if defined(PIN_SERIAL2_TX)
#define LT_UART_DEFAULT_PORT 2
#else
#define LT_UART_DEFAULT_PORT 1
#endif
#endif
// Define available serial ports
#ifdef __cplusplus
#include "SerialClass.h"
#include <core/SerialExtern.h>
#endif

View File

@@ -0,0 +1,208 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <LibreTuyaAPI.h>
#include <libraries/Flash/Flash.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
#define FLASH_BUSY_SW (0x01UL << 31)
#define FLASH_WP_VALUE (0x01UL << 30)
#define FLASH_OP_SW (0x01UL << 29)
#define FLASH_OP_TYPE_POS 24
#define FLASH_OP_RDID 20
extern "C" {
#include <flash_pub.h>
#include <param_config.h>
#include <start_type_pub.h>
#include <sys_ctrl.h>
#include <sys_rtos.h>
#include <wdt_pub.h>
#include <wlan_ui_pub.h>
extern uint8_t system_mac[];
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
} // extern "C"
void LibreTuya::restart() {
bk_reboot();
}
void LibreTuya::restartDownloadMode() {
bk_reboot();
}
ResetReason LibreTuya::getResetReason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return RESET_REASON_POWER;
case RESET_SOURCE_REBOOT:
return RESET_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return RESET_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return RESET_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
return RESET_REASON_SLEEP;
}
return RESET_REASON_UNKNOWN;
}
/* CPU-related */
ChipType LibreTuya::getChipType() {
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
return CHIP_TYPE_ENUM(FAMILY, chipId);
}
const char *LibreTuya::getChipModel() {
return STRINGIFY_MACRO(MCU);
}
uint32_t LibreTuya::getChipId() {
uint8_t mac[6];
cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC)
return (mac[3]) | (mac[4] << 8) | (mac[5] << 16);
}
uint8_t LibreTuya::getChipCores() {
return 1;
}
const char *LibreTuya::getChipCoreType() {
return "ARM968E-S";
}
uint32_t LibreTuya::getCpuFreq() {
return configCPU_CLOCK_HZ;
}
uint32_t LibreTuya::getCycleCount() {
// TODO
return 0;
}
/* Flash memory utilities */
FlashId LibreTuya::getFlashChipId() {
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
REG_WRITE(REG_FLASH_OPERATE_SW, data);
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
FlashId id = {
.manufacturerId = REG_RD8(REG_FLASH_RDID, 2),
.chipId = REG_RD8(REG_FLASH_RDID, 1),
.chipSizeId = REG_RD8(REG_FLASH_RDID, 0),
};
return id;
}
/* Memory management */
uint32_t LibreTuya::getRamSize() {
return 256 * 1024;
}
uint32_t LibreTuya::getHeapSize() {
#if configDYNAMIC_HEAP_SIZE
extern unsigned char _empty_ram;
#if CFG_SOC_NAME == SOC_BK7231N
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
#else
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
#endif
#else
return configTOTAL_HEAP_SIZE;
#endif
}
uint32_t LibreTuya::getFreeHeap() {
return xPortGetFreeHeapSize();
}
uint32_t LibreTuya::getMinFreeHeap() {
return xPortGetMinimumEverFreeHeapSize();
}
uint32_t LibreTuya::getMaxAllocHeap() {
return 0;
}
/* OTA-related */
static int8_t otaImage2Valid = -1;
uint8_t LibreTuya::otaGetRunning() {
// Beken has bootloader-based OTA, running app is always index 1
return 1;
}
uint8_t LibreTuya::otaGetStoredIndex() {
return otaHasImage2() ? 2 : 1;
}
bool LibreTuya::otaSupportsDual() {
return true;
}
bool LibreTuya::otaHasImage1() {
return true;
}
bool LibreTuya::otaHasImage2() {
if (otaImage2Valid != -1)
return otaImage2Valid;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
Flash.readBlock(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
otaImage2Valid = magic == 0x004C4252; // "RBL\0", little-endian
return otaImage2Valid;
}
bool LibreTuya::otaSwitch(bool force) {
// no need to check otaGetStoredIndex() as it does the same as otaHasImage2()
// force checking validity again
otaImage2Valid = -1;
if (otaHasImage2() && force) {
// "rollback" - abort bootloader upgrade operation by wiping first sector
return Flash.eraseSector(FLASH_DOWNLOAD_OFFSET);
}
return otaHasImage2(); // false if second image is not valid
}
/* Watchdog */
bool LibreTuya::wdtEnable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
}
void LibreTuya::wdtDisable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void LibreTuya::wdtFeed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}
/* Global instance */
LibreTuya LT;
LibreTuya ESP = LT;

View File

@@ -0,0 +1,109 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-23. */
#include "SerialClass.h"
extern "C" {
#include <uart_pub.h>
extern void bk_send_byte(uint8_t uport, uint8_t data);
extern void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config);
extern int uart_rx_callback_set(int uport, uart_callback callback, void *param);
} // extern "C"
#ifdef PIN_SERIAL1_TX
SerialClass Serial1(UART1_PORT);
#endif
#ifdef PIN_SERIAL2_TX
SerialClass Serial2(UART2_PORT);
#endif
SerialClass::SerialClass(uint8_t port) {
this->port = port;
this->buf = NULL;
}
#if LT_AUTO_DOWNLOAD_REBOOT
static uint8_t adrState = 0;
static const uint8_t adrCmd[] = {0x01, 0xE0, 0xFC, 0x01, 0x00};
static void adrParse(uint8_t c) {
// parse and respond to link check command (CMD_LinkCheck=0)
adrState = (adrState + 1) * (c == adrCmd[adrState]);
if (adrState == 5) {
LT_I("Auto download mode: rebooting");
LT.restart();
}
}
#endif
static void callback(int port, void *param) {
RingBuffer *buf = (RingBuffer *)param;
int ch;
while ((ch = uart_read_byte(port)) != -1) {
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX)
// parse UART protocol commands on UART1
if (port == UART1_PORT)
adrParse(ch);
#endif
buf->store_char(ch);
}
}
void SerialClass::begin(unsigned long baudrate, uint16_t config) {
uint8_t dataWidth = ((config & SERIAL_DATA_MASK) >> 8) - 1; // 0x100..0x400 -> 0..3
uint8_t parity = 3 - (config & SERIAL_PARITY_MASK); // 0x3..0x1 -> 0..2
uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; // 0x10..0x30 -> 0..1
bk_uart_config_t cfg = {
.baud_rate = baudrate,
.data_width = (uart_data_width_t)dataWidth,
.parity = (uart_parity_t)parity,
.stop_bits = (uart_stop_bits_t)stopBits,
.flow_control = FLOW_CTRL_DISABLED,
};
if (this->buf) {
this->buf->clear();
} else {
this->buf = new RingBuffer();
}
uart_hw_set_change(port, &cfg);
uart_rx_callback_set(port, callback, this->buf);
}
void SerialClass::end() {
uart_rx_callback_set(port, NULL, NULL);
switch (port) {
case 1:
uart1_exit();
break;
case 2:
uart2_exit();
break;
}
delete this->buf;
}
int SerialClass::available() {
return buf->available();
}
int SerialClass::peek() {
return buf->peek();
}
int SerialClass::read() {
return buf->read_char();
}
void SerialClass::flush() {
uart_wait_tx_over();
}
size_t SerialClass::write(uint8_t c) {
bk_send_byte(port, c);
return 1;
}

View File

@@ -0,0 +1,38 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-23. */
#pragma once
#include <Arduino.h>
#include <api/HardwareSerial.h>
#include <api/RingBuffer.h>
using namespace arduino;
class SerialClass : public HardwareSerial {
private:
uint8_t port;
RingBuffer *buf;
public:
SerialClass(uint8_t port);
inline void begin(unsigned long baudrate) {
begin(baudrate, SERIAL_8N1);
}
void begin(unsigned long baudrate, uint16_t config);
void end();
int available();
int peek();
int read();
void flush();
size_t write(uint8_t c);
operator bool() {
return !!buf;
}
using Print::write;
};
#define HAS_SERIAL_CLASS 1

View File

@@ -0,0 +1,22 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "sdk_extern.h"
#include "sdk_mem.h"
// define an inline delay() which overrides BDK's delay()
static inline __attribute__((always_inline)) void delay(unsigned long ms) {
delayMilliseconds(ms);
}
// from fixups/arch_main.c
extern unsigned char __bk_rf_is_init;
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,38 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
extern "C" {
#include <rtos_pub.h>
#include <sys_rtos.h>
extern int uart_print_port;
} // extern "C"
beken_thread_t mainThread;
void initArduino() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX) && defined(PIN_SERIAL1_TX)
// initialize auto-download-reboot parser
Serial1.begin(115200);
#endif
}
bool startMainTask() {
OSStatus ret = rtos_create_thread(
&mainThread,
THD_APPLICATION_PRIORITY,
"main",
(beken_thread_function_t)mainTask,
8192,
NULL
);
if (ret != kNoErr)
return false;
vTaskStartScheduler();
return true;
}

View File

@@ -0,0 +1,30 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
#pragma once
// for printf() etc (they are wrapped anyway)
#include <stdio.h>
// most stuff is here
#include <include.h>
// for os_printf
#include <uart_pub.h>
// for GPIO names
#include <gpio_pub.h>
// conflict with stl_algobase.h
#undef min
#undef max
// include printf() wrapper disable methods
#include <printf_port.h>
// make non-SDK code call the proper printf()
#undef bk_printf
#undef os_printf
#undef warning_prf
#undef fatal_prf
#define bk_printf printf
#define os_printf printf
#define warning_prf printf
#define fatal_prf printf

View File

@@ -0,0 +1,20 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
#pragma once
#include <stddef.h>
// Beken SDK is actually pretty good, in terms of declaring
// stdlib functions properly! So no need for any #define hell.
#include <mem_pub.h>
// All the MemMang functions are in stdlib, just wrapped
// during linking.
#include <stdlib.h>
// for memcpy etc.
#include <string.h>
// ...except zalloc, which is apparently not in the stdlib
#define zalloc os_zalloc
#define LT_HEAP_FUNC xPortGetFreeHeapSize

View File

@@ -0,0 +1,109 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
#include <arm_arch.h>
#include <bk_timer.h>
#include <bk_timer_pub.h>
#include <rtos_pub.h>
#include <sys_rtos.h>
#define TICKS_PER_US (CFG_XTAL_FREQUENCE / 1000 / 1000)
#define US_PER_OVERFLOW (portTICK_PERIOD_MS * 1000)
#define TICKS_PER_OVERFLOW (TICKS_PER_US * US_PER_OVERFLOW)
void delayMilliseconds(unsigned long ms) {
rtos_delay_milliseconds(ms);
}
static uint32_t getTicksCount() {
// copied from bk_timer_ctrl(), for speeds
uint32_t timeout = 0;
REG_WRITE(TIMER0_2_READ_CTL, (BKTIMER0 << 2) | 1);
while (REG_READ(TIMER0_2_READ_CTL) & 1) {
timeout++;
if (timeout > (120 * 1000))
return 0;
}
return REG_READ(TIMER0_2_READ_VALUE);
}
void delayMicroseconds(unsigned int us) {
#if LT_MICROS_HIGH_RES
if (us == 0)
return;
us--;
uint32_t startTick = getTicksCount();
/* startTick2 accounts for the case where the timer counter overflows */
uint32_t startTick2 = startTick - TICKS_PER_OVERFLOW;
uint32_t delayTicks = TICKS_PER_US * us;
while (delayTicks > TICKS_PER_OVERFLOW) {
// The delay is longer than what the timer can count.
// Let it overflow until only a fraction of TICKS_PER_OVERFLOW remain.
while (getTicksCount() > startTick) {}
while (getTicksCount() < startTick) {}
delayTicks -= TICKS_PER_OVERFLOW;
}
while ((getTicksCount() - startTick < delayTicks) || // normal case
(getTicksCount() - startTick2 < delayTicks) // counter overflow case
) {}
#else
volatile uint32_t i, j;
for (i = 0; i < us; i++) {
for (j = 0; j < 6; j++) {}
}
#endif
}
unsigned long millis() {
return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
unsigned long micros() {
#if (CFG_SOC_NAME == SOC_BK7231)
#error "Not implemented"
#endif
#if LT_MICROS_HIGH_RES
static uint32_t lastMillis = 0;
static uint32_t correctedMillis = 0;
static uint32_t lastTicks = 0;
uint32_t nowMillis = millis();
uint32_t nowTicks = getTicksCount();
bool tickOverflow = nowTicks < lastTicks;
bool millisUpdated = nowMillis != lastMillis;
if (millisUpdated) {
/* reset artificially corrected millis */
correctedMillis = nowMillis;
} else if (tickOverflow) {
/*
This can happen if micros is called from within a interruptLock block (interrupts disabled).
In this case, if the tick counter rolls over, millis() won't be updated, and micros will
lag by US_PER_OVERFLOW milliseconds (one rollover).
The workaround only works as long as micros() calls happen within 2ms of eachother.
WARNING: if interrupts are disabled for more than 2ms, micros() and millis() will temporarily get out of sync.
*/
correctedMillis += portTICK_PERIOD_MS;
}
lastMillis = nowMillis;
lastTicks = nowTicks;
return correctedMillis * 1000 + nowTicks / (CFG_XTAL_FREQUENCE / 1000 / 1000);
#else
#if 0
uint32_t timeout = 0;
REG_WRITE(TIMER3_5_READ_CTL, (BKTIMER3 << 2) | 1);
while (REG_READ(TIMER3_5_READ_CTL) & 1) {
timeout++;
if (timeout > (120 * 1000))
return 0;
}
return millis() * 1000 + REG_READ(TIMER3_5_READ_VALUE) / 32;
#endif
return millis() * 1000;
#endif
}
void yield() {
runPeriodicTasks();
vTaskDelay(1);
taskYIELD();
}

View File

@@ -0,0 +1,142 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include <Arduino.h>
#include <pwm_pub.h>
#include <saradc_pub.h>
static GPIO_INDEX pwmToGpio[] = {
GPIO6, // PWM0
GPIO7, // PWM1
GPIO8, // PWM2
GPIO9, // PWM3
GPIO24, // PWM4
GPIO26, // PWM5
};
#if CFG_SOC_NAME == SOC_BK7231N
static GPIO_INDEX adcToGpio[] = {
-1, // ADC0 - VBAT
GPIONUM, // ADC1
GPIONUM, // ADC2
GPIO23, // ADC3
GPIONUM, // ADC4
GPIONUM, // ADC5
GPIONUM, // ADC6
GPIONUM, // ADC7
};
#else
static GPIO_INDEX adcToGpio[] = {
-1, // ADC0 - VBAT
GPIO4, // ADC1
GPIO5, // ADC2
GPIO23, // ADC3
GPIO2, // ADC4
GPIO3, // ADC5
GPIO12, // ADC6
GPIO13, // ADC7
};
#endif
static uint8_t gpioToPwm(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(pwmToGpio) / sizeof(GPIO_INDEX); i++) {
if (pwmToGpio[i] == gpio)
return i;
}
return 0;
}
static uint8_t gpioToAdc(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(adcToGpio) / sizeof(GPIO_INDEX); i++) {
if (adcToGpio[i] == gpio)
return i;
}
return 0;
}
static pwm_param_t pwm;
static uint16_t adcData[1];
uint16_t analogReadVoltage(pin_size_t pinNumber) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return 0;
if (!pinSupported(pin, PIN_ADC))
return 0;
UINT32 status;
saradc_desc_t adc;
DD_HANDLE handle;
saradc_config_param_init(&adc);
adc.channel = gpioToAdc(pin->gpio);
adc.mode = ADC_CONFIG_MODE_CONTINUE;
adc.pData = adcData;
adc.data_buff_size = 1;
handle = ddev_open(SARADC_DEV_NAME, &status, (uint32_t)&adc);
if (status)
return 0;
// wait for data
while (!adc.has_data || adc.current_sample_data_cnt < 1) {
delay(1);
}
ddev_control(handle, SARADC_CMD_RUN_OR_STOP_ADC, (void *)false);
ddev_close(handle);
return adcData[0];
}
uint16_t analogReadMaxVoltage(pin_size_t pinNumber) {
return 3300;
}
void analogWrite(pin_size_t pinNumber, int value) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_PWM))
return;
float percent = value * 1.0 / ((1 << _analogWriteResolution) - 1);
uint32_t frequency = 26 * _analogWritePeriod - 1;
uint32_t dutyCycle = percent * frequency;
pwm.channel = gpioToPwm(pin->gpio);
#if CFG_SOC_NAME != SOC_BK7231N
pwm.duty_cycle = dutyCycle;
#else
pwm.duty_cycle1 = dutyCycle;
pwm.duty_cycle2 = 0;
pwm.duty_cycle3 = 0;
#endif
if (dutyCycle) {
if (!pinEnabled(pin, PIN_PWM)) {
// enable PWM and set its value
pwm.cfg.bits.en = PWM_ENABLE;
pwm.cfg.bits.int_en = PWM_INT_DIS;
pwm.cfg.bits.mode = PWM_PWM_MODE;
pwm.cfg.bits.clk = PWM_CLK_26M;
pwm.end_value = frequency;
pwm.p_Int_Handler = NULL;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_PARAM, &pwm);
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_LEVL_SET_HIGH, &pwm.channel);
sddev_control(PWM_DEV_NAME, CMD_PWM_UNIT_ENABLE, &pwm.channel);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_GPIO;
pin->enabled |= PIN_PWM;
} else {
// update duty cycle
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
}
} else {
if (pinEnabled(pin, PIN_PWM)) {
// disable PWM
pwm.cfg.bits.en = PWM_DISABLE;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, &pwm);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_PWM;
}
// force level as LOW
pinMode(pinNumber, OUTPUT);
digitalWrite(pinNumber, LOW);
}
}

View File

@@ -0,0 +1,62 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include <Arduino.h>
void pinMode(pin_size_t pinNumber, PinMode pinMode) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_GPIO))
return;
if (pinEnabled(pin, PIN_PWM))
// disable PWM before using the pin
analogWrite(pinNumber, 0);
if (pinEnabled(pin, PIN_GPIO) && pin->mode == pinMode)
return;
switch (pinMode) {
case INPUT:
gpio_config(pin->gpio, GMODE_INPUT);
break;
case OUTPUT:
gpio_config(pin->gpio, GMODE_OUTPUT);
break;
case INPUT_PULLUP:
gpio_config(pin->gpio, GMODE_INPUT_PULLUP);
break;
case INPUT_PULLDOWN:
gpio_config(pin->gpio, GMODE_INPUT_PULLDOWN);
break;
case OUTPUT_OPENDRAIN:
gpio_config(pin->gpio, GMODE_SET_HIGH_IMPENDANCE);
break;
}
pin->enabled |= PIN_GPIO;
pin->mode = pinMode;
}
void digitalWrite(pin_size_t pinNumber, PinStatus status) {
// verify level is 0 or 1
if (status > HIGH)
return;
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
// pin is not GPIO yet or not OUTPUT; enable or disable input pullup
if (!pinEnabled(pin, PIN_GPIO) || !pinIsOutput(pin)) {
pinMode(pinNumber, status * INPUT_PULLUP);
return;
}
// write the new state
gpio_output(pin->gpio, status);
}
PinStatus digitalRead(pin_size_t pinNumber) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return 0;
// pin is not GPIO yet or not INPUT; change the mode
if (!pinEnabled(pin, PIN_GPIO) || !pinIsInput(pin))
pinMode(pinNumber, INPUT);
// read the value
return gpio_input(pin->gpio);
}

View File

@@ -0,0 +1,99 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include <Arduino.h>
static void *irqHandlerList[PINS_COUNT] = {NULL};
static void *irqHandlerArgs[PINS_COUNT] = {NULL};
static bool irqChangeList[PINS_COUNT];
static void irqHandler(unsigned char gpio) {
int pin = -1;
for (pin_size_t i = 0; i < PINS_COUNT; i++) {
if (pinTable[i].gpio == gpio) {
pin = i;
break;
}
}
if (pin == -1)
return;
if (!irqHandlerList[pin])
return;
if (irqChangeList[pin]) {
if (pinTable[pin].mode == INPUT_PULLDOWN) {
pinTable[pin].mode = INPUT_PULLUP;
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_FALLING, irqHandler);
} else if (pinTable[pin].mode == INPUT_PULLUP) {
pinTable[pin].mode = INPUT_PULLDOWN;
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_RISING, irqHandler);
}
}
if (irqHandlerArgs[pin] == NULL) {
((voidFuncPtr)irqHandlerList[pin])();
} else {
((voidFuncPtrParam)irqHandlerList[pin])(irqHandlerArgs[pin]);
}
}
void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) {
attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL);
}
void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) {
PinInfo *pin = pinInfo(interruptNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_IRQ))
return;
uint32_t event = 0;
PinMode modeNew = 0;
bool change = 0;
switch (mode) {
case LOW:
event = GPIO_INT_LEVEL_LOW;
modeNew = INPUT_PULLUP;
change = false;
break;
case HIGH:
event = GPIO_INT_LEVEL_HIGH;
modeNew = INPUT_PULLDOWN;
change = false;
break;
case FALLING:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = false;
break;
case RISING:
event = GPIO_INT_LEVEL_RISING;
modeNew = INPUT_PULLDOWN;
change = false;
break;
case CHANGE:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = true;
break;
default:
return;
}
irqHandlerList[interruptNumber] = callback;
irqHandlerArgs[interruptNumber] = param;
irqChangeList[interruptNumber] = change;
gpio_int_enable(pin->gpio, event, irqHandler);
pin->enabled |= PIN_IRQ | PIN_GPIO;
pin->mode = modeNew;
}
void detachInterrupt(pin_size_t interruptNumber) {
PinInfo *pin = pinInfo(interruptNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_IRQ))
return;
irqHandlerList[interruptNumber] = NULL;
irqHandlerArgs[interruptNumber] = NULL;
irqChangeList[interruptNumber] = false;
gpio_int_disable(pin->gpio);
pin->enabled &= ~PIN_IRQ;
}

View File

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
#pragma once
#define LT_MD5_USE_HOSTAPD 1

View File

@@ -0,0 +1,81 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#include "WiFiPriv.h"
WiFiClass::WiFiClass() {
memset(&data, 0x00, sizeof(WiFiData));
data.scanSem = xSemaphoreCreateBinary();
}
WiFiClass::~WiFiClass() {
vSemaphoreDelete(data.scanSem);
}
void WiFiClass::dataInitialize() {
if (data.statusIp)
return;
LT_DM(WIFI, "Data init");
data.configSta = zalloc(sizeof(network_InitTypeDef_st));
data.configAp = zalloc(sizeof(network_InitTypeDef_ap_st));
data.statusIp = malloc(sizeof(IPStatusTypedef));
data.statusLink = malloc(sizeof(LinkStatusTypeDef));
STA_CFG->dhcp_mode = DHCP_CLIENT;
LT_DM(WIFI, "Data = %p", data.configSta);
}
void WiFiClass::dataFree() {
LT_DM(WIFI, "Data free");
free(data.configSta);
free(data.configAp);
free(data.statusIp);
free(data.statusLink);
data.configSta = NULL;
data.configAp = NULL;
data.statusIp = NULL;
data.statusLink = NULL;
}
WiFiStatus eventTypeToStatus(uint8_t type) {
// rw_msg_pub.h:9
switch (type) {
case RW_EVT_STA_IDLE:
return WL_IDLE_STATUS;
case RW_EVT_STA_NO_AP_FOUND:
return WL_NO_SSID_AVAIL;
case RW_EVT_STA_CONNECTING:
case RW_EVT_STA_CONNECTED:
return WL_SCAN_COMPLETED;
case RW_EVT_STA_GOT_IP:
return WL_CONNECTED;
case RW_EVT_STA_PASSWORD_WRONG:
case RW_EVT_STA_ASSOC_FULL:
case RW_EVT_STA_CONNECT_FAILED:
return WL_CONNECT_FAILED;
case RW_EVT_STA_BEACON_LOSE:
return WL_CONNECTION_LOST;
case RW_EVT_STA_DISCONNECTED:
return WL_DISCONNECTED;
}
}
WiFiAuthMode securityTypeToAuthMode(uint8_t type) {
// wlan_ui_pub.h:62
switch (type) {
case BK_SECURITY_TYPE_NONE:
return WIFI_AUTH_OPEN;
case BK_SECURITY_TYPE_WEP:
return WIFI_AUTH_WEP;
case BK_SECURITY_TYPE_WPA_TKIP:
case BK_SECURITY_TYPE_WPA_AES:
return WIFI_AUTH_WPA_PSK;
case BK_SECURITY_TYPE_WPA2_TKIP:
case BK_SECURITY_TYPE_WPA2_AES:
case BK_SECURITY_TYPE_WPA2_MIXED:
return WIFI_AUTH_WPA2_PSK;
case BK_SECURITY_TYPE_WPA3_SAE:
return WIFI_AUTH_WPA3_PSK;
case BK_SECURITY_TYPE_WPA3_WPA2_MIXED:
return WIFI_AUTH_WPA2_WPA3_PSK;
}
return WIFI_AUTH_INVALID;
}

View File

@@ -0,0 +1,10 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#pragma once
#include <Arduino.h>
#include <api/WiFi/WiFi.h>
#include "WiFiClient.h"
#include "WiFiClientSecure.h"
#include "WiFiServer.h"

View File

@@ -0,0 +1,127 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-01. */
#include "WiFiPriv.h"
bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bool ssidHidden, int maxClients) {
if (!enableAP(true))
return WL_CONNECT_FAILED;
if (!validate(ssid, passphrase))
return WL_CONNECT_FAILED;
LT_HEAP_I();
// Beken SDK bug: bk_wlan_ap_init_adv() doesn't null-terminate the passphrase
memset(g_ap_param_ptr->key, '\0', 65);
strcpy(AP_CFG->wifi_ssid, ssid);
if (passphrase) {
strcpy(AP_CFG->wifi_key, passphrase);
AP_CFG->security = BK_SECURITY_TYPE_WPA2_MIXED;
} else {
AP_CFG->wifi_key[0] = '\0';
AP_CFG->security = BK_SECURITY_TYPE_NONE;
}
AP_CFG->channel = channel;
AP_CFG->ssid_hidden = ssidHidden;
AP_CFG->max_con = maxClients;
AP_CFG->dhcp_mode = DHCP_SERVER;
AP_CFG->wifi_retry_interval = 100;
LT_IM(WIFI, "Creating SoftAP %s", ssid);
if (!AP_CFG->local_ip_addr[0]) {
LT_DM(WIFI, "Setting default IP config");
softAPConfig((uint32_t)0, (uint32_t)0, (uint32_t)0);
}
LT_DM(WIFI, "Static IP: %s / %s / %s", AP_CFG->local_ip_addr, AP_CFG->net_mask, AP_CFG->gateway_ip_addr);
__wrap_bk_printf_disable();
OSStatus ret = bk_wlan_start_ap_adv(AP_CFG);
__wrap_bk_printf_enable();
if (ret != 0) {
LT_EM(WIFI, "SoftAP failed; ret=%d", ret);
return false;
}
LT_DM(WIFI, "AP start OK");
return true;
}
bool WiFiClass::softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet) {
dataInitialize();
if (!localIP) {
localIP = gateway = IPAddress(192, 168, 43, 1);
subnet = IPAddress(255, 255, 255, 0);
}
sprintf(AP_CFG->local_ip_addr, IP_FMT, localIP[0], localIP[1], localIP[2], localIP[3]);
sprintf(AP_CFG->net_mask, IP_FMT, subnet[0], subnet[1], subnet[2], subnet[3]);
sprintf(AP_CFG->gateway_ip_addr, IP_FMT, gateway[0], gateway[1], gateway[2], gateway[3]);
// from wlan_ui.c:1370
if (uap_ip_is_start()) {
uap_ip_down();
ip_address_set(
BK_STATION,
AP_CFG->dhcp_mode,
AP_CFG->local_ip_addr,
AP_CFG->net_mask,
AP_CFG->gateway_ip_addr,
AP_CFG->dns_server_ip_addr
);
uap_ip_start();
}
return true;
}
bool WiFiClass::softAPdisconnect(bool wifiOff) {
if (!(getMode() & WIFI_MODE_AP))
// do not call SDK methods before even initializing WiFi first
return true;
return bk_wlan_stop(BK_SOFT_AP) == kNoErr;
}
uint8_t WiFiClass::softAPgetStationNum() {
return 0;
}
IPAddress WiFiClass::softAPIP() {
AP_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->ip);
return ip;
}
IPAddress WiFiClass::softAPSubnetMask() {
AP_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->mask);
return ip;
}
const char *WiFiClass::softAPgetHostname() {
struct netif *ifs = (struct netif *)net_get_uap_handle();
return netif_get_hostname(ifs);
}
bool WiFiClass::softAPsetHostname(const char *hostname) {
struct netif *ifs = (struct netif *)net_get_uap_handle();
netif_set_hostname(ifs, (char *)hostname);
return true;
}
uint8_t *WiFiClass::softAPmacAddress(uint8_t *mac) {
setMacAddress(mac);
return mac;
}
String WiFiClass::softAPmacAddress(void) {
uint8_t mac[ETH_ALEN];
wifi_get_mac_address((char *)mac, CONFIG_ROLE_AP);
return macToString(mac);
}
const String WiFiClass::softAPSSID(void) {
AP_GET_LINK_STATUS_RETURN("");
return AP_CFG->wifi_ssid;
}

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <lwip/LwIPClient.h>
typedef LwIPClient WiFiClient;

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-04. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <ssl/MbedTLSClient.h>
typedef MbedTLSClient WiFiClientSecure;

View File

@@ -0,0 +1,25 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#pragma once
#include <Arduino.h>
extern "C" {
#include <FreeRTOS.h>
#include <rw_msg_pub.h>
#include <semphr.h>
} // extern "C"
typedef struct {
void *configSta;
void *configAp;
unsigned long scannedAt;
SemaphoreHandle_t scanSem;
void *statusIp;
void *statusLink;
rw_evt_type lastStaEvent;
rw_evt_type lastApEvent;
bool apEnabled;
} WiFiData;

View File

@@ -0,0 +1,149 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-10. */
#include "WiFiPriv.h"
#include <vector>
static xQueueHandle wifiEventQueueHandle = NULL;
static xTaskHandle wifiEventTaskHandle = NULL;
static const rw_evt_type eventConnected = RW_EVT_STA_CONNECTED;
// callback for bk_wlan_status_register_cb()
static void wifiStatusCallback(rw_evt_type *pEvent) {
if (wifiEventQueueHandle && wifiEventTaskHandle) {
xQueueSend(wifiEventQueueHandle, pEvent, portMAX_DELAY);
} else {
wifiEventHandler(*pEvent);
}
}
static void wifiEventTask(void *arg) {
rw_evt_type event = RW_EVT_MAX;
for (;;) {
if (xQueueReceive(wifiEventQueueHandle, &event, portMAX_DELAY) == pdTRUE) {
wifiEventHandler(event);
}
}
}
void wifiEventSendArduino(EventId event) {
event = (EventId)(RW_EVT_ARDUINO | event);
wifiStatusCallback((rw_evt_type *)&event);
}
void startWifiTask() {
if (!wifiEventQueueHandle) {
LT_HEAP_I();
wifiEventQueueHandle = xQueueCreate(32, sizeof(rw_evt_type));
LT_HEAP_I();
}
if (!wifiEventTaskHandle) {
LT_HEAP_I();
xTaskCreate(wifiEventTask, "wifievent", 512, NULL, 4, &wifiEventTaskHandle);
LT_HEAP_I();
}
bk_wlan_status_register_cb((FUNC_1PARAM_PTR)wifiStatusCallback);
}
void wifiEventHandler(rw_evt_type event) {
if (!pWiFi)
return; // failsafe
LT_DM(WIFI, "BK event %u", event);
if (event <= RW_EVT_STA_GOT_IP)
pWiFi->data.lastStaEvent = event;
else
pWiFi->data.lastApEvent = event;
EventId eventId;
EventInfo eventInfo;
String ssid;
memset(&eventInfo, 0, sizeof(EventInfo));
switch (event) {
case RW_EVT_STA_IDLE:
eventId = ARDUINO_EVENT_WIFI_READY;
break;
case RW_EVT_STA_BEACON_LOSE:
case RW_EVT_STA_PASSWORD_WRONG:
case RW_EVT_STA_NO_AP_FOUND:
case RW_EVT_STA_ASSOC_FULL:
case RW_EVT_STA_DISCONNECTED:
case RW_EVT_STA_CONNECT_FAILED:
eventId = ARDUINO_EVENT_WIFI_STA_DISCONNECTED;
eventInfo.wifi_sta_disconnected.ssid_len = 0;
switch (event) {
case RW_EVT_STA_BEACON_LOSE:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_BEACON_TIMEOUT;
break;
case RW_EVT_STA_PASSWORD_WRONG:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_AUTH_FAIL;
break;
case RW_EVT_STA_NO_AP_FOUND:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_NO_AP_FOUND;
break;
case RW_EVT_STA_ASSOC_FULL:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_ASSOC_TOOMANY;
break;
case RW_EVT_STA_DISCONNECTED:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_ASSOC_LEAVE;
break;
case RW_EVT_STA_CONNECT_FAILED:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_CONNECTION_FAIL;
break;
default:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_UNSPECIFIED;
break;
}
break;
case RW_EVT_STA_CONNECTED:
eventId = ARDUINO_EVENT_WIFI_STA_CONNECTED;
ssid = pWiFi->SSID();
eventInfo.wifi_sta_connected.ssid_len = ssid.length();
eventInfo.wifi_sta_connected.channel = pWiFi->channel();
eventInfo.wifi_sta_connected.authmode = pWiFi->getEncryption();
memcpy(eventInfo.wifi_sta_connected.ssid, ssid.c_str(), eventInfo.wifi_sta_connected.ssid_len + 1);
memcpy(eventInfo.wifi_sta_connected.bssid, pWiFi->BSSID(), 6);
break;
case RW_EVT_STA_GOT_IP:
eventId = ARDUINO_EVENT_WIFI_STA_GOT_IP;
eventInfo.got_ip.if_index = 0;
eventInfo.got_ip.ip_changed = true;
eventInfo.got_ip.ip_info.ip.addr = WiFi.localIP();
eventInfo.got_ip.ip_info.netmask.addr = WiFi.subnetMask();
eventInfo.got_ip.ip_info.gw.addr = WiFi.gatewayIP();
break;
case RW_EVT_AP_CONNECTED:
eventId = ARDUINO_EVENT_WIFI_AP_STACONNECTED;
// TODO station MAC
break;
case RW_EVT_AP_DISCONNECTED:
eventId = ARDUINO_EVENT_WIFI_AP_STADISCONNECTED;
// TODO station MAC
break;
case RW_EVT_ARDUINO | ARDUINO_EVENT_WIFI_SCAN_DONE:
eventId = ARDUINO_EVENT_WIFI_SCAN_DONE;
eventInfo.wifi_scan_done.status = 0;
if (pWiFi->scan)
eventInfo.wifi_scan_done.number = pWiFi->scan->count;
eventInfo.wifi_scan_done.scan_id = 0;
break;
default:
if (event < RW_EVT_ARDUINO)
return;
eventId = (EventId)(event - RW_EVT_ARDUINO);
break;
}
pWiFi->postEvent(eventId, eventInfo);
}

View File

@@ -0,0 +1,89 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#include "WiFiPriv.h"
bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
__wrap_bk_printf_disable();
startWifiTask();
if (!__bk_rf_is_init) {
LT_DM(WIFI, "Initializing func&app");
func_init_extended();
app_pre_start();
// wait for the init_thread to finish its job
while (xTaskGetHandle("init_thread")) {
LT_VM(WIFI, "Waiting for init_thread");
delay(10);
}
LT_DM(WIFI, "Init OK");
__bk_rf_is_init = true;
}
LT_HEAP_I();
if (mode) {
LT_DM(WIFI, "Wakeup RF");
uint32_t reg = 1; // this is only checked for being true-ish
sddev_control(SCTRL_DEV_NAME, CMD_RF_HOLD_BIT_SET, &reg);
}
if (sta == WLMODE_ENABLE) {
LT_DM(WIFI, "Enabling STA");
bk_wlan_sta_init(NULL);
#if CFG_WPA_CTRL_IFACE
wlan_sta_enable();
#endif
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_START);
} else if (sta == WLMODE_DISABLE) {
LT_DM(WIFI, "Disabling STA");
bk_wlan_stop(BK_STATION);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_STOP);
}
LT_HEAP_I();
if (ap == WLMODE_ENABLE) {
LT_DM(WIFI, "Enabling AP");
// fake it - on BK7231, enabling the AP without starting it breaks all connection attempts
data.apEnabled = true;
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_START);
} else if (ap == WLMODE_DISABLE) {
LT_DM(WIFI, "Disabling AP");
bk_wlan_stop(BK_SOFT_AP);
data.apEnabled = false;
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_STOP);
}
// force checking actual mode again
mode = getMode();
if (!mode)
dataFree();
LT_HEAP_I();
__wrap_bk_printf_enable();
return true;
}
WiFiMode WiFiClass::getMode() {
uint8_t sta = !!bk_wlan_has_role(VIF_STA) * WIFI_MODE_STA;
uint8_t ap = data.apEnabled * WIFI_MODE_AP; // report the faked value
return (WiFiMode)(sta | ap);
}
WiFiStatus WiFiClass::status() {
rw_evt_type status = data.lastStaEvent;
if (status == RW_EVT_STA_CONNECTED && STA_CFG->dhcp_mode == DHCP_DISABLE)
status = RW_EVT_STA_GOT_IP;
return eventTypeToStatus(status);
}
IPAddress WiFiClass::hostByName(const char *hostname) {
ip_addr_t ip;
int ret = netconn_gethostbyname(hostname, &ip);
if (ret == ERR_OK) {
return ip.addr;
}
return IPAddress();
}

View File

@@ -0,0 +1,90 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#pragma once
#include <api/WiFi/WiFi.h>
extern "C" {
#include <lwip/api.h>
#include <lwip/ip_addr.h>
#include <lwip/netif.h>
// port/net.h
#include <net.h>
#include <FreeRTOS.h>
#include <semphr.h>
#include <common.h>
#include <config.h>
#include <main_none.h>
#include <param_config.h>
#include <rw_msg_rx.h>
#include <sa_ap.h>
#include <sys_ctrl_pub.h>
#include <vif_mgmt.h>
#include <wlan_ui_pub.h>
#include <wpa_supplicant_i.h>
extern void func_init_extended();
extern void app_pre_start();
extern void bk_wlan_ap_init(network_InitTypeDef_st *inNetworkInitPara);
// func/hostapd-2.5/wpa_supplicant/main_supplicant.c
extern struct wpa_ssid_value *wpas_connect_ssid;
// app/param_config.c
extern general_param_t *g_wlan_general_param;
extern ap_param_t *g_ap_param_ptr;
extern sta_param_t *g_sta_param_ptr;
extern uint8_t system_mac[6];
// WiFi.cpp
WiFiStatus eventTypeToStatus(uint8_t type);
WiFiAuthMode securityTypeToAuthMode(uint8_t type);
// WiFiEvents.cpp
extern void wifiEventSendArduino(EventId event);
extern void startWifiTask();
extern void wifiEventHandler(rw_evt_type event);
#define RW_EVT_ARDUINO (1 << 7)
#define IP_FMT "%u.%u.%u.%u"
#define STA_CFG ((network_InitTypeDef_st *)data.configSta)
#define AP_CFG ((network_InitTypeDef_ap_st *)data.configAp)
#define IP_STATUS ((IPStatusTypedef *)data.statusIp)
#define LINK_STATUS ((LinkStatusTypeDef *)data.statusLink)
#define STA_GET_LINK_STATUS_RETURN(ret) \
{ \
if (!sta_ip_is_start()) \
return ret; \
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef)); \
bk_wlan_get_link_status(LINK_STATUS); \
}
#define STA_GET_IP_STATUS_RETURN(ret) \
{ \
if (!sta_ip_is_start()) \
return ret; \
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
bk_wlan_get_ip_status(IP_STATUS, BK_STATION); \
}
#define AP_GET_LINK_STATUS_RETURN(ret) \
{ \
if (!uap_ip_is_start()) \
return ret; \
}
#define AP_GET_IP_STATUS_RETURN(ret) \
{ \
if (!uap_ip_is_start()) \
return ret; \
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
bk_wlan_get_ip_status(IP_STATUS, BK_SOFT_AP); \
}
} // extern "C"

View File

@@ -0,0 +1,224 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
#include "WiFiPriv.h"
WiFiStatus
WiFiClass::begin(const char *ssid, const char *passphrase, int32_t channel, const uint8_t *bssid, bool connect) {
if (!enableSTA(true))
return WL_CONNECT_FAILED;
if (!validate(ssid, passphrase))
return WL_CONNECT_FAILED;
LT_HEAP_I();
disconnect(false);
strcpy(STA_CFG->wifi_ssid, ssid);
if (passphrase) {
strcpy(STA_CFG->wifi_key, passphrase);
} else {
STA_CFG->wifi_bssid[0] = '\0';
}
if (reconnect(bssid))
return WL_CONNECTED;
return WL_CONNECT_FAILED;
}
bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
dataInitialize();
STA_CFG->dhcp_mode = localIP ? DHCP_DISABLE : DHCP_CLIENT;
if (localIP) {
sprintf(STA_CFG->local_ip_addr, IP_FMT, localIP[0], localIP[1], localIP[2], localIP[3]);
sprintf(STA_CFG->net_mask, IP_FMT, subnet[0], subnet[1], subnet[2], subnet[3]);
sprintf(STA_CFG->gateway_ip_addr, IP_FMT, gateway[0], gateway[1], gateway[2], gateway[3]);
if (dns1) {
sprintf(STA_CFG->dns_server_ip_addr, IP_FMT, dns1[0], dns1[1], dns1[2], dns1[3]);
} else {
STA_CFG->dns_server_ip_addr[0] = '\0';
}
} else {
STA_CFG->local_ip_addr[0] = '\0';
STA_CFG->net_mask[0] = '\0';
STA_CFG->gateway_ip_addr[0] = '\0';
STA_CFG->dns_server_ip_addr[0] = '\0';
}
// from wlan_ui.c:1370
if (sta_ip_is_start()) {
sta_ip_down();
ip_address_set(
BK_STATION,
STA_CFG->dhcp_mode,
STA_CFG->local_ip_addr,
STA_CFG->net_mask,
STA_CFG->gateway_ip_addr,
STA_CFG->dns_server_ip_addr
);
sta_ip_start();
}
return true;
}
bool WiFiClass::reconnect(const uint8_t *bssid) {
dataInitialize();
if (!bssid && !STA_CFG->wifi_ssid[0]) {
LT_EM(WIFI, "(B)SSID not specified");
goto error;
}
if (bssid) {
LT_IM(WIFI, "Connecting to " MACSTR, MAC2STR(bssid));
} else {
LT_IM(WIFI, "Connecting to %s", STA_CFG->wifi_ssid);
}
LT_DM(WIFI, "Data = %p", data.configSta);
STA_CFG->wifi_mode = BK_STATION;
STA_CFG->wifi_retry_interval = 100;
if (bssid)
memcpy(STA_CFG->wifi_bssid, bssid, 6);
else
memset(STA_CFG->wifi_bssid, 0x00, 6);
if (STA_CFG->dhcp_mode == DHCP_DISABLE) {
LT_DM(WIFI, "Static IP: %s / %s / %s", STA_CFG->local_ip_addr, STA_CFG->net_mask, STA_CFG->gateway_ip_addr);
LT_DM(WIFI, "Static DNS: %s", STA_CFG->dns_server_ip_addr);
} else {
LT_DM(WIFI, "Using DHCP");
}
LT_DM(WIFI, "Starting WiFi...");
__wrap_bk_printf_disable();
bk_wlan_start_sta(STA_CFG);
__wrap_bk_printf_enable();
LT_DM(WIFI, "Start OK");
return true;
error:
return false;
}
bool WiFiClass::disconnect(bool wifiOff) {
#if LT_DEBUG_WIFI
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef));
bk_wlan_get_link_status(LINK_STATUS);
LT_DM(WIFI, "Disconnecting from %s (wifiOff=%d)", LINK_STATUS ? LINK_STATUS->ssid : NULL, wifiOff);
#endif
bk_wlan_connection_loss();
if (wifiOff)
enableSTA(false);
return true;
}
bool WiFiClass::setAutoReconnect(bool autoReconnect) {
return false;
}
bool WiFiClass::getAutoReconnect() {
return false;
}
IPAddress WiFiClass::localIP() {
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->ip);
return ip;
}
IPAddress WiFiClass::subnetMask() {
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->mask);
return ip;
}
IPAddress WiFiClass::gatewayIP() {
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->gate);
return ip;
}
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->dns);
return ip;
}
IPAddress WiFiClass::broadcastIP() {
return calculateBroadcast(localIP(), subnetMask());
}
const char *WiFiClass::getHostname() {
struct netif *ifs = (struct netif *)net_get_sta_handle();
return netif_get_hostname(ifs);
}
bool WiFiClass::setHostname(const char *hostname) {
struct netif *ifs = (struct netif *)net_get_sta_handle();
netif_set_hostname(ifs, (char *)hostname);
return true;
}
uint8_t *WiFiClass::macAddress(uint8_t *mac) {
wifi_get_mac_address((char *)mac, CONFIG_ROLE_STA);
return mac;
}
bool WiFiClass::setMacAddress(const uint8_t *mac) {
if (mac[0] & 0x01) {
LT_EM(WIFI, "Invalid MAC address");
return false;
}
// ensure "mac_inited" is true
wifi_get_mac_address((char *)system_mac, BK_STATION);
// store the MAC globally
memcpy(system_mac, mac, 6);
WiFiMode previousMode = getMode();
if (previousMode) {
mode(WIFI_MODE_NULL);
mode(previousMode);
}
return true;
}
const String WiFiClass::SSID() {
STA_GET_LINK_STATUS_RETURN("");
return (char *)LINK_STATUS->ssid;
}
const String WiFiClass::psk() {
if (!isConnected())
return "";
struct wpa_supplicant *wpas = wpa_suppliant_ctrl_get_wpas();
if (!wpas || !wpas->conf || !wpas->conf->ssid)
return "";
return (char *)wpas->conf->ssid->passphrase;
}
uint8_t *WiFiClass::BSSID() {
STA_GET_LINK_STATUS_RETURN(NULL);
return LINK_STATUS->bssid;
}
int32_t WiFiClass::channel() {
STA_GET_LINK_STATUS_RETURN(0);
return LINK_STATUS->channel;
}
int8_t WiFiClass::RSSI() {
STA_GET_LINK_STATUS_RETURN(0);
return LINK_STATUS->wifi_strength;
}
WiFiAuthMode WiFiClass::getEncryption() {
STA_GET_LINK_STATUS_RETURN(WIFI_AUTH_INVALID);
return securityTypeToAuthMode(LINK_STATUS->security);
}

View File

@@ -0,0 +1,95 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
#include "WiFiPriv.h"
static void scanHandler(void *ctx, uint8_t param) {
LT_HEAP_I();
WiFiClass *cls = (WiFiClass *)ctx;
if (!cls) {
LT_WM(WIFI, "Called without ctx");
return;
}
WiFiScanData *scan = cls->scan;
if (!scan) {
LT_WM(WIFI, "Called without cls->scan");
return;
}
ScanResult_adv result;
if (wlan_sta_scan_result(&result)) {
LT_EM(WIFI, "Failed to get scan result");
goto end;
}
LT_IM(WIFI, "Found %d APs", result.ApNum);
cls->scanAlloc(result.ApNum);
if (!scan->ap) {
LT_WM(WIFI, "scan->ap alloc failed");
goto end;
}
for (uint8_t i = 0; i < result.ApNum; i++) {
scan->ap[i].ssid = strdup(result.ApList[i].ssid);
scan->ap[i].auth = securityTypeToAuthMode(result.ApList[i].security);
scan->ap[i].rssi = result.ApList[i].ApPower;
scan->ap[i].channel = result.ApList[i].channel;
memcpy(scan->ap[i].bssid.addr, result.ApList[i].bssid, 6);
}
cls->data.scannedAt = millis();
wifiEventSendArduino(ARDUINO_EVENT_WIFI_SCAN_DONE);
end:
scan->timeout = 0;
if (scan->running) {
// running == false means it was discarded (timeout)
scan->running = false;
xSemaphoreGive(cls->data.scanSem);
}
LT_HEAP_I();
return;
}
int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint32_t maxMsPerChannel, uint8_t channel) {
if (scan && scan->running) {
if (scan->timeout && millis() > scan->timeout) {
LT_WM(WIFI, "Scan timeout, discarding");
scan->running = false;
} else {
return WIFI_SCAN_RUNNING;
}
}
enableSTA(true);
scanDelete();
scanInit();
LT_IM(WIFI, "Starting WiFi scan");
__wrap_bk_printf_disable();
mhdr_scanu_reg_cb(scanHandler, this);
bk_wlan_start_scan();
LT_HEAP_I();
scan->running = true;
scan->timeout = millis() + maxMsPerChannel * 20 + 1000;
int16_t ret = WIFI_SCAN_RUNNING;
if (!async) {
LT_IM(WIFI, "Waiting for results");
xSemaphoreTake(data.scanSem, 1); // reset the semaphore quickly
xSemaphoreTake(data.scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
if (scan->running) {
scanDelete();
ret = WIFI_SCAN_FAILED;
goto exit;
}
ret = scan->count;
goto exit;
}
exit:
__wrap_bk_printf_enable();
return ret;
}

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-27. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <lwip/LwIPServer.h>
typedef LwIPServer WiFiServer;

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2022-09-10. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <lwip/LwIPUdp.h>
typedef LwIPUDP WiFiUDP;

View File

@@ -0,0 +1,65 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <lt_logger.h>
#include <sdk_extern.h>
#include <fal.h>
#include <flash_pub.h>
#define FLASH_ERASE_MIN_SIZE (4 * 1024)
extern uint32_t flash_ctrl(uint32_t cmd, void *param);
extern PROTECT_TYPE get_flash_protect();
extern void flash_protection_op(uint8_t mode, PROTECT_TYPE type);
static void unprotect() {
PROTECT_TYPE type = get_flash_protect();
if (type != FLASH_PROTECT_NONE) {
flash_protection_op(0, FLASH_PROTECT_NONE);
#if LT_LOGLEVEL <= LT_LEVEL_DEBUG
LT_D("Flash protect: %u -> %u", type, get_flash_protect());
uint16_t sr = 0;
flash_ctrl(CMD_FLASH_READ_SR, &sr);
LT_D("SR = %04x", sr);
#endif
}
}
static int init() {
__wrap_bk_printf_disable();
flash_init();
flash_ctrl(CMD_FLASH_WRITE_ENABLE, NULL);
unprotect();
__wrap_bk_printf_enable();
return 0;
}
static int read(long offset, uint8_t *buf, size_t size) {
flash_read((char *)buf, size, offset);
return size;
}
static int write(long offset, const uint8_t *buf, size_t size) {
unprotect();
flash_write((char *)buf, size, offset);
return size;
}
static int erase(long offset, size_t size) {
unprotect();
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
for (uint16_t i = 0; i < size; i++) {
uint32_t addr = offset + i * FLASH_ERASE_MIN_SIZE;
flash_ctrl(CMD_FLASH_ERASE_SECTOR, &addr);
}
return size * FLASH_ERASE_MIN_SIZE;
}
const struct fal_flash_dev flash0 = {
.name = FAL_FLASH_DEV_NAME,
.addr = 0x0,
.len = FLASH_LENGTH,
.blk_size = FLASH_ERASE_MIN_SIZE,
.ops = {init, read, write, erase},
.write_gran = 1,
};

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <printf_config.h>
#include <printf/printf.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
extern void bk_send_byte(uint8_t uport, uint8_t data);
extern int uart_print_port;
void putchar_(char c) {
bk_send_byte(uart_print_port, c);
}
void putchar_p(char c, unsigned long port) {
bk_send_byte((port & 0xFF) - 1, c);
}
WRAP_PRINTF(bk_printf);

View File

@@ -0,0 +1,7 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#pragma once
#include <printf_config.h>
WRAP_DISABLE_DEF(bk_printf);

View File

@@ -1,2 +0,0 @@
realtek-ambz Realtek AmebaZ Arduino Core
libretuya Interfaces for LibreTuya Arduino cores

View File

@@ -22,10 +22,10 @@
#pragma once
#include <Arduino.h>
#include <api/WiFi/WiFiEvents.h>
#include <functional>
#include "WiFiEvents.h"
typedef enum {
ARDUINO_EVENT_WIFI_READY = 0, /**< ESP32 WiFi ready */
ARDUINO_EVENT_WIFI_SCAN_DONE, /**< ESP32 finish scanning AP */

View File

@@ -1,27 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct {
uint8_t manufacturerId;
uint8_t chipId;
uint8_t chipSizeId;
} FlashId;
class IFlashClass {
public:
IFlashClass() {}
~IFlashClass() {}
virtual FlashId getChipId() = 0;
virtual uint32_t getSize() = 0;
virtual bool eraseSector(uint32_t sector) = 0;
virtual bool readBlock(uint32_t offset, uint8_t *data, size_t size) = 0;
virtual bool writeBlock(uint32_t offset, uint8_t *data, size_t size) = 0;
};

View File

@@ -0,0 +1,45 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-03. */
#include "SoftwareSerial.h"
#ifdef LT_ARD_HAS_SOFTSERIAL
SoftwareSerial::SoftwareSerial(pin_size_t receivePin, pin_size_t transmitPin, bool inverted) {
data.rx.buf = NULL;
data.tx.buf = NULL;
data.rx.pin = receivePin;
data.tx.pin = transmitPin;
data.invert = inverted == true;
}
int SoftwareSerial::available() {
return data.rx.buf->available();
}
int SoftwareSerial::peek() {
return data.rx.buf->peek();
}
int SoftwareSerial::read() {
return data.rx.buf->read_char();
}
void SoftwareSerial::flush() {
while (data.rx.buf->available()) {
yield();
}
}
size_t SoftwareSerial::write(uint8_t c) {
while (data.tx.buf->isFull()) {
yield();
}
data.tx.buf->store_char(c);
if (data.tx.state == SS_IDLE) {
data.tx.state = SS_START;
this->startTx();
}
return 1;
}
#endif

View File

@@ -0,0 +1,76 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-03. */
#pragma once
#ifdef LT_ARD_HAS_SOFTSERIAL
#include <Arduino.h>
#include <api/HardwareSerial.h>
#include <api/RingBuffer.h>
using namespace arduino;
typedef enum {
SS_IDLE = 0,
SS_START,
SS_DATA0,
SS_DATA1,
SS_DATA2,
SS_DATA3,
SS_DATA4,
SS_DATA5,
SS_DATA6,
SS_DATA7,
SS_STOP,
SS_END,
} SoftState;
typedef struct {
SoftState state;
RingBuffer *buf;
uint8_t byte;
pin_size_t pin;
void *param;
} SoftData;
typedef struct {
SoftData rx;
SoftData tx;
uint8_t invert;
void *param;
} SoftSerial;
class SoftwareSerial : public HardwareSerial {
private:
SoftSerial data;
void *param;
public:
SoftwareSerial(pin_size_t receivePin, pin_size_t transmitPin, bool inverted = false);
inline void begin(unsigned long baudrate) {
begin(baudrate, SERIAL_8N1);
}
int available();
int peek();
int read();
void flush();
size_t write(uint8_t c);
operator bool() {
return data.rx.buf || data.tx.buf;
}
public: // Family needs to implement these methods only
void begin(unsigned long baudrate, uint16_t config);
void end();
private:
void startTx();
void endTx();
using Print::write;
};
#endif

View File

@@ -1,190 +0,0 @@
/*
WiFi.h - esp32 Wifi support.
Based on WiFi.h from Arduino WiFi shield library.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <stdint.h>
#include <api/IPAddress.h>
#include <api/IPv6Address.h>
#include <api/Print.h>
#include <vector>
#include "Events.h"
#include "WiFiType.h"
class IWiFiClass {
public:
virtual void printDiag(Print &dest) = 0;
};
class IWiFiGenericClass {
public:
virtual int32_t channel(void) = 0;
virtual bool mode(WiFiMode mode) = 0;
virtual WiFiMode getMode() = 0;
virtual WiFiStatus status() = 0;
virtual bool enableSTA(bool enable) = 0;
virtual bool enableAP(bool enable) = 0;
virtual bool setSleep(bool enable) = 0;
virtual bool getSleep() = 0;
virtual bool setTxPower(int power) = 0;
virtual int getTxPower() = 0;
virtual int hostByName(const char *hostname, IPAddress &aResult) {
aResult = hostByName(hostname);
return true;
}
virtual IPAddress hostByName(const char *hostname) = 0;
static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet);
static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet);
static uint8_t calculateSubnetCIDR(IPAddress subnetMask);
static String macToString(uint8_t *mac);
protected:
static std::vector<EventHandler> handlers;
public:
uint16_t onEvent(EventCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventFuncCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventSysCb callback, EventId eventId = ARDUINO_EVENT_MAX);
void removeEvent(EventCb callback, EventId eventId);
void removeEvent(EventSysCb callback, EventId eventId);
void removeEvent(uint16_t id);
protected:
static void postEvent(EventId eventId, EventInfo eventInfo);
};
class IWiFiSTAClass {
public:
virtual WiFiStatus begin(
const char *ssid,
const char *passphrase = NULL,
int32_t channel = 0,
const uint8_t *bssid = NULL,
bool connect = true
) = 0;
virtual WiFiStatus begin(
char *ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t *bssid = NULL, bool connect = true
) = 0;
virtual bool config(
IPAddress localIP,
IPAddress gateway,
IPAddress subnet,
IPAddress dns1 = (uint32_t)0x00000000,
IPAddress dns2 = (uint32_t)0x00000000
) = 0;
virtual bool reconnect() = 0;
virtual bool disconnect(bool wifiOff = false) = 0;
virtual bool isConnected();
virtual bool setAutoReconnect(bool autoReconnect) = 0;
virtual bool getAutoReconnect() = 0;
virtual WiFiStatus waitForConnectResult(unsigned long timeout) = 0;
virtual IPAddress localIP() = 0;
virtual uint8_t *macAddress(uint8_t *mac) = 0;
virtual String macAddress() = 0;
virtual IPAddress subnetMask() = 0;
virtual IPAddress gatewayIP() = 0;
virtual IPAddress dnsIP(uint8_t dns_no = 0) = 0;
virtual IPAddress broadcastIP() = 0;
virtual IPAddress networkID() = 0;
virtual uint8_t subnetCIDR() = 0;
virtual bool enableIpV6() = 0;
virtual IPv6Address localIPv6() = 0;
virtual const char *getHostname() = 0;
virtual bool setHostname(const char *hostname) = 0;
virtual bool setMacAddress(const uint8_t *mac) = 0;
inline bool hostname(const String &aHostname) {
return setHostname(aHostname.c_str());
}
virtual const String SSID() = 0;
virtual const String psk() = 0;
virtual uint8_t *BSSID() = 0;
virtual String BSSIDstr() = 0;
virtual int8_t RSSI() = 0;
virtual WiFiAuthMode getEncryption() = 0;
};
class IWiFiScanClass {
public:
virtual int16_t scanNetworks(
bool async = false,
bool showHidden = false,
bool passive = false,
uint32_t maxMsPerChannel = 300,
uint8_t channel = 0
) = 0;
virtual bool getNetworkInfo(
uint8_t networkItem,
String &ssid,
WiFiAuthMode &encryptionType,
int32_t &RSSI,
uint8_t *&BSSID,
int32_t &channel
) = 0;
virtual int16_t scanComplete() = 0;
virtual void scanDelete() = 0;
virtual String SSID(uint8_t networkItem) = 0;
virtual WiFiAuthMode encryptionType(uint8_t networkItem) = 0;
virtual int32_t RSSI(uint8_t networkItem) = 0;
virtual uint8_t *BSSID(uint8_t networkItem) = 0;
virtual String BSSIDstr(uint8_t networkItem) = 0;
virtual int32_t channel(uint8_t networkItem) = 0;
};
class IWiFiAPClass {
public:
virtual bool softAP(
const char *ssid, const char *passphrase = NULL, int channel = 1, bool ssidHidden = false, int maxClients = 4
) = 0;
virtual bool softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet) = 0;
virtual bool softAPdisconnect(bool wifiOff = false) = 0;
virtual uint8_t softAPgetStationNum() = 0;
virtual IPAddress softAPIP() = 0;
virtual IPAddress softAPBroadcastIP() = 0;
virtual IPAddress softAPNetworkID() = 0;
virtual uint8_t softAPSubnetCIDR() = 0;
virtual bool softAPenableIpV6() = 0;
virtual IPv6Address softAPIPv6() = 0;
virtual const char *softAPgetHostname() = 0;
virtual bool softAPsetHostname(const char *hostname) = 0;
virtual uint8_t *softAPmacAddress(uint8_t *mac) = 0;
virtual String softAPmacAddress(void) = 0;
virtual const String softAPSSID(void) = 0;
};

View File

@@ -0,0 +1,77 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h"
void WiFiClass::printDiag(Print &dest) {
const char *modes[] = {"NULL", "STA", "AP", "STA+AP"};
const char *enc[] = {"Open", "WEP", "WPA PSK", "WPA2 PSK", "WPA/WPA2", "WPA", "WPA2"};
dest.print("Mode: ");
dest.println(modes[getMode()]);
if (getMode() & WIFI_MODE_STA) {
dest.println("-- Station --");
dest.print("SSID: ");
if (isConnected()) {
dest.println(SSID());
dest.print("Channel: ");
dest.println(channel());
dest.print("BSSID: ");
dest.println(BSSIDstr());
dest.print("RSSI: ");
dest.println(RSSI());
dest.print("Encryption: ");
dest.println(enc[getEncryption()]);
dest.print("IP: ");
dest.println(localIP());
dest.print("MAC: ");
dest.println(macAddress());
dest.print("Hostname: ");
dest.println(getHostname());
} else {
dest.println("disconnected");
}
}
if (getMode() & WIFI_MODE_AP) {
dest.println("-- Access Point --");
dest.print("SSID: ");
if (softAPSSID().length()) {
dest.println(softAPSSID());
dest.print("IP: ");
dest.println(softAPIP());
dest.print("MAC: ");
dest.println(softAPmacAddress());
dest.print("Hostname: ");
dest.println(softAPgetHostname());
} else {
dest.println("disconnected");
}
}
}
bool WiFiClass::validate(const char *ssid, const char *passphrase) {
if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) {
LT_WM(WIFI, "SSID not specified or too long");
return false;
}
if (passphrase) {
uint16_t length = strlen(passphrase);
if (length < 8) {
LT_WM(WIFI, "Passphrase too short (%u)", length);
return false;
}
if (length > 63) {
LT_WM(WIFI, "Passphrase too long (%u)", length);
return false;
}
}
return true;
}
__attribute__((weak)) void WiFiClass::dataInitialize() {}
__attribute__((weak)) void WiFiClass::dataFree() {}
WiFiClass WiFi;
WiFiClass *pWiFi = NULL;

View File

@@ -0,0 +1,197 @@
/*
WiFi.h - esp32 Wifi support.
Based on WiFi.h from Arduino WiFi shield library.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <Arduino.h>
#include <api/Events.h>
#include <api/IPAddress.h>
#include <api/IPv6Address.h>
#include <vector>
#include "WiFiType.h"
#ifdef LT_ARD_HAS_WIFI
// family's data structure
#include <WiFiData.h>
#endif
class WiFiClass {
public:
#ifdef LT_ARD_HAS_WIFI
// must be public for WiFiEvents & WiFiScan static handlers
WiFiData data;
#endif
WiFiScanData *scan = NULL;
public: /* WiFi.cpp */
WiFiClass();
~WiFiClass();
void printDiag(Print &dest);
bool validate(const char *ssid, const char *passphrase);
void dataInitialize();
void dataFree();
public: /* WiFiGeneric.cpp */
bool mode(WiFiMode mode);
bool modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap);
WiFiMode getMode();
WiFiStatus status();
bool enableSTA(bool enable);
bool enableAP(bool enable);
bool setSleep(bool enable);
bool getSleep();
bool setTxPower(int power);
int getTxPower();
int hostByName(const char *hostname, IPAddress &aResult);
IPAddress hostByName(const char *hostname);
static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet);
static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet);
static uint8_t calculateSubnetCIDR(IPAddress subnetMask);
static String macToString(uint8_t *mac);
protected: /* WiFiEvents.cpp */
static std::vector<EventHandler> handlers;
public: /* WiFiEvents.cpp */
uint16_t onEvent(EventCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventFuncCb callback, EventId eventId = ARDUINO_EVENT_MAX);
uint16_t onEvent(EventSysCb callback, EventId eventId = ARDUINO_EVENT_MAX);
void removeEvent(EventCb callback, EventId eventId);
void removeEvent(EventSysCb callback, EventId eventId);
void removeEvent(uint16_t id);
static void postEvent(EventId eventId, EventInfo eventInfo);
public: /* WiFiSTA.cpp */
WiFiStatus begin(
const char *ssid,
const char *passphrase = NULL,
int32_t channel = 0,
const uint8_t *bssid = NULL,
bool connect = true
);
WiFiStatus
begin(char *ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t *bssid = NULL, bool connect = true);
bool config(
IPAddress localIP,
IPAddress gateway,
IPAddress subnet,
IPAddress dns1 = (uint32_t)0x00000000,
IPAddress dns2 = (uint32_t)0x00000000
);
bool reconnect(const uint8_t *bssid = NULL);
bool disconnect(bool wifiOff = false);
bool isConnected();
bool setAutoReconnect(bool autoReconnect);
bool getAutoReconnect();
WiFiStatus waitForConnectResult(unsigned long timeout);
IPAddress localIP();
uint8_t *macAddress(uint8_t *mac);
String macAddress();
IPAddress subnetMask();
IPAddress gatewayIP();
IPAddress dnsIP(uint8_t dns_no = 0);
IPAddress broadcastIP();
IPAddress networkID();
uint8_t subnetCIDR();
bool enableIpV6();
IPv6Address localIPv6();
const char *getHostname();
bool setHostname(const char *hostname);
bool setMacAddress(const uint8_t *mac);
inline bool hostname(const String &aHostname) {
return setHostname(aHostname.c_str());
}
const String SSID();
const String psk();
uint8_t *BSSID();
String BSSIDstr();
int32_t channel();
int8_t RSSI();
WiFiAuthMode getEncryption();
public: /* WiFiScan.cpp */
int16_t scanNetworks(
bool async = false,
bool showHidden = false,
bool passive = false,
uint32_t maxMsPerChannel = 300,
uint8_t channel = 0
);
bool getNetworkInfo(
uint8_t networkItem,
String &ssid,
WiFiAuthMode &encryptionType,
int32_t &RSSI,
uint8_t *&BSSID,
int32_t &channel
);
int16_t scanComplete();
uint8_t scanAlloc(uint8_t count);
void scanInit();
void scanDelete();
String SSID(uint8_t networkItem);
WiFiAuthMode encryptionType(uint8_t networkItem);
int32_t RSSI(uint8_t networkItem);
uint8_t *BSSID(uint8_t networkItem);
String BSSIDstr(uint8_t networkItem);
int32_t channel(uint8_t networkItem);
public: /* WiFiAP.cpp */
bool softAP(
const char *ssid, const char *passphrase = NULL, int channel = 1, bool ssidHidden = false, int maxClients = 4
);
bool softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet);
bool softAPdisconnect(bool wifiOff = false);
uint8_t softAPgetStationNum();
IPAddress softAPIP();
IPAddress softAPBroadcastIP();
IPAddress softAPNetworkID();
uint8_t softAPSubnetCIDR();
IPAddress softAPSubnetMask();
bool softAPenableIpV6();
IPv6Address softAPIPv6();
const char *softAPgetHostname();
bool softAPsetHostname(const char *hostname);
uint8_t *softAPmacAddress(uint8_t *mac);
String softAPmacAddress(void);
const String softAPSSID(void);
};
extern WiFiClass WiFi;
extern WiFiClass *pWiFi;

View File

@@ -0,0 +1,23 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h"
IPAddress WiFiClass::softAPBroadcastIP() {
return calculateBroadcast(softAPIP(), softAPSubnetMask());
}
IPAddress WiFiClass::softAPNetworkID() {
return calculateNetworkID(softAPIP(), softAPSubnetMask());
}
uint8_t WiFiClass::softAPSubnetCIDR() {
return calculateSubnetCIDR(softAPSubnetMask());
}
__attribute__((weak)) bool WiFiClass::softAPenableIpV6() {
return false;
}
__attribute__((weak)) IPv6Address WiFiClass::softAPIPv6() {
return IPv6Address();
}

View File

@@ -2,9 +2,9 @@
#include "WiFi.h"
std::vector<EventHandler> IWiFiGenericClass::handlers;
std::vector<EventHandler> WiFiClass::handlers;
uint16_t IWiFiGenericClass::onEvent(EventCb callback, EventId eventId) {
uint16_t WiFiClass::onEvent(EventCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
@@ -14,7 +14,7 @@ uint16_t IWiFiGenericClass::onEvent(EventCb callback, EventId eventId) {
return handler.id;
}
uint16_t IWiFiGenericClass::onEvent(EventFuncCb callback, EventId eventId) {
uint16_t WiFiClass::onEvent(EventFuncCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
@@ -24,7 +24,7 @@ uint16_t IWiFiGenericClass::onEvent(EventFuncCb callback, EventId eventId) {
return handler.id;
}
uint16_t IWiFiGenericClass::onEvent(EventSysCb callback, EventId eventId) {
uint16_t WiFiClass::onEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
@@ -34,7 +34,7 @@ uint16_t IWiFiGenericClass::onEvent(EventSysCb callback, EventId eventId) {
return handler.id;
}
void IWiFiGenericClass::removeEvent(EventCb callback, EventId eventId) {
void WiFiClass::removeEvent(EventCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
@@ -45,7 +45,7 @@ void IWiFiGenericClass::removeEvent(EventCb callback, EventId eventId) {
}
}
void IWiFiGenericClass::removeEvent(EventSysCb callback, EventId eventId) {
void WiFiClass::removeEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
@@ -56,7 +56,7 @@ void IWiFiGenericClass::removeEvent(EventSysCb callback, EventId eventId) {
}
}
void IWiFiGenericClass::removeEvent(uint16_t id) {
void WiFiClass::removeEvent(uint16_t id) {
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.id == id) {
@@ -65,7 +65,7 @@ void IWiFiGenericClass::removeEvent(uint16_t id) {
}
}
void IWiFiGenericClass::postEvent(EventId eventId, EventInfo eventInfo) {
void WiFiClass::postEvent(EventId eventId, EventInfo eventInfo) {
for (auto handler : handlers) {
if (handler.eventId != ARDUINO_EVENT_MAX && handler.eventId != eventId)
continue;

View File

@@ -0,0 +1,117 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h"
bool WiFiClass::mode(WiFiMode mode) {
// store a pointer to WiFi for WiFiEvents.cpp
pWiFi = this;
WiFiMode currentMode = getMode();
LT_DM(WIFI, "Mode changing %u -> %u", currentMode, mode);
if (mode == currentMode)
return true;
// get mode changes as 0/1
WiFiModeAction sta = WiFiModeAction((mode & WIFI_MODE_STA) != (currentMode & WIFI_MODE_STA));
WiFiModeAction ap = WiFiModeAction((mode & WIFI_MODE_AP) != (currentMode & WIFI_MODE_AP));
// change 0/1 to 1/2
sta = WiFiModeAction(sta + sta * !!(mode & WIFI_MODE_STA));
ap = WiFiModeAction(ap + ap * !!(mode & WIFI_MODE_AP));
// initialize data structures if wifi is enabled
if (mode)
dataInitialize();
// actually change the mode
LT_HEAP_I();
if (!modePriv(mode, sta, ap))
return false;
if (getMode() != mode) {
LT_WM(WIFI, "Mode changed to %d (requested %d)", getMode(), mode);
}
return true;
}
bool WiFiClass::enableSTA(bool enable) {
WiFiMode currentMode = getMode();
if (((currentMode & WIFI_MODE_STA) != 0) != enable) {
return mode((WiFiMode)(currentMode ^ WIFI_MODE_STA));
}
return true;
}
bool WiFiClass::enableAP(bool enable) {
WiFiMode currentMode = getMode();
if (((currentMode & WIFI_MODE_AP) != 0) != enable) {
return mode((WiFiMode)(currentMode ^ WIFI_MODE_AP));
}
return true;
}
__attribute__((weak)) bool WiFiClass::setSleep(bool enable) {
return false;
}
__attribute__((weak)) bool WiFiClass::getSleep() {
return false;
}
__attribute__((weak)) bool WiFiClass::setTxPower(int power) {
return false;
}
__attribute__((weak)) int WiFiClass::getTxPower() {
return 0;
}
int WiFiClass::hostByName(const char *hostname, IPAddress &aResult) {
aResult = hostByName(hostname);
return true;
}
IPAddress WiFiClass::calculateNetworkID(IPAddress ip, IPAddress subnet) {
IPAddress networkID;
for (size_t i = 0; i < 4; i++)
networkID[i] = subnet[i] & ip[i];
return networkID;
}
IPAddress WiFiClass::calculateBroadcast(IPAddress ip, IPAddress subnet) {
IPAddress broadcastIp;
for (int i = 0; i < 4; i++)
broadcastIp[i] = ~subnet[i] | ip[i];
return broadcastIp;
}
uint8_t WiFiClass::calculateSubnetCIDR(IPAddress subnetMask) {
uint8_t CIDR = 0;
for (uint8_t i = 0; i < 4; i++) {
if (subnetMask[i] == 0x80) // 128
CIDR += 1;
else if (subnetMask[i] == 0xC0) // 192
CIDR += 2;
else if (subnetMask[i] == 0xE0) // 224
CIDR += 3;
else if (subnetMask[i] == 0xF0) // 242
CIDR += 4;
else if (subnetMask[i] == 0xF8) // 248
CIDR += 5;
else if (subnetMask[i] == 0xFC) // 252
CIDR += 6;
else if (subnetMask[i] == 0xFE) // 254
CIDR += 7;
else if (subnetMask[i] == 0xFF) // 255
CIDR += 8;
}
return CIDR;
}
String WiFiClass::macToString(uint8_t *mac) {
char macStr[18]; // 6*2 + 5*':' + '\0'
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return macStr;
}

View File

@@ -0,0 +1,48 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h"
WiFiStatus WiFiClass::begin(char *ssid, char *passphrase, int32_t channel, const uint8_t *bssid, bool connect) {
return begin((const char *)ssid, (const char *)passphrase, channel, bssid, connect);
}
bool WiFiClass::isConnected() {
return status() == WL_CONNECTED;
}
WiFiStatus WiFiClass::waitForConnectResult(unsigned long timeout) {
if ((getMode() & WIFI_MODE_STA) == 0) {
return WL_DISCONNECTED;
}
unsigned long start = millis();
while ((!status() || status() >= WL_DISCONNECTED) && (millis() - start) < timeout) {
delay(100);
}
return status();
}
String WiFiClass::macAddress(void) {
uint8_t mac[6];
macAddress(mac);
return macToString(mac);
}
IPAddress WiFiClass::networkID() {
return calculateNetworkID(gatewayIP(), subnetMask());
}
uint8_t WiFiClass::subnetCIDR() {
return calculateSubnetCIDR(subnetMask());
}
String WiFiClass::BSSIDstr() {
return macToString(BSSID());
}
__attribute__((weak)) bool WiFiClass::enableIpV6() {
return false;
}
__attribute__((weak)) IPv6Address WiFiClass::localIPv6() {
return IPv6Address();
}

View File

@@ -0,0 +1,83 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h"
bool WiFiClass::getNetworkInfo(
uint8_t networkItem, String &ssid, WiFiAuthMode &encType, int32_t &rssi, uint8_t *&bssid, int32_t &channel
) {
ssid = SSID(networkItem);
encType = encryptionType(networkItem);
rssi = RSSI(networkItem);
bssid = BSSID(networkItem);
channel = this->channel(networkItem);
return true;
}
int16_t WiFiClass::scanComplete() {
if (!scan)
return 0;
if (scan->running)
return WIFI_SCAN_RUNNING;
return scan->count;
}
void WiFiClass::scanInit() {
if (scan)
return;
scan = (WiFiScanData *)zalloc(sizeof(WiFiScanData));
}
void WiFiClass::scanDelete() {
if (!scan)
return;
for (uint8_t i = 0; i < scan->count; i++) {
free(scan->ap[i].ssid);
}
free(scan->ap);
free(scan);
scan = NULL;
}
uint8_t WiFiClass::scanAlloc(uint8_t count) {
uint8_t last = scan->count;
scan->count = count;
scan->ap = (WiFiScanAP *)realloc(scan->ap, count * sizeof(WiFiScanAP));
if (!scan->ap)
return 255;
memset(scan->ap + last, 0, sizeof(WiFiScanAP));
return last;
}
String WiFiClass::SSID(uint8_t networkItem) {
if (!scan || networkItem >= scan->count)
return "";
return scan->ap[networkItem].ssid;
}
WiFiAuthMode WiFiClass::encryptionType(uint8_t networkItem) {
if (!scan || networkItem >= scan->count)
return WIFI_AUTH_INVALID;
return scan->ap[networkItem].auth;
}
int32_t WiFiClass::RSSI(uint8_t networkItem) {
if (!scan || networkItem >= scan->count)
return 0;
return scan->ap[networkItem].rssi;
}
uint8_t *WiFiClass::BSSID(uint8_t networkItem) {
if (!scan || networkItem >= scan->count)
return NULL;
return scan->ap[networkItem].bssid.addr;
}
String WiFiClass::BSSIDstr(uint8_t networkItem) {
return macToString(BSSID(networkItem));
}
int32_t WiFiClass::channel(uint8_t networkItem) {
if (!scan || networkItem >= scan->count)
return 0;
return scan->ap[networkItem].channel;
}

View File

@@ -21,6 +21,8 @@
#pragma once
#include <Arduino.h>
#define WIFI_SCAN_RUNNING (-1)
#define WIFI_SCAN_FAILED (-2)
@@ -38,6 +40,10 @@
#define WiFiEventInfo_t arduino_event_info_t
#define WiFiEventId_t uint16_t
typedef struct {
uint8_t addr[6];
} WiFiMacAddr;
struct esp_ip6_addr {
uint32_t addr[4];
uint8_t zone;
@@ -121,3 +127,24 @@ typedef enum {
WIFI_REASON_AP_TSF_RESET = 206,
WIFI_REASON_ROAMING = 207,
} wifi_err_reason_t;
typedef struct {
char *ssid;
WiFiAuthMode auth;
int32_t rssi;
WiFiMacAddr bssid;
int32_t channel;
} WiFiScanAP;
typedef struct {
bool running = false;
unsigned long timeout = 0;
uint8_t count = 0;
WiFiScanAP *ap = NULL;
} WiFiScanData;
typedef enum {
WLMODE_NONE = 0,
WLMODE_DISABLE = 1,
WLMODE_ENABLE = 2,
} WiFiModeAction;

View File

@@ -5,7 +5,28 @@
class IWiFiUDP : public UDP {
public:
uint8_t beginMulticast(IPAddress ip, uint16_t port);
int beginMulticastPacket();
int beginPacket();
IWiFiUDP() {}
~IWiFiUDP() {}
virtual uint8_t begin(IPAddress ip, uint16_t port) = 0;
virtual uint8_t begin(uint16_t port) = 0;
virtual uint8_t beginMulticast(IPAddress ip, uint16_t port) = 0;
virtual void stop() = 0;
virtual int beginMulticastPacket() = 0;
virtual int beginPacket() = 0;
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
virtual int beginPacket(const char *host, uint16_t port) = 0;
virtual int endPacket() = 0;
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
virtual int parsePacket() = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int read(unsigned char *buffer, size_t len) = 0;
virtual int read(char *buffer, size_t len) = 0;
virtual int peek() = 0;
virtual void flush() = 0;
virtual IPAddress remoteIP() = 0;
virtual uint16_t remotePort() = 0;
};

View File

@@ -41,15 +41,3 @@ long random(long howsmall, long howbig) {
return random(diff) + howsmall;
}
extern long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
extern uint16_t makeWord(uint16_t w) {
return w;
}
extern uint16_t makeWord(uint8_t h, uint8_t l) {
return (h << 8) | l;
}

View File

@@ -0,0 +1,11 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
void serialEvent() __attribute__((weak));
bool Serial_available() __attribute__((weak));
void serialEventRun(void) {
if (Serial_available && serialEvent && Serial_available())
serialEvent();
}

View File

@@ -0,0 +1,6 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-04. */
#pragma once
// lowercase "md5.h" to allow including actual MD5.h on case-sensitive OSes
#include <MD5.h>

View File

@@ -0,0 +1,37 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#define CHIP_TYPE(family, chip_id) (((family >> 24) << 8) | chip_id)
#define CHIP_TYPE_ENUM(family, chip_id) (ChipType) CHIP_TYPE(family, chip_id)
enum ChipFamily {
// used in UF2 Family ID
F_RTL8710A = 0x9FFFD543, // Realtek Ameba1
F_RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz)
F_RTL8720C = 0xE08F7564, // Realtek AmebaZ2
F_RTL8720D = 0x3379CFE2, // Realtek AmebaD
F_BK7231U = 0x675A40B0, // Beken 7231U/7231T
F_BK7231N = 0x7B3EF230, // Beken 7231N
F_BK7251 = 0x6A82CC42, // Beken 7251/7252
F_BL602 = 0xDE1270B7, // Boufallo 602
F_XR809 = 0x51E903A8, // Xradiotech 809
F_NATIVE = 0xDEADBEEF, // Host-native
};
enum ChipType {
// Realtek AmebaZ
// IDs copied from rtl8710b_efuse.h
RTL8710BL = CHIP_TYPE(F_RTL8710B, 0xE0), // ???
RTL8710BN = CHIP_TYPE(F_RTL8710B, 0xFF), // CHIPID_8710BN / QFN32
RTL8710BU = CHIP_TYPE(F_RTL8710B, 0xFE), // CHIPID_8710BU / QFN48
RTL8710BX = CHIP_TYPE(F_RTL8710B, 0xF6), // found on an actual RTL8710BX
RTL8710L0 = CHIP_TYPE(F_RTL8710B, 0xFB), // CHIPID_8710BN_L0 / QFN32
RTL8711BN = CHIP_TYPE(F_RTL8710B, 0xFD), // CHIPID_8711BN / QFN48
RTL8711BU = CHIP_TYPE(F_RTL8710B, 0xFC), // CHIPID_8711BG / QFN68
// Beken 72XX
BK7231T = CHIP_TYPE(F_BK7231U, 0x1A), // *SCTRL_CHIP_ID = 0x7231a
BK7231N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
BL2028N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
BK7252 = CHIP_TYPE(F_BK7251, 0x00), // TODO
};

View File

@@ -8,7 +8,14 @@ String ipToString(const IPAddress &ip) {
return String(szRet);
}
static void lt_random_bytes(uint8_t *buf, size_t len) {
/**
* @brief Generate random bytes using rand().
*
* @param buf destination pointer
* @param len how many bytes to generate
*/
extern "C" {
void lt_rand_bytes(uint8_t *buf, size_t len) {
int *data = (int *)buf;
size_t i;
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
@@ -20,3 +27,39 @@ static void lt_random_bytes(uint8_t *buf, size_t len) {
memcpy(buf + i * sizeof(int), pRem, len);
}
}
#undef putchar
/**
* @brief Print data pointed to by buf in hexdump-like format (hex+ASCII).
*
* @param buf source pointer
* @param len how many bytes to print
* @param offset increment printed offset by this value
* @param width how many bytes on a line
*/
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
uint16_t pos = 0;
while (pos < len) {
// print hex offset
printf("%06x ", offset + pos);
// calculate current line width
uint8_t lineWidth = min(width, len - pos);
// print hexadecimal representation
for (uint8_t i = 0; i < lineWidth; i++) {
if (i % 8 == 0) {
printf(" ");
}
printf("%02x ", buf[pos + i]);
}
// print ascii representation
printf(" |");
for (uint8_t i = 0; i < lineWidth; i++) {
char c = buf[pos + i];
putchar((c >= 0x20 && c <= 0x7f) ? c : '.');
}
puts("|\r");
pos += lineWidth;
}
}
}

View File

@@ -2,6 +2,28 @@
#pragma once
// C standard libraries
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// C++ standard libraries
#ifdef __cplusplus
#include <algorithm>
#include <cmath>
using ::round;
using std::abs;
using std::isinf;
using std::isnan;
using std::max;
using std::min;
#endif
// LibreTuya version macros
#ifndef LT_VERSION
#define LT_VERSION 1.0.0
@@ -15,7 +37,10 @@
#define LT_BOARD_STR STRINGIFY_MACRO(LT_BOARD)
// Includes
#include "LibreTuyaConfig.h"
#include "LibreTuyaClass.h" // global LT class
#include "LibreTuyaCompat.h" // compatibility methods
#include "LibreTuyaConfig.h" // configuration macros
#include "LibreTuyaCustom.h" // family-defined methods (Wiring custom)
#include <Arduino.h>
// C includes
@@ -39,37 +64,17 @@ extern "C" {
"LibreTuya v" LT_VERSION_STR " on " LT_BOARD_STR ", compiled at " __DATE__ " " __TIME__ \
)
// ArduinoCore-API doesn't define these anymore
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define PGM_VOID_P const void *
// C functions
void lt_rand_bytes(uint8_t *buf, size_t len);
// C++ only functions
#ifdef __cplusplus
String ipToString(const IPAddress &ip);
#endif
// Main class
#ifdef __cplusplus
/**
* @brief Main LibreTuya API class.
*
* This class contains all functions common amongst all platforms.
* Implementations of these methods may vary between platforms.
*
* The class is accessible using the `LT` global object (defined by the platform).
*/
class LibreTuya {
/* Common methods*/
public:
/* Platform-defined methods */
public:
};
extern LibreTuya LT;
extern "C" {
void lt_rand_bytes(uint8_t *buf, size_t len);
void hexdump(const uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
}
#else
void lt_rand_bytes(uint8_t *buf, size_t len);
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
#endif

View File

@@ -0,0 +1,148 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
#include "LibreTuyaClass.h"
/**
* @brief Get LibreTuya version string.
*/
const char *LibreTuya::getVersion() {
return LT_VERSION_STR;
}
/**
* @brief Get board name.
*/
const char *LibreTuya::getBoard() {
return LT_BOARD_STR;
}
/**
* @brief Get CPU family ID.
*/
ChipFamily LibreTuya::getChipFamily() {
return FAMILY;
}
/**
* @brief Get CPU family name as string.
*/
const char *LibreTuya::getChipFamilyName() {
return STRINGIFY_MACRO(FAMILY) + 2;
}
static char *deviceName = NULL;
/**
* @brief Get device friendly name in format "LT-<board>-<chip id>".
* Can be used as hostname.
*/
const char *LibreTuya::getDeviceName() {
if (deviceName)
return deviceName;
uint32_t chipId = getChipId();
uint8_t *id = (uint8_t *)&chipId;
const char *board = getBoard();
uint8_t boardLen = strlen(board);
deviceName = (char *)malloc(3 + boardLen + 1 + 6 + 1);
sprintf(deviceName, "LT-%s-%02x%02x%02x", board, id[0], id[1], id[2]);
return deviceName;
}
/**
* @brief Get a textual representation of a reset reason.
*
* @param reason value to convert to text, uses getResetReason() by default
*/
const char *LibreTuya::getResetReasonName(ResetReason reason) {
if (reason >= RESET_REASON_MAX)
reason = getResetReason();
switch (reason) {
case RESET_REASON_POWER:
return "Power-On";
case RESET_REASON_BROWNOUT:
return "Brownout";
case RESET_REASON_HARDWARE:
return "HW Reboot";
case RESET_REASON_SOFTWARE:
return "SW Reboot";
case RESET_REASON_WATCHDOG:
return "WDT Reset";
case RESET_REASON_CRASH:
return "Crash";
case RESET_REASON_SLEEP:
return "Sleep Wakeup";
}
return "Unknown";
}
/**
* @brief Get CPU frequency in MHz.
*/
uint32_t LibreTuya::getCpuFreqMHz() {
return getCpuFreq() / 1000000;
}
/**
* @brief Get flash chip total size.
* The default implementation uses the least significant
* byte of the chip ID to determine the size.
*/
__attribute__((weak)) uint32_t LibreTuya::getFlashChipSize() {
FlashId id = getFlashChipId();
if (id.chipSizeId >= 0x14 && id.chipSizeId <= 0x19) {
return (1 << id.chipSizeId);
}
#ifdef FLASH_LENGTH
return FLASH_LENGTH;
#else
return 0;
#endif
}
/**
* @brief Get the OTA index for updated firmware.
*
* Note: returns 1 for chips without dual-OTA.
*/
uint8_t LibreTuya::otaGetTarget() {
if (!otaSupportsDual())
return 1;
return otaGetRunning() ^ 0b11;
}
/**
* @brief Perform OTA rollback: switch to the previous image, or abort current
* switched OTA update, if not rebooted yet.
*
* @return false if no second image to run, writing failed or dual-OTA not supported
*/
bool LibreTuya::otaRollback() {
if (!otaCanRollback())
return false;
if (otaGetRunning() != otaGetStoredIndex())
// force switching back to current image
return otaSwitch(true);
return true;
}
/**
* @brief Check if OTA rollback is supported and available (there is another image to run).
* @return false if no second image to run or dual-OTA not supported
*/
bool LibreTuya::otaCanRollback() {
if (!otaSupportsDual())
return false;
if (otaGetRunning() == otaGetStoredIndex())
return true;
if (otaGetRunning() == 1 && otaHasImage1())
return true;
if (otaGetRunning() == 2 && otaHasImage2())
return true;
return false;
}
__attribute__((weak)) void LibreTuya::gpioRecover() {
// nop by default
}

View File

@@ -0,0 +1,193 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
#pragma once
#ifdef __cplusplus
#include "LibreTuyaAPI.h"
#include <core/ChipType.h>
typedef enum {
RESET_REASON_UNKNOWN = 0,
RESET_REASON_POWER = 1,
RESET_REASON_BROWNOUT = 2,
RESET_REASON_HARDWARE = 3,
RESET_REASON_SOFTWARE = 4,
RESET_REASON_WATCHDOG = 5,
RESET_REASON_CRASH = 6,
RESET_REASON_SLEEP = 7,
RESET_REASON_MAX = 8,
} ResetReason;
/**
* @brief Flash chip ID structure.
*/
typedef struct {
uint8_t manufacturerId;
uint8_t chipId;
uint8_t chipSizeId;
} FlashId;
/**
* @brief Main LibreTuya API class.
*
* This class contains all functions common amongst all families.
* Implementations of these methods may vary between families.
*
* The class is accessible using the `LT` global object (defined by the family).
*/
class LibreTuya {
public: /* Common methods - note: these are documented in LibreTuyaAPI.cpp */
const char *getVersion();
const char *getBoard();
ChipFamily getChipFamily();
const char *getChipFamilyName();
const char *getDeviceName();
const char *getResetReasonName(ResetReason reason = RESET_REASON_MAX);
uint32_t getCpuFreqMHz();
uint32_t getFlashChipSize();
uint8_t otaGetTarget();
bool otaRollback();
bool otaCanRollback();
public: /* Compatibility methods */
/**
* @brief Alias of getMaxAllocHeap().
*/
inline uint32_t getMaxFreeBlockSize() {
return getMaxAllocHeap();
}
public: /* Family-defined methods */
/**
* @brief Reboot the CPU.
*/
void restart();
/**
* @brief Reboot the CPU and stay in download mode (if possible).
*/
void restartDownloadMode();
/**
* @brief Get the reason of last chip reset.
*/
ResetReason getResetReason();
/**
* @brief Reconfigure GPIO pins used for debugging
* (SWD/JTAG), so that they can be used as normal I/O.
*/
void gpioRecover();
public: /* CPU-related */
/**
* @brief Get CPU model ID.
*/
ChipType getChipType();
/**
* @brief Get CPU model name as string.
*/
const char *getChipModel();
/**
* @brief Get CPU unique ID. This may be based on MAC, eFuse, etc.
* Note: the number should be 24-bit (with most significant byte being zero).
*/
uint32_t getChipId();
/**
* @brief Get CPU core count.
*/
uint8_t getChipCores();
/**
* @brief Get CPU core type name as string.
*/
const char *getChipCoreType();
/**
* @brief Get CPU frequency in Hz.
*/
uint32_t getCpuFreq();
/**
* @brief Get CPU cycle count.
*/
uint32_t getCycleCount();
public: /* Flash memory utilities */
/**
* @brief Read flash chip ID and return a FlashId struct.
*/
FlashId getFlashChipId();
public: /* Memory management */
/**
* @brief Get total RAM size.
*/
uint32_t getRamSize();
/**
* @brief Get total heap size.
*/
uint32_t getHeapSize();
/**
* @brief Get free heap size.
*/
uint32_t getFreeHeap();
/**
* @brief Get lowest level of free heap memory.
*/
uint32_t getMinFreeHeap();
/**
* @brief Get largest block of heap that can be allocated at once.
*/
uint32_t getMaxAllocHeap();
public: /* OTA-related */
/**
* @brief Get the currently running firmware OTA index.
*/
uint8_t otaGetRunning();
/**
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
*/
uint8_t otaGetStoredIndex();
/**
* @brief Check if the chip supports dual-OTA (i.e. OTA is flashed to a different partition).
*
* TODO: make this work for actual dual-OTA chips; remove checking this in otaGetTarget() etc.
*/
bool otaSupportsDual();
/**
* @brief Check if OTA1 image is valid.
*/
bool otaHasImage1();
/**
* @brief Check if OTA2 image is valid.
*/
bool otaHasImage2();
/**
* @brief Try to switch OTA index to the other image.
*
* Note: should return true for chips without dual-OTA. Should return false if one of two images is not valid.
*
* @param force switch even if other image already marked as active
* @return false if writing failed; true otherwise
*/
bool otaSwitch(bool force = false);
public: /* Watchdog */
/**
* @brief Enable the hardware watchdog.
*
* @param timeout watchdog timeout, milliseconds (defaults to 10s)
* @return whether the chip has a hardware watchdog
*/
bool wdtEnable(uint32_t timeout = 10000);
/**
* @brief Disable the hardware watchdog.
*/
void wdtDisable();
/**
* @brief Feed/reset the hardware watchdog timer.
*/
void wdtFeed();
};
extern LibreTuya LT;
extern LibreTuya ESP;
#endif

View File

@@ -0,0 +1,33 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-04. */
#include "LibreTuyaCompat.h"
#if LT_HAS_FREERTOS
BaseType_t xTaskCreateUniversal(
TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pxCreatedTask,
const BaseType_t xCoreID
) {
// #ifndef CONFIG_FREERTOS_UNICORE
// if (xCoreID >= 0 && xCoreID < 2) {
// return xTaskCreatePinnedToCore(
// pxTaskCode,
// pcName,
// usStackDepth,
// pvParameters,
// uxPriority,
// pxCreatedTask,
// xCoreID
// );
// } else {
// #endif
return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
// #ifndef CONFIG_FREERTOS_UNICORE
// }
// #endif
}
#endif

View File

@@ -0,0 +1,42 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-04. */
#pragma once
#include <Arduino.h>
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
// Definitions for error constants.
#define esp_err_t int
#define ESP_OK 0 /*!< esp_err_t value indicating success (no error) */
#define ESP_FAIL -1 /*!< Generic esp_err_t code indicating failure */
// ArduinoCore-API doesn't define these anymore
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define PGM_VOID_P const void *
#define vsnprintf_P vsnprintf
#define OUTPUT_OPEN_DRAIN OUTPUT_OPENDRAIN
#define attachInterruptArg attachInterruptParam
#define voidFuncPtrArg voidFuncPtrParam
// FreeRTOS utilities
#if LT_HAS_FREERTOS
// if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore
// allows to easily handle all possible situations without repetitive code
BaseType_t xTaskCreateUniversal(
TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pxCreatedTask,
const BaseType_t xCoreID
);
#define xTaskCreatePinnedToCore xTaskCreateUniversal
#endif
// Default values from sdkconfig.h
#define CONFIG_LWIP_MAX_ACTIVE_TCP 16

View File

@@ -12,6 +12,7 @@
#define LT_LEVEL_WARN 3
#define LT_LEVEL_ERROR 4
#define LT_LEVEL_FATAL 5
#define LT_LEVEL_NONE 6
// Logger enabled/disabled
#ifndef LT_LOGGER
@@ -24,11 +25,11 @@
#endif
#ifndef LT_LOGGER_CALLER
#define LT_LOGGER_CALLER 1
#define LT_LOGGER_CALLER 0
#endif
#ifndef LT_LOGGER_TASK
#define LT_LOGGER_TASK 1
#define LT_LOGGER_TASK 0
#endif
#ifndef LT_LOGGER_COLOR
@@ -44,32 +45,88 @@
#define LT_LOGLEVEL LT_LEVEL_INFO
#endif
#if !LT_LOGGER
#undef LT_LOGLEVEL
#define LT_LOGLEVEL LT_LEVEL_NONE
#endif
// Free heap size debugging
#ifndef LT_LOG_HEAP
#define LT_LOG_HEAP 0
#endif
// Per-module debugging
// Debug errno values using LT_ERRNO()
#ifndef LT_LOG_ERRNO
#define LT_LOG_ERRNO 0
#endif
// Serial output options
#ifndef LT_UART_SILENT_ENABLED
#define LT_UART_SILENT_ENABLED 1
#endif
#ifndef LT_UART_SILENT_ALL
#define LT_UART_SILENT_ALL 0
#endif
#ifndef LT_UART_DEFAULT_LOGGER
#define LT_UART_DEFAULT_LOGGER LT_UART_DEFAULT_PORT
#endif
#ifndef LT_UART_DEFAULT_SERIAL
#define LT_UART_DEFAULT_SERIAL LT_UART_DEFAULT_PORT
#endif
// Misc options
#ifndef LT_USE_TIME
#define LT_USE_TIME 0
#endif
#ifndef LT_MICROS_HIGH_RES // NOTE: this is also defined in fixups/clock_rtos.c
#define LT_MICROS_HIGH_RES 1
#endif
#ifndef LT_AUTO_DOWNLOAD_REBOOT
#define LT_AUTO_DOWNLOAD_REBOOT 1
#endif
// Per-module logging output - applies to all loglevels
#ifndef LT_DEBUG_ALL
#define LT_DEBUG_ALL 0
#endif
#ifndef LT_DEBUG_WIFI
#define LT_DEBUG_WIFI 0
#define LT_DEBUG_WIFI 1
#endif
#ifndef LT_DEBUG_WIFI_CLIENT
#define LT_DEBUG_WIFI_CLIENT 0
#ifndef LT_DEBUG_CLIENT
#define LT_DEBUG_CLIENT LT_DEBUG_ALL
#endif
#ifndef LT_DEBUG_WIFI_SERVER
#define LT_DEBUG_WIFI_SERVER 0
#endif
#ifndef LT_DEBUG_WIFI_STA
#define LT_DEBUG_WIFI_STA 0
#endif
#ifndef LT_DEBUG_WIFI_AP
#define LT_DEBUG_WIFI_AP 0
#ifndef LT_DEBUG_SERVER
#define LT_DEBUG_SERVER LT_DEBUG_ALL
#endif
#ifndef LT_DEBUG_SSL
#define LT_DEBUG_SSL 0
#define LT_DEBUG_SSL LT_DEBUG_ALL
#endif
#ifndef LT_DEBUG_OTA
#define LT_DEBUG_OTA 1
#endif
#ifndef LT_DEBUG_FDB
#define LT_DEBUG_FDB 0
#endif
#ifndef LT_DEBUG_MDNS
#define LT_DEBUG_MDNS LT_DEBUG_ALL
#endif
#ifndef LT_DEBUG_LWIP
#define LT_DEBUG_LWIP 0
#endif
#ifndef LT_DEBUG_LWIP_ASSERT
#define LT_DEBUG_LWIP_ASSERT 0
#endif

View File

@@ -0,0 +1,103 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include "LibreTuyaCustom.h"
int _analogReadResolution = 10; // 0-1023
int _analogWriteResolution = 8; // 0-255
int _analogWritePeriod = 20000; // 50 Hz
/**
* @brief Check if pin is invalid (too low or too high).
*/
bool pinInvalid(pin_size_t pinNumber) {
#ifdef PINS_COUNT
return pinNumber < 0 || pinNumber >= PINS_COUNT;
#else
return false;
#endif
}
/**
* @brief Get PinInfo struct for the specified number.
* Returns NULL if pin number is invalid.
*/
PinInfo *pinInfo(pin_size_t pinNumber) {
if (pinInvalid(pinNumber))
return NULL;
return &(pinTable[pinNumber]);
}
/**
* @brief Check if pin supports all features represented by 'mask'.
*/
bool pinSupported(PinInfo *pin, uint32_t mask) {
return (pin->supported & mask) == mask;
}
/**
* @brief Check if pin has all features represented by 'mask' enabled.
*/
bool pinEnabled(PinInfo *pin, uint32_t mask) {
return (pin->enabled & mask) == mask;
}
/**
* @brief Check if GPIO pin is configured as output.
*/
bool pinIsOutput(PinInfo *pin) {
return pin->mode == OUTPUT || pin->mode == OUTPUT_OPENDRAIN;
}
/**
* @brief Check if GPIO pin is configured as output.
*/
bool pinIsInput(PinInfo *pin) {
return pin->mode == INPUT || pin->mode == INPUT_PULLUP || pin->mode == INPUT_PULLDOWN;
}
/**
* @brief Read voltage from ADC and return a value between 0 and
* the current reading resolution.
*/
int analogRead(pin_size_t pinNumber) {
float voltage = analogReadVoltage(pinNumber);
float maxVoltage = analogReadMaxVoltage(pinNumber);
uint16_t ret = round((1 << _analogReadResolution) * voltage / maxVoltage);
if (ret >= (1 << _analogReadResolution))
ret = (1 << _analogReadResolution) - 1;
return ret;
}
/**
* @brief Set resolution of values (in bits) returned by analogRead().
* Defaults to 10 bit (0-1023).
*/
void analogReadResolution(int res) {
_analogReadResolution = res;
}
/**
* @brief Set resolution of values (in bits) expected by analogWrite().
* Defaults to 8 bit (0-255).
*/
void analogWriteResolution(int res) {
_analogWriteResolution = res;
}
/**
* @brief Set PWM output frequency (in Hz).
* Defaults to 50 Hz (20,000 uS).
*/
void analogWriteFrequency(int hz) {
_analogWritePeriod = 1E6 / hz;
}
/**
* @brief Set PWM output frequency (cycle period) in microseconds.
* Defaults to 20,000 uS (50 Hz).
*/
void analogWritePeriod(int us) {
_analogWritePeriod = us;
}
__attribute__((weak)) void analogReference(uint8_t mode) {}

View File

@@ -0,0 +1,96 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
#pragma once
#include "LibreTuyaAPI.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Run mainTask & start OS kernel (family-defined).
* Return false if an error occured; else do not return and
* and keep the OS kernel running.
*/
extern bool startMainTask();
/**
* @brief Main setup() and loop() task.
* Not to be called directly.
*/
extern void mainTask(const void *arg);
/**
* @brief Run periodic tasks, like printing free heap or checking millis() overflow.
*
* This is called during delaying operations, like yield() or delay().
*/
extern void runPeriodicTasks();
#define PIN_NONE (1 << 0)
#define PIN_GPIO (1 << 1)
#define PIN_IRQ (1 << 2)
#define PIN_PWM (1 << 3)
#define PIN_ADC (1 << 4)
#define PIN_DAC (1 << 5)
#define PIN_I2C (1 << 6)
#define PIN_I2S (1 << 7)
#define PIN_JTAG (1 << 8)
#define PIN_SPI (1 << 9)
#define PIN_SWD (1 << 10)
#define PIN_UART (1 << 11)
typedef struct {
/**
* @brief GPIO name in the family SDK.
*/
uint32_t gpio;
/**
* @brief Supported pin functions.
*/
uint32_t supported;
/**
* @brief Enabled pin functions. Used values are family-specific.
*/
uint32_t enabled;
/**
* @brief Pin mode (direction, IRQ level, etc.).
*/
uint32_t mode;
} PinInfo;
extern PinInfo pinTable[];
// Custom Wiring methods
bool pinInvalid(pin_size_t pinNumber);
PinInfo *pinInfo(pin_size_t pinNumber);
bool pinSupported(PinInfo *pin, uint32_t mask);
bool pinEnabled(PinInfo *pin, uint32_t mask);
bool pinIsOutput(PinInfo *pin);
bool pinIsInput(PinInfo *pin);
int analogRead(pin_size_t pinNumber);
void analogReadResolution(int res);
void analogWriteResolution(int res);
void analogWriteFrequency(int hz);
void analogWritePeriod(int us);
extern int _analogReadResolution;
extern int _analogWriteResolution;
extern int _analogWritePeriod;
/**
* @brief Read voltage from analog input (in millivolts).
*/
uint16_t analogReadVoltage(pin_size_t pinNumber);
/**
* @brief Get max reading voltage for the specified pin (millivolts).
*/
uint16_t analogReadMaxVoltage(pin_size_t pinNumber);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,25 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-04. */
#pragma once
#include <Arduino.h>
#ifdef HAS_SERIAL_CLASS // failsafe for circular inclusion
#ifdef PIN_SERIAL0_TX
extern SerialClass Serial0;
#endif
#ifdef PIN_SERIAL1_TX
extern SerialClass Serial1;
#endif
#ifdef PIN_SERIAL2_TX
extern SerialClass Serial2;
#endif
#define SerialN(x) Serial##x
#define SerialM(x) SerialN(x)
#define Serial SerialM(LT_UART_DEFAULT_SERIAL)
#endif

View File

@@ -3,8 +3,9 @@
#include "lt_logger.h"
#include <Arduino.h>
#include <printf/printf.h>
#if LT_LOGGER_TASK
#if LT_LOGGER_TASK && LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
@@ -27,10 +28,11 @@
#define COLOR_BRIGHT_CYAN 0x16
#define COLOR_BRIGHT_WHITE 0x17
const char levels[] = {'V', 'D', 'I', 'W', 'E', 'F'};
static uint32_t uart_port = LT_UART_DEFAULT_LOGGER;
static const char levels[] = {'V', 'D', 'I', 'W', 'E', 'F'};
#if LT_LOGGER_COLOR
const uint8_t colors[] = {
static const uint8_t colors[] = {
COLOR_BRIGHT_CYAN,
COLOR_BRIGHT_BLUE,
COLOR_BRIGHT_GREEN,
@@ -48,6 +50,9 @@ void lt_log(const uint8_t level, const char *caller, const unsigned short line,
void lt_log(const uint8_t level, const char *format, ...) {
#endif
if (uart_port == 0xFF)
return;
#if LT_LOGGER_TIMESTAMP
float seconds = millis() / 1000.0f;
#if LT_PRINTF_BROKEN
@@ -57,7 +62,7 @@ void lt_log(const uint8_t level, const char *format, ...) {
#endif
#endif
#if LT_LOGGER_TASK
#if LT_LOGGER_TASK && LT_HAS_FREERTOS
char task_colon = ':';
TaskHandle_t task = xTaskGetCurrentTaskHandle();
char *task_name = pcTaskGetTaskName(task);
@@ -72,7 +77,10 @@ void lt_log(const uint8_t level, const char *format, ...) {
char c_value = '0' + (colors[level] & 0x7);
#endif
printf(
fctprintf(
(void (*)(char, void *))putchar_p,
(void *)uart_port,
// format:
#if LT_LOGGER_COLOR
"\e[%c;3%cm"
#endif
@@ -90,16 +98,16 @@ void lt_log(const uint8_t level, const char *format, ...) {
#if LT_LOGGER_CALLER
"%s():%hu: "
#endif
#if LT_LOGGER_TASK
#if LT_LOGGER_TASK && LT_HAS_FREERTOS
"%s%c "
#endif
,
levels[level]
// arguments:
#if LT_LOGGER_COLOR
,
c_bright, // whether text is bright
c_value // text color
c_value, // text color
#endif
levels[level]
#if LT_LOGGER_TIMESTAMP
,
seconds // float
@@ -113,7 +121,7 @@ void lt_log(const uint8_t level, const char *format, ...) {
caller,
line
#endif
#if LT_LOGGER_TASK
#if LT_LOGGER_TASK && LT_HAS_FREERTOS
,
task_name,
task_colon // printing outside of tasks
@@ -122,7 +130,16 @@ void lt_log(const uint8_t level, const char *format, ...) {
va_list va_args;
va_start(va_args, format);
vprintf(format, va_args);
vfctprintf((void (*)(char, void *))putchar_p, (void *)uart_port, format, va_args);
va_end(va_args);
printf("\r\n");
putchar_p('\r', uart_port);
putchar_p('\n', uart_port);
}
void lt_log_set_port(uint8_t port) {
uart_port = port;
}
void lt_log_disable() {
uart_port = 0xFF;
}

View File

@@ -7,48 +7,87 @@
#if LT_LOGGER_CALLER
#define LT_LOG(level, caller, line, ...) lt_log(level, caller, line, __VA_ARGS__)
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...);
#define LT_LOGM(level, module, caller, line, ...) \
do { \
if (LT_DEBUG_##module) { \
lt_log(level, caller, line, #module ": " __VA_ARGS__); \
} \
} while (0)
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...)
__attribute__((format(printf, 4, 5)));
#else
#define LT_LOG(level, caller, line, ...) lt_log(level, __VA_ARGS__)
void lt_log(const uint8_t level, const char *format, ...);
#define LT_LOGM(level, module, caller, line, ...) \
do { \
if (LT_DEBUG_##module) { \
lt_log(level, #module ": " __VA_ARGS__); \
} \
} while (0)
void lt_log(const uint8_t level, const char *format, ...) __attribute__((format(printf, 2, 3)));
#endif
/**
* @brief Change log output port.
*
* @param port UART port index - can be 0, 1 or 2
*/
void lt_log_set_port(uint8_t port);
/**
* @brief Disable LT logger. Enable it back using lt_log_set_port(LT_UART_DEFAULT_LOGGER).
*/
void lt_log_disable();
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_TM(module, ...) LT_LOGM(LT_LEVEL_TRACE, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_VM(module, ...) LT_LOGM(LT_LEVEL_TRACE, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_T(...)
#define LT_V(...)
#define LT_TM(...)
#define LT_VM(...)
#endif
#if LT_LEVEL_DEBUG >= LT_LOGLEVEL
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_DM(module, ...) LT_LOGM(LT_LEVEL_DEBUG, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_D(...)
#define LT_DM(...)
#endif
#if LT_LEVEL_INFO >= LT_LOGLEVEL
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_IM(module, ...) LT_LOGM(LT_LEVEL_INFO, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_I(...)
#define LT_IM(...)
#endif
#if LT_LEVEL_WARN >= LT_LOGLEVEL
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_WM(module, ...) LT_LOGM(LT_LEVEL_WARN, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_W(...)
#define LT_WM(...)
#endif
#if LT_LEVEL_ERROR >= LT_LOGLEVEL
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_EM(module, ...) LT_LOGM(LT_LEVEL_ERROR, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_E(...)
#define LT_EM(...)
#endif
#if LT_LEVEL_FATAL >= LT_LOGLEVEL
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_FM(module, ...) LT_LOGM(LT_LEVEL_FATAL, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_F(...)
#define LT_FM(...)
#endif
#if LT_LOG_HEAP
@@ -81,20 +120,8 @@ void lt_log(const uint8_t level, const char *format, ...);
#define ESP_EARLY_LOGI(...) LT_I(__VA_ARGS__)
#define ESP_EARLY_LOGW(...) LT_W(__VA_ARGS__)
#define ESP_EARLY_LOGE(...) LT_E(__VA_ARGS__)
#define LT_T_MOD(module, ...) \
do { \
if (module) { \
LT_T(__VA_ARGS__); \
} \
} while (0)
#define LT_D_MOD(module, ...) \
do { \
if (module) { \
LT_D(__VA_ARGS__); \
} \
} while (0)
#define ets_printf(...) LT_I(__VA_ARGS__)
#define ETS_PRINTF(...) LT_I(__VA_ARGS__)
#define LT_RET(ret) \
LT_E("ret=%d", ret); \
@@ -132,32 +159,12 @@ void lt_log(const uint8_t level, const char *format, ...);
return ret; \
}
// WiFi.cpp
#define LT_T_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_V_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
#define LT_D_WG(...) LT_D_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
// WiFiClient.cpp
#define LT_T_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_V_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_D_WC(...) LT_D_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
// WiFiServer.cpp
#define LT_T_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_V_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_D_WS(...) LT_D_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
// WiFiSTA.cpp
#define LT_T_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_V_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_D_WSTA(...) LT_D_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
// WiFiAP.cpp
#define LT_T_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_V_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_D_WAP(...) LT_D_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
// WiFiClientSecure.cpp & implementations
#define LT_T_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_V_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_D_SSL(...) LT_D_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#if LT_LOG_ERRNO
#define LT_ERRNO() \
if (errno) { \
LT_E("errno=%d", errno); \
errno = 0; \
}
#else
#define LT_ERRNO()
#endif

View File

@@ -0,0 +1,74 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
#include <api/HardwareSerial.h>
using namespace arduino;
extern "C" {
#include <fal.h>
fal_partition_t fal_root_part = NULL;
}
// Arduino framework initialization.
// May be redefined by family files.
void initArduino() __attribute__((weak));
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
// Initialize C library
extern "C" void __libc_init_array(void);
void mainTask(const void *arg) {
setup();
for (;;) {
loop();
if (serialEventRun)
serialEventRun();
yield();
}
}
static unsigned long periodicTasks[] = {0, 0};
void runPeriodicTasks() {
#if LT_LOG_HEAP
if (millis() - periodicTasks[0] > 1000) {
LT_HEAP_I();
periodicTasks[0] = millis();
}
#endif
#if LT_USE_TIME
if (millis() - periodicTasks[1] > 10000) {
gettimeofday(NULL, NULL);
periodicTasks[1] = millis();
}
#endif
}
int main(void) {
// print a startup banner
LT_BANNER();
// initialize C library
__libc_init_array();
// inform about the reset reason
LT_I("Reset reason: %u", LT.getResetReason());
// initialize Arduino framework
initArduino();
// optionally initialize per-variant code
initVariant();
// initialize FAL
fal_init();
// provide root partition
fal_root_part = (fal_partition_t)fal_partition_find("root");
// start the main task and OS kernel
if (!startMainTask()) {
LT_F("Couldn't start the main task");
}
while (1) {}
return 0;
}

View File

@@ -0,0 +1,30 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#include "Flash.h"
extern "C" {
#include <fal.h>
}
// Global Flash object.
FlashClass Flash;
FlashId FlashClass::getChipId() {
return LT.getFlashChipId();
}
uint32_t FlashClass::getSize() {
return LT.getFlashChipSize();
}
bool FlashClass::eraseSector(uint32_t offset) {
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
}
bool FlashClass::readBlock(uint32_t offset, uint8_t *data, size_t size) {
return fal_partition_read(fal_root_part, offset, data, size) >= 0;
}
bool FlashClass::writeBlock(uint32_t offset, uint8_t *data, size_t size) {
return fal_partition_write(fal_root_part, offset, data, size) >= 0;
}

View File

@@ -2,20 +2,10 @@
#pragma once
#include "api/Flash.h"
struct flash_s;
typedef struct flash_s flash_t;
class FlashClass : public IFlashClass {
private:
flash_t *flash;
void initialize();
#include <Arduino.h>
class FlashClass {
public:
FlashClass();
~FlashClass();
FlashId getChipId();
uint32_t getSize();

View File

@@ -25,6 +25,8 @@
* Adapted in October 2018
*/
#if LT_ARD_HAS_WIFI
#include <Arduino.h>
#ifdef HTTPCLIENT_1_1_COMPATIBLE
@@ -1623,3 +1625,5 @@ bool HTTPClient::generateCookieString(String *cookieString) {
return found;
}
#endif // LT_ARD_HAS_WIFI

View File

@@ -0,0 +1,37 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
#pragma once
#include <Arduino.h>
#include <MD5Impl.h>
// available built-in implementations
#if LT_MD5_USE_POLARSSL
#include "MD5PolarSSLImpl.h"
#endif
#if LT_MD5_USE_MBEDTLS
#include "MD5MbedTLSImpl.h"
#endif
#if LT_MD5_USE_HOSTAPD
#include "MD5HostapdImpl.h"
#endif
// common API
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LT_MD5_CTX_T
#define LT_MD5_CTX_T void
#endif
// for compatibility with ESP8266
typedef LT_MD5_CTX_T md5_context_t;
void MD5Init(LT_MD5_CTX_T *context);
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len);
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-12. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <crypto/md5_i.h>
#define LT_MD5_CTX_T struct MD5Context
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,28 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
#if LT_ARD_HAS_MD5
#include "MD5.h"
#if LT_MD5_USE_MBEDTLS
extern "C" {
void MD5Init(LT_MD5_CTX_T *context) {
mbedtls_md5_init(context);
mbedtls_md5_starts(context);
}
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len) {
mbedtls_md5_update(context, buf, len);
}
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context) {
mbedtls_md5_finish(context, digest);
}
} // extern "C"
#endif // LT_MD5_USE_MBEDTLS
#endif // LT_ARD_HAS_MD5

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-11. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <mbedtls/md5.h>
#define LT_MD5_CTX_T mbedtls_md5_context
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,28 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
#if LT_ARD_HAS_MD5
#include "MD5.h"
#if LT_MD5_USE_POLARSSL
extern "C" {
void MD5Init(LT_MD5_CTX_T *context) {
md5_init(context);
md5_starts(context);
}
void MD5Update(LT_MD5_CTX_T *context, const unsigned char *buf, unsigned len) {
md5_update(context, buf, len);
}
void MD5Final(unsigned char digest[16], LT_MD5_CTX_T *context) {
md5_finish(context, digest);
}
} // extern "C"
#endif // LT_MD5_USE_POLARSSL
#endif // LT_ARD_HAS_MD5

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <polarssl/md5.h>
#define LT_MD5_CTX_T md5_context
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,7 +1,29 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#include "WiFiClient.h"
#include "WiFiPriv.h"
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
#include "LwIPClient.h"
#define MAX_SOCK_NUM 4
#define WIFI_CLIENT_CONNECT_TIMEOUT 3000
#define WIFI_CLIENT_READ_TIMEOUT 3000
#define WIFI_CLIENT_WRITE_RETRY 10
#define WIFI_CLIENT_SELECT_TIMEOUT 1000
#define WIFI_CLIENT_FLUSH_BUF_SIZE 1024
// disable #defines removing lwip_ prefix
#undef LWIP_COMPAT_SOCKETS
#define LWIP_COMPAT_SOCKETS 0
extern "C" {
#include <lwip/api.h>
#include <lwip/dns.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <sys/time.h>
} // extern "C"
class SocketHandle {
public:
@@ -14,28 +36,28 @@ class SocketHandle {
}
};
WiFiClient::WiFiClient() {
LT_V_WC("WiFiClient()");
LwIPClient::LwIPClient() {
LT_VM(CLIENT, "LwIPClient()");
_connected = false;
_sock = NULL;
_rxBuffer = NULL;
_timeout = WIFI_CLIENT_CONNECT_TIMEOUT;
}
WiFiClient::WiFiClient(int sock) {
LT_V_WC("WiFiClient(%d)", sock);
LwIPClient::LwIPClient(int sock) {
LT_VM(CLIENT, "LwIPClient(%d)", sock);
_connected = true;
_sock = std::make_shared<SocketHandle>(sock);
_rxBuffer = std::make_shared<LwIPRxBuffer>(sock);
_timeout = WIFI_CLIENT_CONNECT_TIMEOUT;
}
WiFiClient::~WiFiClient() {
LT_V_WC("~WiFiClient()");
LwIPClient::~LwIPClient() {
LT_VM(CLIENT, "~LwIPClient()");
stop();
}
WiFiClient &WiFiClient::operator=(const WiFiClient &other) {
LwIPClient &LwIPClient::operator=(const LwIPClient &other) {
stop();
_connected = other._connected;
_sock = other._sock;
@@ -47,24 +69,27 @@ bool IWiFiClient::operator==(const IWiFiClient &other) const {
return fd() == other.fd() && remoteIP() == other.remoteIP() && remotePort() == other.remotePort();
}
int WiFiClient::connect(IPAddress ip, uint16_t port) {
int LwIPClient::connect(IPAddress ip, uint16_t port) {
return connect(ip, port, _timeout);
}
int WiFiClient::connect(const char *host, uint16_t port) {
int LwIPClient::connect(const char *host, uint16_t port) {
return connect(host, port, _timeout);
}
int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout) {
int LwIPClient::connect(const char *host, uint16_t port, int32_t timeout) {
IPAddress ip = WiFi.hostByName(host);
if (!ip)
return 0;
return connect(ip, port, timeout);
}
int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
int LwIPClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
if (_connected)
stop();
int sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
LT_DM(CLIENT, "socket failed");
return -1;
}
@@ -73,6 +98,8 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
lwip_fcntl(sock, F_SETFL, lwip_fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
LT_ERRNO();
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -87,19 +114,19 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
int res = lwip_connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (res < 0 && errno != EINPROGRESS) {
LT_E("Connect failed; errno=%d", errno);
LT_EM(CLIENT, "Connect failed; errno=%d", errno);
lwip_close(sock);
return -1;
}
res = lwip_select(sock + 1, NULL, &fdset, NULL, timeout < 0 ? NULL : &tv);
if (res < 0) {
LT_E("Select failed; errno=%d", errno);
LT_EM(CLIENT, "Select failed; errno=%d", errno);
lwip_close(sock);
return 0;
}
if (res == 0) {
LT_E("Select timeout; errno=%d", errno);
LT_EM(CLIENT, "Select timeout; errno=%d", errno);
lwip_close(sock);
return 0;
}
@@ -109,7 +136,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
res = lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &sockerr, &len);
if (res < 0 || sockerr != 0) {
LT_E("Socket error; res=%d, sockerr=%d", res, sockerr);
LT_EM(CLIENT, "Socket error; res=%d, sockerr=%d", res, sockerr);
lwip_close(sock);
return 0;
}
@@ -120,19 +147,23 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
lwip_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
lwip_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
LT_ERRNO();
lwip_fcntl(sock, F_SETFL, lwip_fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK);
LT_ERRNO();
_connected = true;
_sock = std::make_shared<SocketHandle>(sock);
_rxBuffer = std::make_shared<LwIPRxBuffer>(sock);
return 1;
}
size_t WiFiClient::write(uint8_t data) {
size_t LwIPClient::write(uint8_t data) {
return write(&data, 1);
}
size_t WiFiClient::write(Stream &stream) {
size_t LwIPClient::write(Stream &stream) {
uint8_t *buf = (uint8_t *)malloc(1360);
if (!buf) {
return 0;
@@ -149,7 +180,7 @@ size_t WiFiClient::write(Stream &stream) {
return written;
}
size_t WiFiClient::write(const uint8_t *buf, size_t size) {
size_t LwIPClient::write(const uint8_t *buf, size_t size) {
if (_sock < 0 || !_connected || !size) {
setWriteError();
return 0;
@@ -167,7 +198,7 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) {
retry--;
if (lwip_select(fd() + 1, NULL, &fdset, NULL, &tv) < 0) {
LT_W("Select failed; errno=%d", errno);
LT_WM(CLIENT, "Select failed; errno=%d", errno);
return 0;
}
@@ -183,7 +214,7 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) {
retry = WIFI_CLIENT_WRITE_RETRY;
}
} else if (res < 0 && errno != EAGAIN) {
LT_W("Send failed; errno=%d", errno);
LT_WM(CLIENT, "Send failed; errno=%d", errno);
setWriteError(res);
_connected = false;
retry = 0;
@@ -192,36 +223,38 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) {
}
}
}
LT_DM(CLIENT, "wrote %d bytes", written);
return written;
}
int WiFiClient::available() {
int LwIPClient::available() {
if (!_connected || !_rxBuffer)
return 0;
int res = _rxBuffer->available();
if (_rxBuffer->failed()) {
LT_ERRNO();
stop();
}
return res;
}
int WiFiClient::fd() const {
int LwIPClient::fd() const {
if (!_sock)
return -1;
return _sock->fd;
}
int WiFiClient::socket() {
int LwIPClient::socket() {
return fd();
}
int WiFiClient::setTimeout(uint32_t seconds) {
int LwIPClient::setTimeout(uint32_t seconds) {
Client::setTimeout(seconds * 1000);
lwip_setsockopt(fd(), SOL_SOCKET, SO_RCVTIMEO, &_timeout, sizeof(_timeout));
return lwip_setsockopt(fd(), SOL_SOCKET, SO_SNDTIMEO, &_timeout, sizeof(_timeout));
}
int WiFiClient::read() {
int LwIPClient::read() {
uint8_t data;
int res = read(&data, 1);
if (res < 0)
@@ -231,7 +264,7 @@ int WiFiClient::read() {
return data;
}
int WiFiClient::read(uint8_t *buf, size_t size) {
int LwIPClient::read(uint8_t *buf, size_t size) {
int res = -1;
if (_rxBuffer) {
res = _rxBuffer->read(buf, size);
@@ -242,7 +275,7 @@ int WiFiClient::read(uint8_t *buf, size_t size) {
return res;
}
int WiFiClient::peek() {
int LwIPClient::peek() {
int res = -1;
if (_rxBuffer) {
res = _rxBuffer->peek();
@@ -253,7 +286,7 @@ int WiFiClient::peek() {
return res;
}
void WiFiClient::flush() {
void LwIPClient::flush() {
int res;
size_t len = available();
if (!len)
@@ -272,14 +305,14 @@ void WiFiClient::flush() {
free(buf);
}
void WiFiClient::stop() {
LT_V_WC("stop()");
void LwIPClient::stop() {
LT_VM(CLIENT, "Stopping TCP");
_connected = false;
_sock = NULL;
_rxBuffer = NULL;
}
uint8_t WiFiClient::connected() {
uint8_t LwIPClient::connected() {
if (_connected) {
uint8_t dummy;
if (lwip_recv(fd(), &dummy, 0, MSG_DONTWAIT) <= 0) {
@@ -294,11 +327,11 @@ uint8_t WiFiClient::connected() {
case ECONNRESET:
case ECONNREFUSED:
case ECONNABORTED:
LT_W("Connection closed; errno=%d", errno);
LT_IM(CLIENT, "Connection closed; errno=%d", errno);
_connected = false;
break;
default:
LT_I("Connection status unknown; errno=%d", errno);
LT_WM(CLIENT, "Connection status unknown; errno=%d", errno);
_connected = true;
break;
}
@@ -323,34 +356,36 @@ uint16_t __attribute__((noinline)) getport(int sock, int (*func)(int, struct soc
return ntohs(s->sin_port);
}
IPAddress WiFiClient::remoteIP() const {
IPAddress LwIPClient::remoteIP() const {
return getaddr(fd(), lwip_getpeername);
}
IPAddress WiFiClient::remoteIP(int fd) const {
IPAddress LwIPClient::remoteIP(int fd) const {
return getaddr(fd, lwip_getpeername);
}
uint16_t WiFiClient::remotePort() const {
uint16_t LwIPClient::remotePort() const {
return getport(fd(), lwip_getpeername);
}
uint16_t WiFiClient::remotePort(int fd) const {
uint16_t LwIPClient::remotePort(int fd) const {
return getport(fd, lwip_getpeername);
}
IPAddress WiFiClient::localIP() const {
IPAddress LwIPClient::localIP() const {
return getaddr(fd(), lwip_getsockname);
}
IPAddress WiFiClient::localIP(int fd) const {
IPAddress LwIPClient::localIP(int fd) const {
return getaddr(fd, lwip_getsockname);
}
uint16_t WiFiClient::localPort() const {
uint16_t LwIPClient::localPort() const {
return getport(fd(), lwip_getsockname);
}
uint16_t WiFiClient::localPort(int fd) const {
uint16_t LwIPClient::localPort(int fd) const {
return getport(fd, lwip_getsockname);
}
#endif

View File

@@ -0,0 +1,56 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <api/WiFiClient.h>
#include <lwip/LwIPRxBuffer.h>
#include <memory>
class SocketHandle;
class LwIPClient : public IWiFiClient {
private:
bool _connected;
std::shared_ptr<SocketHandle> _sock;
std::shared_ptr<LwIPRxBuffer> _rxBuffer;
public:
LwIPClient();
LwIPClient(int sock);
~LwIPClient();
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
int connect(IPAddress ip, uint16_t port, int32_t timeout);
int connect(const char *host, uint16_t port, int32_t timeout);
size_t write(uint8_t data);
size_t write(const uint8_t *buf, size_t size);
size_t write(Stream &stream);
int available();
int fd() const;
int socket();
int setTimeout(uint32_t seconds);
int read();
int read(uint8_t *buf, size_t size);
int peek();
void flush();
void stop();
uint8_t connected();
LwIPClient &operator=(const LwIPClient &other);
IPAddress remoteIP() const;
IPAddress remoteIP(int sock) const;
uint16_t remotePort() const;
uint16_t remotePort(int sock) const;
IPAddress localIP() const;
IPAddress localIP(int sock) const;
uint16_t localPort() const;
uint16_t localPort(int sock) const;
using Print::write;
};

View File

@@ -1,26 +1,26 @@
#ifdef LT_HAS_LWIP
#if LT_HAS_LWIP
#include "LwIPRxBuffer.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// disable #defines removing lwip_ prefix
#undef LWIP_COMPAT_SOCKETS
#define LWIP_COMPAT_SOCKETS 0
extern "C" {
#include <lwip/sockets.h>
#ifdef __cplusplus
} // extern "C"
#endif
size_t LwIPRxBuffer::r_available() {
if (_sock < 0) {
LT_DM(CLIENT, "_sock < 0");
return 0;
}
uint16_t count = 0;
int res = lwip_ioctl(_sock, FIONREAD, &count);
int count = 0; // must be of same size as in lwip_ioctl()
int res = lwip_ioctl(_sock, FIONREAD, &count);
if (res < 0) {
LT_DM(CLIENT, "lwip_ioctl()=%d, errno=%d", res, errno);
_failed = true;
return 0;
}
@@ -31,7 +31,7 @@ size_t LwIPRxBuffer::fillBuffer() {
if (!_buffer) {
_buffer = (uint8_t *)malloc(_size);
if (!_buffer) {
printf("[e] Not enough memory to allocate buffer\r\n");
LT_E("buffer alloc failed");
_failed = true;
return 0;
}
@@ -46,6 +46,7 @@ size_t LwIPRxBuffer::fillBuffer() {
int res = lwip_recv(_sock, _buffer + _fill, _size - _fill, MSG_DONTWAIT);
if (res < 0) {
if (errno != EWOULDBLOCK) {
LT_ERRNO();
_failed = true;
}
return 0;

View File

@@ -1,17 +1,30 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#include "WiFiServer.h"
#include "WiFiPriv.h"
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
WiFiServer::WiFiServer(uint32_t addr, uint16_t port, uint8_t maxClients)
#include "LwIPServer.h"
// disable #defines removing lwip_ prefix
#undef LWIP_COMPAT_SOCKETS
#define LWIP_COMPAT_SOCKETS 0
extern "C" {
#include <lwip/api.h>
// #include <lwip/dns.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <sys/time.h>
}
LwIPServer::LwIPServer(uint32_t addr, uint16_t port, uint8_t maxClients)
: _sock(-1), _sockAccepted(-1), _addr(addr), _port(port), _maxClients(maxClients), _active(false), _noDelay(false) {
}
WiFiServer::operator bool() {
LwIPServer::operator bool() {
return _active;
}
bool WiFiServer::begin(uint16_t port, bool reuseAddr) {
bool LwIPServer::begin(uint16_t port, bool reuseAddr) {
if (_active)
return true;
if (port)
@@ -19,7 +32,7 @@ bool WiFiServer::begin(uint16_t port, bool reuseAddr) {
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sock < 0) {
LT_E("Socket failed; errno=%d", errno);
LT_EM(SERVER, "Socket failed; errno=%d", errno);
return false;
}
@@ -32,17 +45,17 @@ bool WiFiServer::begin(uint16_t port, bool reuseAddr) {
addr.sin_port = htons(_port);
if (lwip_bind(_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
LT_E("Bind failed; errno=%d", errno);
LT_EM(SERVER, "Bind failed; errno=%d", errno);
return false;
}
if (lwip_listen(_sock, _maxClients) < 0) {
LT_E("Bind failed; errno=%d", errno);
LT_EM(SERVER, "Bind failed; errno=%d", errno);
return false;
}
uint8_t *addrB = (uint8_t *)&_addr;
LT_I("Server running on %hhu.%hhu.%hhu.%hhu:%hu", addrB[0], addrB[1], addrB[2], addrB[3], _port);
LT_IM(SERVER, "Server running on %hhu.%hhu.%hhu.%hhu:%hu", addrB[0], addrB[1], addrB[2], addrB[3], _port);
lwip_fcntl(_sock, F_SETFL, O_NONBLOCK);
_active = true;
@@ -50,7 +63,7 @@ bool WiFiServer::begin(uint16_t port, bool reuseAddr) {
_sockAccepted = -1;
}
void WiFiServer::end() {
void LwIPServer::end() {
if (_sock == -1)
return;
lwip_close(_sock);
@@ -58,7 +71,7 @@ void WiFiServer::end() {
_active = -1;
}
WiFiClient WiFiServer::accept() {
WiFiClient LwIPServer::accept() {
if (!_active)
return WiFiClient();
@@ -86,7 +99,7 @@ WiFiClient WiFiServer::accept() {
// and receive data, so LwIP still sees a connected client that sends nothing. At least
// that's what I understand. And any loop that doesn't call delay() seems to block the TCP
// stack completely and prevents it from even being pinged.
LT_D_WS("Got client");
LT_DM(SERVER, "Got client");
delay(5);
return WiFiClient(sock);
}
@@ -96,7 +109,7 @@ WiFiClient WiFiServer::accept() {
return WiFiClient();
}
int WiFiServer::setTimeout(uint32_t seconds) {
int LwIPServer::setTimeout(uint32_t seconds) {
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
@@ -105,15 +118,15 @@ int WiFiServer::setTimeout(uint32_t seconds) {
return lwip_setsockopt(_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
void WiFiServer::setNoDelay(bool noDelay) {
void LwIPServer::setNoDelay(bool noDelay) {
_noDelay = noDelay;
}
bool WiFiServer::getNoDelay() {
bool LwIPServer::getNoDelay() {
return _noDelay;
}
bool WiFiServer::hasClient() {
bool LwIPServer::hasClient() {
if (_sockAccepted >= 0) {
return true;
}
@@ -125,3 +138,5 @@ bool WiFiServer::hasClient() {
}
return false;
}
#endif

View File

@@ -0,0 +1,47 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <api/WiFiServer.h>
#include <WiFiClient.h>
class LwIPServer : public IWiFiServer<WiFiClient> {
private:
int _sock;
int _sockAccepted;
uint32_t _addr;
uint16_t _port;
uint8_t _maxClients;
bool _active;
bool _noDelay = false;
private:
LwIPServer(uint32_t addr, uint16_t port = 80, uint8_t maxClients = 4);
public:
LwIPServer(uint16_t port = 80, uint8_t maxClients = 4) : LwIPServer((uint32_t)0, port, maxClients) {}
LwIPServer(int port = 80, uint8_t maxClients = 4) : LwIPServer((uint32_t)0, port, maxClients) {}
LwIPServer(const IPAddress &addr, uint16_t port = 80, uint8_t maxClients = 4)
: LwIPServer((uint32_t)addr, port, maxClients) {}
operator bool();
bool begin(uint16_t port = 0, bool reuseAddr = true);
void end();
WiFiClient accept();
size_t write(const uint8_t *buffer, size_t size) {
return 0;
}
void stopAll() {}
int setTimeout(uint32_t seconds);
void setNoDelay(bool noDelay);
bool getNoDelay();
bool hasClient();
};

View File

@@ -0,0 +1,286 @@
/*
Udp.cpp - UDP class for Raspberry Pi
Copyright (c) 2016 Hristo Gochkov All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if LT_ARD_HAS_WIFI && LT_HAS_LWIP
#include "LwIPUdp.h"
#include <errno.h>
extern "C" {
#include <lwip/netdb.h>
#include <lwip/sockets.h>
} // extern "C"
#undef write
#undef read
LwIPUDP::LwIPUDP() : udp_server(-1), server_port(0), remote_port(0), tx_buffer(0), tx_buffer_len(0), rx_buffer(0) {}
LwIPUDP::~LwIPUDP() {
stop();
}
uint8_t LwIPUDP::begin(IPAddress address, uint16_t port) {
stop();
server_port = port;
tx_buffer = new char[1460];
if (!tx_buffer) {
log_e("could not create tx buffer: %d", errno);
return 0;
}
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
log_e("could not create socket: %d", errno);
return 0;
}
int yes = 1;
if (setsockopt(udp_server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
log_e("could not set socket option: %d", errno);
stop();
return 0;
}
struct sockaddr_in addr;
memset((char *)&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = (in_addr_t)address;
if (bind(udp_server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
log_e("could not bind socket: %d", errno);
stop();
return 0;
}
fcntl(udp_server, F_SETFL, O_NONBLOCK);
return 1;
}
uint8_t LwIPUDP::begin(uint16_t p) {
return begin(IPAddress(INADDR_ANY), p);
}
uint8_t LwIPUDP::beginMulticast(IPAddress a, uint16_t p) {
if (begin(IPAddress(INADDR_ANY), p)) {
if ((uint32_t)a != 0) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = (in_addr_t)a;
mreq.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
log_e("could not join igmp: %d", errno);
stop();
return 0;
}
multicast_ip = a;
}
return 1;
}
return 0;
}
void LwIPUDP::stop() {
if (tx_buffer) {
delete[] tx_buffer;
tx_buffer = NULL;
}
tx_buffer_len = 0;
if (rx_buffer) {
cbuf *b = rx_buffer;
rx_buffer = NULL;
delete b;
}
if (udp_server == -1)
return;
if ((uint32_t)multicast_ip != 0) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
mreq.imr_interface.s_addr = (in_addr_t)0;
setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
multicast_ip = IPAddress(INADDR_ANY);
}
close(udp_server);
udp_server = -1;
}
int LwIPUDP::beginMulticastPacket() {
if (!server_port || multicast_ip == IPAddress(INADDR_ANY))
return 0;
remote_ip = multicast_ip;
remote_port = server_port;
return beginPacket();
}
int LwIPUDP::beginPacket() {
if (!remote_port)
return 0;
// allocate tx_buffer if is necessary
if (!tx_buffer) {
tx_buffer = new char[1460];
if (!tx_buffer) {
log_e("could not create tx buffer: %d", errno);
return 0;
}
}
tx_buffer_len = 0;
// check whereas socket is already open
if (udp_server != -1)
return 1;
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
log_e("could not create socket: %d", errno);
return 0;
}
fcntl(udp_server, F_SETFL, O_NONBLOCK);
return 1;
}
int LwIPUDP::beginPacket(IPAddress ip, uint16_t port) {
remote_ip = ip;
remote_port = port;
return beginPacket();
}
int LwIPUDP::beginPacket(const char *host, uint16_t port) {
struct hostent *server;
server = gethostbyname(host);
if (server == NULL) {
log_e("could not get host from dns: %d", errno);
return 0;
}
return beginPacket(IPAddress((const uint8_t *)(server->h_addr_list[0])), port);
}
int LwIPUDP::endPacket() {
struct sockaddr_in recipient;
recipient.sin_addr.s_addr = (uint32_t)remote_ip;
recipient.sin_family = AF_INET;
recipient.sin_port = htons(remote_port);
int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr *)&recipient, sizeof(recipient));
if (sent < 0) {
log_e("could not send data: %d", errno);
return 0;
}
return 1;
}
size_t LwIPUDP::write(uint8_t data) {
if (tx_buffer_len == 1460) {
endPacket();
tx_buffer_len = 0;
}
tx_buffer[tx_buffer_len++] = data;
return 1;
}
size_t LwIPUDP::write(const uint8_t *buffer, size_t size) {
size_t i;
for (i = 0; i < size; i++)
write(buffer[i]);
return i;
}
int LwIPUDP::parsePacket() {
if (rx_buffer)
return 0;
struct sockaddr_in si_other;
int slen = sizeof(si_other), len;
char *buf = new char[1460];
if (!buf) {
return 0;
}
if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *)&si_other, (socklen_t *)&slen)) == -1) {
delete[] buf;
if (errno == EWOULDBLOCK) {
return 0;
}
log_e("could not receive data: %d", errno);
return 0;
}
remote_ip = IPAddress(si_other.sin_addr.s_addr);
remote_port = ntohs(si_other.sin_port);
if (len > 0) {
rx_buffer = new cbuf(len);
rx_buffer->write(buf, len);
}
delete[] buf;
return len;
}
int LwIPUDP::available() {
if (!rx_buffer)
return 0;
return rx_buffer->available();
}
int LwIPUDP::read() {
if (!rx_buffer)
return -1;
int out = rx_buffer->read();
if (!rx_buffer->available()) {
cbuf *b = rx_buffer;
rx_buffer = 0;
delete b;
}
return out;
}
int LwIPUDP::read(unsigned char *buffer, size_t len) {
return read((char *)buffer, len);
}
int LwIPUDP::read(char *buffer, size_t len) {
if (!rx_buffer)
return 0;
int out = rx_buffer->read(buffer, len);
if (!rx_buffer->available()) {
cbuf *b = rx_buffer;
rx_buffer = 0;
delete b;
}
return out;
}
int LwIPUDP::peek() {
if (!rx_buffer)
return -1;
return rx_buffer->peek();
}
void LwIPUDP::flush() {
if (!rx_buffer)
return;
cbuf *b = rx_buffer;
rx_buffer = 0;
delete b;
}
IPAddress LwIPUDP::remoteIP() {
return remote_ip;
}
uint16_t LwIPUDP::remotePort() {
return remote_port;
}
#endif

View File

@@ -0,0 +1,43 @@
/* Copyright (c) Kuba Szczodrzyński 2022-09-10. */
#pragma once
#include <api/WiFi/WiFi.h>
#include <api/WiFiUdp.h>
#include <cbuf.h>
class LwIPUDP : public IWiFiUDP {
private:
int udp_server;
IPAddress multicast_ip;
IPAddress remote_ip;
uint16_t server_port;
uint16_t remote_port;
char *tx_buffer;
size_t tx_buffer_len;
cbuf *rx_buffer;
public:
LwIPUDP();
~LwIPUDP();
uint8_t begin(IPAddress ip, uint16_t port);
uint8_t begin(uint16_t port);
uint8_t beginMulticast(IPAddress ip, uint16_t port);
void stop();
int beginMulticastPacket();
int beginPacket();
int beginPacket(IPAddress ip, uint16_t port);
int beginPacket(const char *host, uint16_t port);
int endPacket();
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
int parsePacket();
int available();
int read();
int read(unsigned char *buffer, size_t len);
int read(char *buffer, size_t len);
int peek();
void flush();
IPAddress remoteIP();
uint16_t remotePort();
};

View File

@@ -1,23 +1,17 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#if LT_ARD_HAS_WIFI && LT_HAS_MBEDTLS
#include "MbedTLSClient.h"
#include <IPAddress.h>
#include <WiFi.h>
#include <WiFiClient.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <mbedtls/debug.h>
#include <mbedtls/platform.h>
#include <mbedtls/sha256.h>
#include <mbedtls/ssl.h>
#ifdef __cplusplus
} // extern "C"
#endif
MbedTLSClient::MbedTLSClient() : WiFiClient() {
init(); // ensure the context is zero filled
@@ -27,9 +21,13 @@ MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {
init(); // ensure the context is zero filled
}
MbedTLSClient::~MbedTLSClient() {
LT_VM(CLIENT, "~MbedTLSClient()");
stop();
}
void MbedTLSClient::stop() {
WiFiClient::stop();
LT_V_SSL("Closing SSL connection");
LT_VM(SSL, "Stopping SSL");
if (_sslCfg.ca_chain) {
mbedtls_x509_crt_free(&_caCert);
@@ -40,6 +38,7 @@ void MbedTLSClient::stop() {
}
mbedtls_ssl_free(&_sslCtx);
mbedtls_ssl_config_free(&_sslCfg);
LT_HEAP_I();
}
void MbedTLSClient::init() {
@@ -85,7 +84,11 @@ static int ssl_random(void *data, unsigned char *output, size_t len) {
}
void debug_cb(void *ctx, int level, const char *file, int line, const char *str) {
LT_I("%04d: |%d| %s", line, level, str);
// do not print the trailing \n
uint16_t len = strlen(str);
char *msg = (char *)str;
msg[len - 1] = '\0';
LT_IM(SSL, "%04d: |%d| %s", line, level, msg);
}
int MbedTLSClient::connect(
@@ -98,7 +101,7 @@ int MbedTLSClient::connect(
const char *pskIdent,
const char *psk
) {
LT_D_SSL("Free heap before TLS: TODO");
LT_HEAP_I();
if (!rootCABuf && !pskIdent && !psk && !_insecure && !_useRootCA)
return -1;
@@ -112,14 +115,15 @@ int MbedTLSClient::connect(
int ret = WiFiClient::connect(addr, port, timeout);
if (ret < 0) {
LT_E("SSL socket failed");
LT_EM(SSL, "SSL socket failed");
return ret;
}
char *uid = "lt-ssl"; // TODO
LT_V_SSL("Init SSL");
LT_VM(SSL, "Init SSL");
init();
LT_HEAP_I();
// mbedtls_debug_set_threshold(4);
// mbedtls_ssl_conf_dbg(&_sslCfg, debug_cb, NULL);
@@ -156,7 +160,7 @@ int MbedTLSClient::connect(
#ifdef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED
uint16_t len = strlen(psk);
if ((len & 1) != 0 || len > 2 * MBEDTLS_PSK_MAX_LEN) {
LT_E("PSK length invalid");
LT_EM(SSL, "PSK length invalid");
return -1;
}
unsigned char pskBin[MBEDTLS_PSK_MAX_LEN] = {};
@@ -181,13 +185,13 @@ int MbedTLSClient::connect(
if (!_insecure && clientCert && clientKey) {
mbedtls_x509_crt_init(&_clientCert);
mbedtls_pk_init(&_clientKey);
LT_V_SSL("Loading client cert");
LT_VM(SSL, "Loading client cert");
ret = mbedtls_x509_crt_parse(&_clientCert, (const unsigned char *)clientCert, strlen(clientCert) + 1);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
LT_RET(ret);
}
LT_V_SSL("Loading private key");
LT_VM(SSL, "Loading private key");
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)clientKey, strlen(clientKey) + 1, NULL, 0);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
@@ -196,7 +200,7 @@ int MbedTLSClient::connect(
mbedtls_ssl_conf_own_cert(&_sslCfg, &_clientCert, &_clientKey);
}
LT_V_SSL("Setting TLS hostname");
LT_VM(SSL, "Setting TLS hostname");
ret = mbedtls_ssl_set_hostname(&_sslCtx, host);
LT_RET_NZ(ret);
@@ -208,7 +212,9 @@ int MbedTLSClient::connect(
mbedtls_ssl_set_bio(&_sslCtx, &_sockTls, mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_net_set_nonblock((mbedtls_net_context *)&_sockTls);
LT_V_SSL("SSL handshake");
LT_HEAP_I();
LT_VM(SSL, "SSL handshake");
if (_handshakeTimeout == 0)
_handshakeTimeout = timeout;
unsigned long start = millis();
@@ -217,33 +223,36 @@ int MbedTLSClient::connect(
LT_RET(ret);
}
if ((millis() - start) > _handshakeTimeout) {
LT_E("SSL handshake timeout");
LT_EM(SSL, "SSL handshake timeout");
return -1;
}
delay(2);
}
LT_HEAP_I();
if (clientCert && clientKey) {
LT_D_SSL(
LT_DM(
SSL,
"Protocol %s, ciphersuite %s",
mbedtls_ssl_get_version(&_sslCtx),
mbedtls_ssl_get_ciphersuite(&_sslCtx)
);
ret = mbedtls_ssl_get_record_expansion(&_sslCtx);
if (ret >= 0)
LT_D_SSL("Record expansion: %d", ret);
LT_DM(SSL, "Record expansion: %d", ret);
else {
LT_W("Record expansion unknown");
LT_WM(SSL, "Record expansion unknown");
}
}
LT_V_SSL("Verifying certificate");
LT_VM(SSL, "Verifying certificate");
ret = mbedtls_ssl_get_verify_result(&_sslCtx);
if (ret) {
char buf[512];
memset(buf, 0, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", ret);
LT_E("Failed to verify peer certificate! Verification info: %s", buf);
LT_EM(SSL, "Failed to verify peer certificate! Verification info: %s", buf);
return ret;
}
@@ -409,7 +418,7 @@ bool MbedTLSClient::verify(const char *fingerprint, const char *domainName) {
return false;
if (memcmp(fpLocal, fpRemote, 32)) {
LT_D_SSL("Fingerprints don't match");
LT_DM(SSL, "Fingerprints don't match");
return false;
}
@@ -430,7 +439,7 @@ void MbedTLSClient::setAlpnProtocols(const char **alpnProtocols) {
bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&_sslCtx);
if (!cert) {
LT_E("Failed to get peer certificate");
LT_EM(SSL, "Failed to get peer certificate");
return false;
}
mbedtls_sha256_context shaCtx;
@@ -440,3 +449,5 @@ bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
mbedtls_sha256_finish(&shaCtx, result);
return true;
}
#endif // LT_ARD_HAS_WIFI && LT_HAS_MBEDTLS

View File

@@ -2,20 +2,17 @@
#pragma once
#include <api/WiFi/WiFi.h>
#include <api/WiFiClient.h>
#include <api/WiFiClientSecure.h>
#include <WiFiClient.h> // extend platform's WiFiClient impl
#include <WiFiClient.h> // extend family's WiFiClient impl
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <mbedtls/net.h>
#ifdef __cplusplus
} // extern "C"
#endif
class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
private:
@@ -53,6 +50,7 @@ class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
public:
MbedTLSClient();
MbedTLSClient(int sock);
~MbedTLSClient();
int connect(IPAddress ip, uint16_t port, int32_t timeout);
int connect(const char *host, uint16_t port, int32_t timeout);

View File

@@ -0,0 +1,61 @@
/**
StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "StreamString.h"
size_t StreamString::write(const uint8_t *data, size_t size) {
if(size && data) {
concat(data, size);
return size;
}
return 0;
}
size_t StreamString::write(uint8_t data) {
return concat((char) data);
}
int StreamString::available() {
return length();
}
int StreamString::read() {
if(length()) {
char c = charAt(0);
remove(0, 1);
return c;
}
return -1;
}
int StreamString::peek() {
if(length()) {
char c = charAt(0);
return c;
}
return -1;
}
void StreamString::flush() {
}

View File

@@ -0,0 +1,39 @@
/**
StreamString.h
Copyright (c) 2015 Markus Sattler. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STREAMSTRING_H_
#define STREAMSTRING_H_
class StreamString: public Stream, public String
{
public:
size_t write(const uint8_t *buffer, size_t size) override;
size_t write(uint8_t data) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
};
#endif /* STREAMSTRING_H_ */

View File

@@ -0,0 +1,224 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "Update.h"
UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) {
cleanup();
}
/**
* @brief Initialize the update process.
*
* @param size total UF2 file size
* @param command must be U_FLASH
* @return false if parameters are invalid or update is running, true otherwise
*/
bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, const char *unused4) {
if (ctx)
return false;
cleanup();
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget());
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
info = uf2_info_init();
if (!size) {
cleanup(UPDATE_ERROR_SIZE);
return false;
}
if (command != U_FLASH) {
cleanup(UPDATE_ERROR_BAD_ARGUMENT);
return false;
}
bytesTotal = size;
return true;
}
/**
* @brief Finalize the update process. Check for errors and update completion, then activate the new firmware image.
*
* @param evenIfRemaining no idea
* @return false in case of errors or no update running, true otherwise
*/
bool UpdateClass::end(bool evenIfRemaining) {
if (hasError() || !ctx)
// false if not running
return false;
if (!isFinished() && !evenIfRemaining) {
// abort if not finished
cleanup(UPDATE_ERROR_ABORT);
return false;
}
// TODO what is evenIfRemaining for?
if (!LT.otaSwitch()) {
// try to activate the second OTA
cleanup(UPDATE_ERROR_ACTIVATE);
return false;
}
cleanup();
return true;
}
/**
* @brief Write a chunk of data to the buffer or flash memory.
*
* It's advised to write in 512-byte chunks (or its multiples).
*
* @param data
* @param len
* @return size_t
*/
size_t UpdateClass::write(uint8_t *data, size_t len) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
return 0;
LT_VM(OTA, "write(%u) / buf %u/512", len, bufSize());
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
// buffer empty and entire block is in data
if (!tryWriteData(data, UF2_BLOCK_SIZE)) {
// returns 0 if data contains an invalid block
return written;
}
data += UF2_BLOCK_SIZE;
len -= UF2_BLOCK_SIZE;
written += UF2_BLOCK_SIZE;
} */
// write until buffer space is available
uint16_t toWrite; // 1..512
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError()) {
// return on errors
printErrorContext2(data, toWrite);
return written;
}
data += toWrite;
len -= toWrite;
written += toWrite;
}
return written;
}
size_t UpdateClass::writeStream(Stream &data) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
return 0;
uint32_t lastData = millis();
// loop until the update is complete
while (remaining()) {
// check stream availability
int available = data.available();
if (available <= 0) {
if (millis() - lastData > UPDATE_TIMEOUT_MS) {
// waited for data too long; abort with error
cleanup(UPDATE_ERROR_STREAM);
return written;
}
continue;
}
// available > 0
lastData = millis();
// read data to fit in the remaining buffer space
bufAlloc();
uint16_t read = data.readBytes(bufPos, bufLeft());
bufPos += read;
written += read;
tryWriteData();
if (hasError()) {
// return on errors
printErrorContext2(NULL, read); // buf is not valid anymore
return written;
}
}
return written;
}
/**
* @brief Try to use the buffer as a block to write. In case of UF2 errors,
* error codes are set, the update is aborted and 0 is returned
*
* @param data received data to copy to buffer or NULL if already in buffer
* @param len received data length - must be at most bufLeft()
* @return size_t "used" data size - 0 or 512
*/
size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
uf2_block_t *block = NULL;
LT_VM(OTA, "Writing %u to buffer (%u/512)", len, bufSize());
if (len == UF2_BLOCK_SIZE) {
// data has a complete block
block = (uf2_block_t *)data;
} else if (data && len) {
// data has a part of a block, copy it to buffer
bufAlloc();
memcpy(bufPos, data, len);
bufPos += len;
}
if (!block && bufSize() == UF2_BLOCK_SIZE) {
// use buffer as block (only if not found above)
block = (uf2_block_t *)buf;
}
// a complete block has been found
if (block) {
if (checkUf2Error(uf2_check_block(ctx, block)))
// block is invalid
return 0;
if (errUf2 == UF2_ERR_IGNORE)
// treat ignored blocks as valid
return UF2_BLOCK_SIZE;
if (!bytesWritten) {
// parse header block to allow retrieving firmware info
if (checkUf2Error(uf2_parse_header(ctx, block, info)))
// header is invalid
return 0;
LT_IM(OTA, "%s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board);
if (bytesTotal == UPDATE_SIZE_UNKNOWN) {
// set total update size from block count info
bytesTotal = block->block_count * UF2_BLOCK_SIZE;
} else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) {
// given update size does not match the block count
LT_EM(OTA, "Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE);
cleanup(UPDATE_ERROR_SIZE);
return 0;
}
} else {
// write data blocks normally
if (checkUf2Error(uf2_write(ctx, block)))
// block writing failed
return 0;
}
// increment total writing progress
bytesWritten += UF2_BLOCK_SIZE;
// call progress callback
if (callback)
callback(bytesWritten, bytesTotal);
// reset the buffer as it's used already
if (bufSize() == UF2_BLOCK_SIZE)
bufPos = buf;
return UF2_BLOCK_SIZE;
}
return 0;
}
UpdateClass Update;

View File

@@ -0,0 +1,154 @@
#pragma once
#include <Arduino.h>
#include <functional>
#include <uf2ota/uf2ota.h>
// No Error
#define UPDATE_ERROR_OK (0)
// Flash Write Failed
#define UPDATE_ERROR_WRITE (1)
// Flash Erase Failed
#define UPDATE_ERROR_ERASE (2)
// Flash Read Failed
#define UPDATE_ERROR_READ (3)
// Not Enough Space
#define UPDATE_ERROR_SPACE (4)
// Bad Size Given
#define UPDATE_ERROR_SIZE (5)
// Stream Read Timeout
#define UPDATE_ERROR_STREAM (6)
// MD5 Check Failed
#define UPDATE_ERROR_MD5 (7)
// Wrong Magic Byte
#define UPDATE_ERROR_MAGIC_BYTE (8)
// Could Not Activate The Firmware
#define UPDATE_ERROR_ACTIVATE (9)
// Partition Could Not be Found
#define UPDATE_ERROR_NO_PARTITION (10)
// Bad Argument
#define UPDATE_ERROR_BAD_ARGUMENT (11)
// Aborted
#define UPDATE_ERROR_ABORT (12)
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
#define U_FLASH 0
#define U_SPIFFS 100
#define U_AUTH 200
#define ENCRYPTED_BLOCK_SIZE 16
#define UPDATE_TIMEOUT_MS 30 * 1000
class UpdateClass {
public:
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
public: /* Update.cpp */
UpdateClass();
bool begin(
size_t size = UPDATE_SIZE_UNKNOWN,
int command = U_FLASH,
int unused2 = -1,
uint8_t unused3 = LOW,
const char *unused4 = NULL // this is for SPIFFS
);
bool end(bool evenIfRemaining = false);
size_t write(uint8_t *data, size_t len);
size_t writeStream(Stream &data);
bool canRollBack();
bool rollBack();
// bool setMD5(const char *expected_md5);
private: /* Update.cpp */
size_t tryWriteData(uint8_t *data = NULL, size_t len = 0);
public: /* UpdateUtil.cpp */
UpdateClass &onProgress(THandlerFunction_Progress callback);
void abort();
void printError(Print &out);
const char *errorString();
const char *getFirmwareName();
const char *getFirmwareVersion();
const char *getLibreTuyaVersion();
const char *getBoardName();
private: /* UpdateUtil.cpp */
void cleanup(uint8_t ardErr = UPDATE_ERROR_OK, uf2_err_t uf2Err = UF2_ERR_OK);
bool checkUf2Error(uf2_err_t err);
void bufAlloc();
void printErrorContext1();
void printErrorContext2(const uint8_t *data, size_t len);
uint16_t bufLeft();
uint16_t bufSize();
private:
// uf2ota context
uf2_ota_t *ctx;
uf2_info_t *info;
// block buffer
uint8_t *buf;
uint8_t *bufPos;
// update progress - multiplies of 512 bytes
uint32_t bytesWritten;
uint32_t bytesTotal;
// errors
uf2_err_t errUf2;
uint8_t errArd;
// progress callback
THandlerFunction_Progress callback;
// String _target_md5;
// MD5Builder _md5;
public:
String md5String(void) {
// return _md5.toString();
}
void md5(uint8_t *result) {
// return _md5.getBytes(result);
}
uint8_t getError() {
return errArd;
}
uf2_err_t getUF2Error() {
return errUf2;
}
uint16_t getErrorCode() {
return (errArd << 8) | errUf2;
}
void clearError() {
cleanup(UPDATE_ERROR_OK);
}
bool hasError() {
return errArd != UPDATE_ERROR_OK;
}
bool isRunning() {
return ctx != NULL;
}
bool isFinished() {
return bytesWritten == bytesTotal;
}
size_t size() {
return bytesTotal;
}
size_t progress() {
return bytesWritten;
}
size_t remaining() {
return bytesTotal - bytesWritten;
}
};
extern UpdateClass Update;

View File

@@ -0,0 +1,190 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-30. */
#include "Update.h"
static const uint8_t errorMap[] = {
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA index */
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_ONE - only one partition tag in a block */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */
UPDATE_ERROR_WRITE /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */
};
static char errorStr[14];
/**
* @brief Set the callback invoked after writing data to flash.
*/
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) {
this->callback = callback;
return *this;
}
void UpdateClass::cleanup(uint8_t ardErr, uf2_err_t uf2Err) {
errUf2 = uf2Err;
errArd = ardErr;
#if LT_DEBUG_OTA
if (hasError())
printErrorContext1();
#endif
free(ctx); // NULL in constructor
ctx = NULL;
uf2_info_free(info); // NULL in constructor
info = NULL;
free(buf); // NULL in constructor
buf = bufPos = NULL;
bytesWritten = 0;
bytesTotal = 0;
}
/**
* @brief Check for UF2 errors. Set errArd and errUf2 in case of errors.
* Ignored blocks are not reported as errors.
* Abort the update.
* Use like: "if (errorUf2(...)) return false;"
* @return true if err is not OK, false otherwise
*/
bool UpdateClass::checkUf2Error(uf2_err_t err) {
if (err <= UF2_ERR_IGNORE)
return false;
cleanup(errorMap[err], err);
return true;
}
/**
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
LT_DM(OTA, "Aborting update");
cleanup(UPDATE_ERROR_ABORT);
}
void UpdateClass::bufAlloc() {
if (!buf)
buf = bufPos = (uint8_t *)malloc(UF2_BLOCK_SIZE);
}
uint16_t UpdateClass::bufLeft() {
return buf + UF2_BLOCK_SIZE - bufPos;
}
uint16_t UpdateClass::bufSize() {
return bufPos - buf;
}
/**
* @brief Print string error info to the stream.
*/
void UpdateClass::printError(Print &out) {
out.println(errorString());
}
/**
* @brief Print details about the error and current OTA state.
*/
void UpdateClass::printErrorContext1() {
#if LT_DEBUG_OTA
LT_EM(OTA, "Error: %s", errorString());
if (errArd == UPDATE_ERROR_ABORT)
return;
LT_EM(OTA, "- written: %u of %u", bytesWritten, bytesTotal);
LT_EM(OTA, "- buf: size=%u, left=%u", bufSize(), bufLeft());
hexdump(buf, bufSize());
if (ctx)
LT_EM(
OTA,
"- ctx: seq=%u, part1=%s, part2=%s",
ctx->seq - 1, // print last parsed block seq
ctx->part1 ? ctx->part1->name : NULL,
ctx->part2 ? ctx->part2->name : NULL
);
uf2_block_t *block = (uf2_block_t *)buf;
if (buf)
LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
#endif
}
void UpdateClass::printErrorContext2(const uint8_t *data, size_t len) {
#if LT_DEBUG_OTA
LT_EM(OTA, "- while writing %u bytes", len);
if (data)
hexdump(data, len);
#endif
}
/**
* @brief Get string representation of the error in format
* "ard=..,uf2=..". Returns "" if no error.
*/
const char *UpdateClass::errorString() {
if (!errArd && !errUf2)
return "";
sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2);
return errorStr;
}
/**
* @brief Get firmware name from UF2 info.
*/
const char *UpdateClass::getFirmwareName() {
if (info)
return info->fw_name;
return NULL;
}
/**
* @brief Get firmware version from UF2 info.
*/
const char *UpdateClass::getFirmwareVersion() {
if (info)
return info->fw_version;
return NULL;
}
/**
* @brief Get LibreTuya version from UF2 info.
*/
const char *UpdateClass::getLibreTuyaVersion() {
if (info)
return info->lt_version;
return NULL;
}
/**
* @brief Get target board name from UF2 info.
*/
const char *UpdateClass::getBoardName() {
if (info)
return info->board;
return NULL;
}
/**
* @brief See LT.otaCanRollback() for more info.
*/
bool UpdateClass::canRollBack() {
return LT.otaCanRollback();
}
/**
* @brief See LT.otaRollback() for more info.
*/
bool UpdateClass::rollBack() {
return LT.otaRollback();
}

View File

@@ -19,6 +19,8 @@
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#if LT_ARD_HAS_WIFI
#include <Arduino.h>
#include "WebServer.h"
@@ -603,3 +605,5 @@ bool WebServer::_parseFormUploadAborted() {
_currentHandler->upload(*this, _currentUri, *_currentUpload);
return false;
}
#endif // LT_ARD_HAS_WIFI

View File

@@ -20,6 +20,8 @@
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#if LT_ARD_HAS_WIFI
#include <Arduino.h>
#include "FS.h"
@@ -41,7 +43,7 @@ WebServer::WebServer(IPAddress addr, int port)
_statusChange(0), _nullDelay(true), _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr),
_currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), _headerKeysCount(0),
_currentHeaders(nullptr), _contentLength(0), _chunked(false) {
log_v("WebServer::Webserver(addr=%s, port=%d)", addr.toString().c_str(), port);
log_v("WebServer::Webserver(addr=%s, port=%d)", ipToString(addr).c_str(), port);
}
WebServer::WebServer(int port)
@@ -263,7 +265,7 @@ void WebServer::handleClient() {
return;
}
log_v("New client: client.localIP()=%s", client.localIP().toString().c_str());
log_v("New client: client.localIP()=%s", ipToString(client.localIP()).c_str());
_currentClient = client;
_currentStatus = HC_WAIT_READ;
@@ -715,3 +717,5 @@ String WebServer::_responseCodeToString(int code) {
return F("");
}
}
#endif // LT_ARD_HAS_WIFI

View File

@@ -23,6 +23,8 @@
*
*/
#if LT_ARD_HAS_WIFI
#include "WiFiMulti.h"
#include <Arduino.h>
#include <limits.h>
@@ -48,27 +50,27 @@ bool WiFiMulti::addAP(const char *ssid, const char *passphrase) {
if (!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
// fail SSID too long or missing!
LT_E("SSID missing or too long");
LT_EM(WIFI, "SSID missing or too long");
return false;
}
if (passphrase && strlen(passphrase) > 64) {
// fail passphrase too long!
LT_E("Passphrase too long");
LT_EM(WIFI, "Passphrase too long");
return false;
}
newAP.ssid = strdup(ssid);
if (!newAP.ssid) {
LT_E("Fail newAP.ssid == 0");
LT_EM(WIFI, "Fail newAP.ssid == 0");
return false;
}
if (passphrase && *passphrase != 0x00) {
newAP.passphrase = strdup(passphrase);
if (!newAP.passphrase) {
LT_E("Fail newAP.passphrase == 0");
LT_EM(WIFI, "Fail newAP.passphrase == 0");
free(newAP.ssid);
return false;
}
@@ -77,7 +79,7 @@ bool WiFiMulti::addAP(const char *ssid, const char *passphrase) {
}
APlist.push_back(newAP);
LT_V("Add SSID: %s", newAP.ssid);
LT_VM(WIFI, "Add SSID: %s", newAP.ssid);
return true;
}
@@ -106,12 +108,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
uint8_t bestBSSID[6];
int32_t bestChannel = 0;
LT_I("Scan finished");
LT_IM(WIFI, "Scan finished");
if (scanResult == 0) {
LT_I("No networks found");
LT_IM(WIFI, "No networks found");
} else {
LT_I("%d networks found", scanResult);
LT_IM(WIFI, "%d networks found", scanResult);
for (int8_t i = 0; i < scanResult; ++i) {
String ssid_scan;
@@ -142,7 +144,8 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
}
if (known) {
LT_D(
LT_DM(
WIFI,
" ---> %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
i,
chan_scan,
@@ -157,7 +160,8 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
(sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*'
);
} else {
LT_D(
LT_DM(
WIFI,
" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
i,
chan_scan,
@@ -179,7 +183,8 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
WiFi.scanDelete();
if (bestNetwork.ssid) {
LT_I(
LT_IM(
WIFI,
"Connecting to BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)",
bestBSSID[0],
bestBSSID[1],
@@ -203,36 +208,41 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
status = WiFi.status();
}
IPAddress ip;
switch (status) {
case WL_CONNECTED:
LT_I("Connecting done");
LT_D("SSID: %s", WiFi.SSID().c_str());
LT_D("IP: %s", WiFi.localIP().toString().c_str());
LT_D("MAC: %s", WiFi.BSSIDstr().c_str());
LT_D("Channel: %d", WiFi.channel());
LT_IM(WIFI, "Connecting done");
LT_DM(WIFI, "SSID: %s", WiFi.SSID().c_str());
// TODO fix this after implementing IP format for printf()
ip = WiFi.localIP();
LT_DM(WIFI, "IP: %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
LT_DM(WIFI, "MAC: %s", WiFi.BSSIDstr().c_str());
LT_DM(WIFI, "Channel: %d", WiFi.channel());
break;
case WL_NO_SSID_AVAIL:
LT_E("Connecting failed; AP not found");
LT_EM(WIFI, "Connecting failed; AP not found");
break;
case WL_CONNECT_FAILED:
LT_E("Connecting failed");
LT_EM(WIFI, "Connecting failed");
break;
default:
LT_E("Connecting failed (%d)", status);
LT_EM(WIFI, "Connecting failed (%d)", status);
break;
}
} else {
LT_E("No matching network found!");
LT_EM(WIFI, "No matching network found!");
}
} else {
// start scan
LT_V("Delete old wifi config...");
LT_VM(WIFI, "Delete old wifi config...");
WiFi.disconnect();
LT_D("Start scan");
LT_DM(WIFI, "Start scan");
// scan wifi async mode
WiFi.scanNetworks(true);
}
return status;
}
#endif // LT_ARD_HAS_WIFI

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -0,0 +1,196 @@
/*
cbuf.cpp - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "cbuf.h"
cbuf::cbuf(size_t size) :
next(NULL), _size(size+1), _buf(new char[size+1]), _bufend(_buf + size + 1), _begin(_buf), _end(_begin)
{
}
cbuf::~cbuf()
{
delete[] _buf;
}
size_t cbuf::resizeAdd(size_t addSize)
{
return resize(_size + addSize);
}
size_t cbuf::resize(size_t newSize)
{
size_t bytes_available = available();
newSize += 1;
// not lose any data
// if data can be lost use remove or flush before resize
if((newSize < bytes_available) || (newSize == _size)) {
return _size;
}
char *newbuf = new char[newSize];
char *oldbuf = _buf;
if(!newbuf) {
return _size;
}
if(_buf) {
read(newbuf, bytes_available);
memset((newbuf + bytes_available), 0x00, (newSize - bytes_available));
}
_begin = newbuf;
_end = newbuf + bytes_available;
_bufend = newbuf + newSize;
_size = newSize;
_buf = newbuf;
delete[] oldbuf;
return _size;
}
size_t cbuf::available() const
{
if(_end >= _begin) {
return _end - _begin;
}
return _size - (_begin - _end);
}
size_t cbuf::size()
{
return _size;
}
size_t cbuf::room() const
{
if(_end >= _begin) {
return _size - (_end - _begin) - 1;
}
return _begin - _end - 1;
}
int cbuf::peek()
{
if(empty()) {
return -1;
}
return static_cast<int>(*_begin);
}
size_t cbuf::peek(char *dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
char * begin = _begin;
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, begin, size_to_read);
return size_read;
}
int cbuf::read()
{
if(empty()) {
return -1;
}
char result = *_begin;
_begin = wrap_if_bufend(_begin + 1);
return static_cast<int>(result);
}
size_t cbuf::read(char* dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
_begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, _begin, size_to_read);
_begin = wrap_if_bufend(_begin + size_to_read);
return size_read;
}
size_t cbuf::write(char c)
{
if(full()) {
return 0;
}
*_end = c;
_end = wrap_if_bufend(_end + 1);
return 1;
}
size_t cbuf::write(const char* src, size_t size)
{
size_t bytes_available = room();
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
size_t size_written = size_to_write;
if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) {
size_t top_size = _bufend - _end;
memcpy(_end, src, top_size);
_end = _buf;
size_to_write -= top_size;
src += top_size;
}
memcpy(_end, src, size_to_write);
_end = wrap_if_bufend(_end + size_to_write);
return size_written;
}
void cbuf::flush()
{
_begin = _buf;
_end = _buf;
}
size_t cbuf::remove(size_t size)
{
size_t bytes_available = available();
if(size >= bytes_available) {
flush();
return 0;
}
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
_begin = _buf;
size_to_remove -= top_size;
}
_begin = wrap_if_bufend(_begin + size_to_remove);
return available();
}

View File

@@ -0,0 +1,79 @@
/*
cbuf.h - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __cbuf_h
#define __cbuf_h
#include <stddef.h>
#include <stdint.h>
#include <string.h>
class cbuf
{
public:
cbuf(size_t size);
~cbuf();
size_t resizeAdd(size_t addSize);
size_t resize(size_t newSize);
size_t available() const;
size_t size();
size_t room() const;
inline bool empty() const
{
return _begin == _end;
}
inline bool full() const
{
return wrap_if_bufend(_end + 1) == _begin;
}
int peek();
size_t peek(char *dst, size_t size);
int read();
size_t read(char* dst, size_t size);
size_t write(char c);
size_t write(const char* src, size_t size);
void flush();
size_t remove(size_t size);
cbuf *next;
protected:
inline char* wrap_if_bufend(char* ptr) const
{
return (ptr == _bufend) ? _buf : ptr;
}
size_t _size;
char* _buf;
const char* _bufend;
char* _begin;
char* _end;
};
#endif//__cbuf_h

View File

@@ -3,69 +3,68 @@
#ifdef LT_HAS_LWIP2
#include "mDNS.h"
#include <vector>
extern "C" {
#include <errno.h>
#include <lwip/apps/mdns.h>
#include <lwip/igmp.h>
#include <lwip/init.h>
#include <lwip/netif.h>
}
extern u8_t mdns_netif_client_id;
struct mdns_domain {
/* Encoded domain name */
u8_t name[256];
/* Total length of domain name, including zero */
u16_t length;
/* Set if compression of this domain is not allowed */
u8_t skip_compression;
};
/** Description of a service */
struct mdns_service {
/** TXT record to answer with */
struct mdns_domain txtdata;
/** Name of service, like 'myweb' */
char name[MDNS_LABEL_MAXLEN + 1];
/** Type of service, like '_http' */
char service[MDNS_LABEL_MAXLEN + 1];
/** Callback function and userdata
* to update txtdata buffer */
service_get_txt_fn_t txt_fn;
void *txt_userdata;
/** TTL in seconds of SRV/TXT replies */
u32_t dns_ttl;
/** Protocol, TCP or UDP */
u16_t proto;
/** Port of the service */
u16_t port;
};
/** Description of a host/netif */
struct mdns_host {
/** Hostname */
char name[MDNS_LABEL_MAXLEN + 1];
/** Pointer to services */
struct mdns_service *services[MDNS_MAX_SERVICES];
/** TTL in seconds of A/AAAA/PTR replies */
u32_t dns_ttl;
};
static String mdnsInstanceName = "default_instance";
static std::vector<char *> services;
static std::vector<uint8_t> protos;
static std::vector<std::vector<char *>> records;
mDNS::mDNS() {}
mDNS::~mDNS() {}
bool mDNS::begin(const char *hostname) {
mdns_resp_init();
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif) && mdns_resp_add_netif(netif, hostname, 255) != ERR_OK) {
return false;
}
netif = netif->next;
static void mdnsTxtCallback(struct mdns_service *service, void *userdata) {
size_t index = (size_t)userdata;
if (index >= records.size())
return;
for (const auto record : records[index]) {
err_t err = mdns_resp_add_service_txtitem(service, record, strlen(record));
if (err != ERR_OK)
return;
}
return true;
}
static void mdnsStatusCallback(struct netif *netif, uint8_t result) {
LT_DM(MDNS, "Status: netif %u, status %u", netif->num, result);
}
bool mDNS::begin(const char *hostname) {
setInstanceName(hostname);
LT_DM(MDNS, "Starting (%s)", hostname);
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 1
mdns_resp_register_name_result_cb(mdnsStatusCallback);
#endif
mdns_resp_init();
uint8_t enabled = 0;
struct netif *netif;
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (!netif_is_up(netif))
continue;
LT_DM(MDNS, "Adding netif %u", netif->num);
if ((netif->flags & NETIF_FLAG_IGMP) == 0) {
LT_DM(MDNS, "Enabling IGMP");
netif->flags |= NETIF_FLAG_IGMP;
igmp_start(netif);
}
err_t ret = mdns_resp_add_netif(netif, hostname, 255);
if (ret == ERR_OK)
enabled++;
else
LT_DM(MDNS, "Cannot add netif %u; ret=%d, errno=%d", netif->num, ret, errno);
}
return enabled > 0;
}
void mDNS::end() {
@@ -77,72 +76,53 @@ void mDNS::end() {
}
}
void mDNS::setInstanceName(String name) {
mdnsInstanceName = name;
}
bool mDNS::addService(char *service, char *proto, uint16_t port) {
char _service[strlen(service) + 2];
char _proto[strlen(proto) + 2];
_service[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_service + 1, service + (service[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
bool mDNS::addServiceImpl(const char *name, const char *service, uint8_t proto, uint16_t port) {
bool added = false;
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns_resp_add_service(netif, mdnsInstanceName.c_str(), service, protocol, port, 255, NULL, NULL);
// register TXT callback;
// pass service index as userdata parameter
LT_DM(MDNS, "Add service: netif %u / %s / %s / %u / %u", netif->num, name, service, proto, port);
mdns_resp_add_service(
netif,
name,
service,
(mdns_sd_proto)proto,
port,
255,
mdnsTxtCallback,
(void *)services.size() // index of newly added service
);
added = true;
}
netif = netif->next;
}
if (!added)
return false;
// add the service to TXT record arrays
services.push_back(strdup(service));
protos.push_back(proto);
records.emplace_back();
return true;
}
bool mDNS::addServiceTxt(char *name, char *proto, char *key, char *value) {
char _name[strlen(name) + 2];
char _proto[strlen(proto) + 2];
_name[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_name + 1, name + (name[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
struct netif *netif = netif_list;
struct mdns_host *mdns;
struct mdns_service *service;
uint8_t txt_len = strlen(key) + strlen(value) + 1;
char *txt = (char *)malloc(txt_len + 1);
sprintf(txt, "%s=%s", key, value);
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns = (struct mdns_host *)netif_get_client_data(netif, mdns_netif_client_id);
for (uint8_t i = 0; i < MDNS_MAX_SERVICES; i++) {
service = mdns->services[i];
if (service == NULL)
continue;
if (strcmp(service->service, _name) || service->proto != protocol)
continue;
if (mdns_resp_add_service_txtitem(service, txt, txt_len) != ERR_OK) {
free(txt);
return false;
}
}
bool mDNS::addServiceTxtImpl(const char *service, uint8_t proto, const char *item) {
int8_t index = -1;
for (uint8_t i = 0; i < services.size(); i++) {
// find a matching service
if (strcmp(services[i], service) == 0 && protos[i] == proto) {
index = i;
break;
}
netif = netif->next;
}
free(txt);
if (index == -1)
return false;
records[index].push_back(strdup(item));
return true;
}

Some files were not shown because too many files have changed in this diff Show More