142 Commits

Author SHA1 Message Date
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
Kuba Szczodrzyński
ad590d1eb2 [release] v0.4.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-05-23 14:21:19 +02:00
Kuba Szczodrzyński
8b00358901 [core] Add lwIP-based mDNS responder library 2022-05-23 14:20:41 +02:00
Kuba Szczodrzyński
bf4d8bb9be [realtek-ambz] Update to external lwIP v2.0.0 2022-05-23 12:49:18 +02:00
Kuba Szczodrzyński
157dd2f407 [realtek-ambz] Move to external lwIP v1.4.1 2022-05-23 12:49:18 +02:00
Kuba Szczodrzyński
1dc47878d4 [core] Allow using external framework parts 2022-05-18 20:57:09 +02:00
Kuba Szczodrzyński
36e4b6fd45 [core] Write manifests when installing packages 2022-05-18 20:47:40 +02:00
Kuba Szczodrzyński
36d9a6e7e4 [realtek-ambz] Support WiFi events 2022-05-17 19:27:35 +02:00
Kuba Szczodrzyński
b7cceed4d3 [core] Create POSIX utils module 2022-05-16 15:54:52 +02:00
Kuba Szczodrzyński
694c06e7f5 [core] Allow using "arduino" as framework 2022-05-16 15:53:51 +02:00
Kuba Szczodrzyński
12338747bf [realtek-ambz] Add Wire library 2022-05-14 21:47:56 +02:00
Kuba Szczodrzyński
f4f9788f68 [github] Add board readme generated with boardgen 2022-05-14 20:20:39 +02:00
Kuba Szczodrzyński
b9cdcad262 [core] Add boardgen submodule 2022-05-14 14:21:15 +02:00
Kuba Szczodrzyński
e4cd2ddec8 [core] Split boards as base files 2022-05-10 17:48:41 +02:00
Kuba Szczodrzyński
1477649a50 [core] Move LibreTuyaAPI to core directory 2022-05-09 15:41:22 +02:00
Kuba Szczodrzyński
2e80469ab3 [core] Fix and enable SSL client support 2022-05-08 18:43:10 +02:00
Kuba Szczodrzyński
ccf63a4cdb [realtek-ambz] Add platform configs to fixups 2022-05-08 18:42:19 +02:00
Kuba Szczodrzyński
b464871f73 [realtek-ambz] Remove PolarSSL completely, fix errno 2022-05-08 18:41:56 +02:00
Kuba Szczodrzyński
e44834a95e [docs] Fix mkdocs deployment, again, again 2022-05-07 20:47:34 +02:00
Kuba Szczodrzyński
4efefc5c6c [docs] Fix mkdocs deployment, again 2022-05-07 20:38:13 +02:00
Kuba Szczodrzyński
dfd852b2db [docs] Fix mkdocs deployment 2022-05-07 20:36:41 +02:00
Kuba Szczodrzyński
afa7141ceb [docs] Add Doxygen 2022-05-07 20:35:03 +02:00
290 changed files with 19403 additions and 4654 deletions

View File

@@ -1,11 +1,12 @@
name: Deploy docs on GitHub Pages
on:
push:
branches:
- master
jobs:
build:
docs:
name: Deploy docs
runs-on: ubuntu-latest
steps:
@@ -17,5 +18,5 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: mkdocs.yml
EXTRA_PACKAGES: build-base
EXTRA_PACKAGES: build-base doxygen
REQUIREMENTS: docs/requirements.txt

7
.gitignore vendored
View File

@@ -252,3 +252,10 @@ cython_debug/
# End of https://www.toptal.com/developers/gitignore/api/c,c++,visualstudiocode,python
.vscode/settings.json
# mkdocs
xml/
ltapi/
ltambz/
hashChanges.yaml
.piopm

2
.gitmodules vendored Normal file
View File

@@ -0,0 +1,2 @@
[submodule "tools/boardgen"]
url = https://github.com/kuba2k2/boardgen

201
README.md
View File

@@ -2,26 +2,27 @@
<div align="center" markdown>
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/kuba2k2/libretuya/docs?label=docs)](https://kuba2k2.github.io/libretuya/)
![GitHub last commit](https://img.shields.io/github/last-commit/kuba2k2/libretuya)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/kuba2k2/libretuya/Deploy%20docs%20on%20GitHub%20Pages?label=docs&logo=markdown)](https://kuba2k2.github.io/libretuya/)
![GitHub last commit](https://img.shields.io/github/last-commit/kuba2k2/libretuya?logo=github)
[![Code style: clang-format](https://img.shields.io/badge/code%20style-clang--format-purple.svg)](https://github.com/psf/black)
[![Code style: clang-format](https://img.shields.io/badge/code%20style-clang--format-purple.svg)](.clang-format)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Discord](https://img.shields.io/discord/967863521511608370?color=%235865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/SyGCB9Xwtf)
[![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)
<!-- [![PlatformIO Registry](https://badges.registry.platformio.org/packages/kuba2k2/platform/libretuya.svg)](https://registry.platformio.org/platforms/kuba2k2/libretuya) -->
</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.
LibreTuya also provides a common interface for all family implementations. The interface is based on ESP32 official libraries.
**Note:** this project is work-in-progress.
@@ -34,168 +35,40 @@ LibreTuya also provides a common interface for all platform implementations. The
## 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/ LibreTuya API (interfaces + LT class) for Arduino frameworks
│ ├─ common/ Units common to all platforms
│ ├─ compat/ Fixes for compatibility with ESP32 framework
│ ├─ 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.
See [Boards & CPU list](https://kuba2k2.github.io/libretuya/docs/supported/).
## 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) | ❌ | 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:

53
SUMMARY.md Normal file
View File

@@ -0,0 +1,53 @@
* [Home](README.md)
* [💻 Boards & CPU list](docs/supported.md)
* [✔️ Implementation status](docs/implementation-status.md)
* [🔧 Configuration](docs/config.md)
* [📁 Project structure](docs/project-structure.md)
* 🔖 Code reference
* [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)
* [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_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)
* [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)
* Full documentation
* [Classes](ltapi/classes.md)
* [Functions](ltapi/functions.md)
* [Macros](ltapi/macros.md)
* [File list](ltapi/files.md)
* [✈️ OTA format](docs/ota/README.md)
* [uf2ota.py tool](docs/ota/uf2ota.md)
* [uf2ota.h library](docs/ota/library.md)
* [uf2ota.h reference](ltapi/uf2ota_8h.md)
* Families
* [Realtek - notes](docs/platform/realtek/README.md)
* [Beken 72xx - notes](docs/platform/beken-72xx/README.md)
* Realtek AmebaZ Series
* 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)
* [All supported boards](boards/)
* [🔗 Resources](docs/resources.md)

View File

@@ -0,0 +1,32 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-14. */
#pragma once
#ifdef __cplusplus
#include "WCharacterFixup.h"
#endif
#include <api/ArduinoAPI.h>
#include <core/LibreTuyaAPI.h>
// Include family-specific code
#include "WVariant.h"
// Include board variant
#include "variant.h"
#ifdef __cplusplus
#include "SerialClass.h"
#ifdef HAS_SERIAL_CLASS
// declare instances of available Serial* components
// map Serial to Serial2 if available, else to Serial1
#ifdef PIN_SERIAL1_RX
extern SerialClass Serial1;
#endif
#ifdef PIN_SERIAL2_RX
extern SerialClass Serial2;
#define Serial Serial2
#else
#define Serial Serial1
#endif
#endif
#endif

View File

@@ -0,0 +1,130 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <LibreTuyaAPI.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 <sys_ctrl.h>
#include <sys_rtos.h>
#include <wlan_ui_pub.h>
}
void LibreTuya::restart() {
bk_reboot();
}
/* 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() {
// TODO determine if this really is unique
uint32_t chipId = REG_READ(SCTRL_DEVICE_ID);
chipId &= 0xFFFFFF;
return chipId;
}
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 */
uint8_t LibreTuya::otaGetStoredIndex() {
return 1;
}
bool LibreTuya::otaSupportsDual() {
return false;
}
bool LibreTuya::otaHasImage1() {
return true;
}
bool LibreTuya::otaHasImage2() {
return false;
}
bool LibreTuya::otaSwitch(bool force) {
return true;
}
/* Global instance */
LibreTuya LT;

View File

@@ -0,0 +1,75 @@
/* 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);
}
#ifdef PIN_SERIAL1_RX
SerialClass Serial1(UART1_PORT);
#endif
#ifdef PIN_SERIAL2_RX
SerialClass Serial2(UART2_PORT);
#endif
SerialClass::SerialClass(uint8_t port) {
this->port = port;
this->buf = NULL;
}
static void callback(int port, void *param) {
RingBuffer *buf = (RingBuffer *)param;
buf->store_char(uart_read_byte(port));
}
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);
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,21 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-18. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "sdk_extern.h"
#include "sdk_mem.h"
// allow BDK use its own delay() and let Arduino code use delayMilliseconds()
void delayMilliseconds(unsigned long);
#define delay delayMilliseconds
// from fixups/arch_main.c
extern unsigned char __bk_rf_is_init;
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,25 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
extern "C" {
#include <rtos_pub.h>
#include <sys_rtos.h>
}
beken_thread_t mainThread;
bool startMainTask() {
OSStatus ret = rtos_create_thread(
&mainThread,
THD_APPLICATION_PRIORITY,
"main",
(beken_thread_function_t)main_task,
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,32 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
#include <rtos_pub.h>
#include <sys_rtos.h>
void delayMilliseconds(unsigned long ms) {
rtos_delay_milliseconds(ms);
}
void delayMicroseconds(unsigned int us) {
// TODO implement this properly
us /= 10;
volatile uint32_t i, j;
for (i = 0; i < us; i++) {
for (j = 0; j < 100; j++) {}
}
}
unsigned long millis() {
return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
unsigned long micros() {
// TODO implement this properly
return millis() * 1000;
}
void yield() {
vTaskDelay(1);
taskYIELD();
}

View File

@@ -0,0 +1,113 @@
/* 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
};
static GPIO_INDEX adcToGpio[] = {
-1, // ADC0 - VBAT
GPIO4, // ADC1
GPIO5, // ADC2
GPIO23, // ADC3
GPIO2, // ADC4
GPIO3, // ADC5
GPIO12, // ADC6
GPIO13, // ADC7
};
static uint8_t gpioToPwm(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(pwmToGpio); i++) {
if (pwmToGpio[i] == gpio)
return i;
}
return 0;
}
static uint8_t gpioToAdc(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(adcToGpio); 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);
uint32_t dutyCycle = percent * _analogWritePeriod * 26 - 1;
pwm.channel = gpioToPwm(pin->gpio);
pwm.duty_cycle = 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 = 26 * _analogWritePeriod - 1;
pwm.p_Int_Handler = NULL;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_PARAM, &pwm);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_GPIO;
pin->enabled |= PIN_PWM;
} else if (value == 0) {
// 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;
} else {
// update duty cycle
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
}
}

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) || !pinIsOutput(pin))
pinMode(pinNumber, INPUT);
// read the value
return gpio_input(pin->gpio);
}

View File

@@ -0,0 +1,56 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#include "WiFiPriv.h"
WiFiClass::WiFiClass() {
data.scanSem = xSemaphoreCreateBinary();
}
WiFiClass::~WiFiClass() {
vSemaphoreDelete(data.scanSem);
}
WiFiStatus eventTypeToStatus(uint8_t type) {
// rw_msg_pub.h:9
switch (type) {
case RW_EVT_STA_IDLE:
return WL_IDLE_STATUS;
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_NO_AP_FOUND:
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,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,23 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#pragma once
#include <Arduino.h>
extern "C" {
#include <FreeRTOS.h>
#include <semphr.h>
} // extern "C"
typedef struct {
char ssid[32 + 1];
char pass[64 + 1];
unsigned long scannedAt;
uint32_t ipSta[4];
uint32_t ipAp[4];
SemaphoreHandle_t scanSem;
void *statusIp;
void *statusLink;
} WiFiData;

View File

@@ -0,0 +1,71 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-26. */
#include "WiFiPriv.h"
bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
__wrap_bk_printf_disable();
if (mode && !data.statusIp) {
data.statusIp = (IPStatusTypedef *)malloc(sizeof(IPStatusTypedef));
data.statusLink = (LinkStatusTypeDef *)malloc(sizeof(LinkStatusTypeDef));
}
if (!__bk_rf_is_init) {
LT_D_WG("Initializing func&app");
func_init_extended();
app_pre_start();
__bk_rf_is_init = true;
}
LT_HEAP_I();
if (sta == WLMODE_ENABLE) {
LT_D_WG("Enabling STA");
bk_wlan_sta_init(NULL);
} else if (sta == WLMODE_DISABLE) {
LT_D_WG("Disabling STA");
bk_wlan_stop(BK_STATION);
}
LT_HEAP_I();
if (ap == WLMODE_ENABLE) {
LT_D_WG("Enabling AP");
bk_wlan_ap_init(NULL);
} else if (ap == WLMODE_DISABLE) {
LT_D_WG("Disabling AP");
bk_wlan_stop(BK_SOFT_AP);
}
if (!mode) {
free(data.statusIp);
free(data.statusLink);
data.statusIp = NULL;
data.statusLink = NULL;
}
LT_HEAP_I();
__wrap_bk_printf_enable();
}
WiFiMode WiFiClass::getMode() {
if (!g_wlan_general_param)
return WIFI_MODE_NULL;
uint8_t role = g_wlan_general_param->role;
// change 1->2, 2->1
return (WiFiMode)(role + (role == 1) - (role == 2));
}
WiFiStatus WiFiClass::status() {
return eventTypeToStatus(mhdr_get_station_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,57 @@
/* 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>
#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 <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;
// func/lwip_intf/lwip-2.0.2/port/net.c
extern struct netif *net_get_sta_handle(void);
extern struct netif *net_get_uap_handle(void);
// 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);
#define ADDR_STA_IP data.ipSta[0]
#define ADDR_STA_MASK data.ipSta[1]
#define ADDR_STA_GW data.ipSta[2]
#define ADDR_STA_DNS data.ipSta[3]
#define ADDR_AP_IP data.ipAp[0]
#define ADDR_AP_MASK data.ipAp[1]
#define ADDR_AP_GW data.ipAp[2]
#define ADDR_AP_DNS data.ipAp[3]
#define IP_STATUS ((IPStatusTypedef *)data.statusIp)
#define LINK_STATUS ((LinkStatusTypeDef *)data.statusLink)
} // extern "C"

View File

@@ -0,0 +1,219 @@
/* 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) {
enableSTA(true);
if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) {
LT_W("SSID not specified or too long");
return WL_CONNECT_FAILED;
}
if (passphrase && strlen(passphrase) > 64) {
LT_W("Passphrase too long");
return WL_CONNECT_FAILED;
}
// store the network data for later
strcpy(data.ssid, ssid);
if (passphrase)
strcpy(data.pass, passphrase);
else
data.pass[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) {
ADDR_STA_IP = (uint32_t)localIP;
ADDR_STA_GW = (uint32_t)gateway;
ADDR_STA_MASK = (uint32_t)subnet;
ADDR_STA_DNS = (uint32_t)dns1;
if (status() == WL_CONNECTED) {
IPStatusTypedef config;
config.dhcp = !localIP[0];
if (localIP) {
sprintf(config.ip, "%u.%u.%u.%u", ADDR_STA_IP);
sprintf(config.mask, "%u.%u.%u.%u", ADDR_STA_MASK);
sprintf(config.gate, "%u.%u.%u.%u", ADDR_STA_GW);
}
if (dns1)
sprintf(config.dns, "%u.%u.%u.%u", ADDR_STA_DNS);
bk_wlan_set_ip_status(&config, BK_STATION);
}
return true;
}
bool WiFiClass::reconnect(const uint8_t *bssid) {
if (!bssid && !data.ssid[0]) {
LT_E("(B)SSID not specified");
goto error;
}
if (bssid) {
LT_D_WG("Connecting to " MACSTR, MAC2STR(bssid));
} else {
LT_D_WG("Connecting to %s", data.ssid);
}
network_InitTypeDef_st config;
memset(&config, 0, sizeof(network_InitTypeDef_st));
// network_InitTypeDef_adv_st config;
// memset(&config, 0, sizeof(network_InitTypeDef_adv_st));
config.wifi_mode = BK_STATION;
config.wifi_retry_interval = 100;
// config.ap_info.security = BK_SECURITY_TYPE_WPA2_MIXED;
// config.ap_info.channel = 6;
strcpy(config.wifi_ssid, data.ssid);
// strcpy(config.ap_info.ssid, data.ssid);
strcpy(config.wifi_key, data.pass);
// strcpy(config.key, data.pass);
// config.key_len = strlen(data.pass);
if (bssid)
memcpy(config.wifi_bssid, bssid, 6);
// memcpy(config.ap_info.bssid, bssid, 6);
if (ADDR_STA_IP && ADDR_STA_MASK && ADDR_STA_GW) {
config.dhcp_mode = DHCP_DISABLE;
sprintf(config.local_ip_addr, "%u.%u.%u.%u", ADDR_STA_IP);
sprintf(config.net_mask, "%u.%u.%u.%u", ADDR_STA_MASK);
sprintf(config.gateway_ip_addr, "%u.%u.%u.%u", ADDR_STA_GW);
LT_D_WG("Static IP: %s / %s / %s", config.local_ip_addr, config.net_mask, config.gateway_ip_addr);
} else {
config.dhcp_mode = DHCP_CLIENT;
LT_D_WG("Using DHCP");
}
if (ADDR_STA_DNS) {
sprintf(config.dns_server_ip_addr, "%u.%u.%u.%u", ADDR_STA_DNS);
LT_D_WG("Static DNS: %s", config.dns_server_ip_addr);
}
if (!data.scannedAt || millis() - data.scannedAt > 10000) {
LT_D_WG("Scan needed");
// apparently a scan must be performed first,
// else it hangs at "[sa_sta]MM_START_REQ"
scanNetworks(false);
}
LT_D_WG("Starting WiFi...");
__wrap_bk_printf_disable();
bk_wlan_start_sta(&config);
__wrap_bk_printf_enable();
LT_D_WG("bk_wlan_start() OK");
return true;
error:
return false;
}
bool WiFiClass::disconnect(bool wifiOff) {
bk_wlan_connection_loss();
return true;
}
bool WiFiClass::setAutoReconnect(bool autoReconnect) {
return false;
}
bool WiFiClass::getAutoReconnect() {
return false;
}
IPAddress WiFiClass::localIP() {
IPStatusTypedef config;
bk_wlan_get_ip_status(&config, BK_STATION);
IPAddress ip;
ip.fromString(config.ip);
return ip;
}
IPAddress WiFiClass::subnetMask() {
IPStatusTypedef config;
bk_wlan_get_ip_status(&config, BK_STATION);
IPAddress ip;
ip.fromString(config.mask);
return ip;
}
IPAddress WiFiClass::gatewayIP() {
IPStatusTypedef config;
bk_wlan_get_ip_status(&config, BK_STATION);
IPAddress ip;
ip.fromString(config.gate);
return ip;
}
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
IPStatusTypedef config;
bk_wlan_get_ip_status(&config, BK_STATION);
IPAddress ip;
ip.fromString(config.dns);
return ip;
}
IPAddress WiFiClass::broadcastIP() {
return calculateBroadcast(localIP(), subnetMask());
}
const char *WiFiClass::getHostname() {
struct netif *ifs = net_get_sta_handle();
return netif_get_hostname(ifs);
}
bool WiFiClass::setHostname(const char *hostname) {
struct netif *ifs = 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) {
wifi_set_mac_address((char *)mac);
return true;
}
const String WiFiClass::SSID() {
bk_wlan_get_link_status(LINK_STATUS);
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() {
bk_wlan_get_link_status(LINK_STATUS);
return LINK_STATUS->bssid;
}
int32_t WiFiClass::channel() {
bk_wlan_get_link_status(LINK_STATUS);
return LINK_STATUS->channel;
}
int8_t WiFiClass::RSSI() {
bk_wlan_get_link_status(LINK_STATUS);
return LINK_STATUS->wifi_strength;
}
WiFiAuthMode WiFiClass::getEncryption() {
bk_wlan_get_link_status(LINK_STATUS);
return securityTypeToAuthMode(LINK_STATUS->security);
}

View File

@@ -0,0 +1,73 @@
/* 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_W("Called without ctx");
return;
}
WiFiScanData *scan = cls->scan;
if (!scan) {
LT_W("Called without cls->scan");
return;
}
ScanResult_adv result;
if (wlan_sta_scan_result(&result)) {
LT_E("Failed to get scan result");
goto end;
}
LT_D_WG("Found %d APs", result.ApNum);
cls->scanAlloc(result.ApNum);
if (!scan->ap) {
LT_W("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();
end:
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)
return WIFI_SCAN_RUNNING;
enableSTA(true);
scanDelete();
scanInit();
LT_I("Starting WiFi scan");
__wrap_bk_printf_disable();
mhdr_scanu_reg_cb(scanHandler, this);
bk_wlan_start_scan();
__wrap_bk_printf_enable();
LT_HEAP_I();
scan->running = true;
if (!async) {
LT_I("Waiting for results");
xSemaphoreTake(data.scanSem, 1); // reset the semaphore quickly
xSemaphoreTake(data.scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
return scan->count;
}
return WIFI_SCAN_RUNNING;
}

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,45 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#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);
static int init() {
__wrap_bk_printf_disable();
flash_init();
__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) {
flash_write((char *)buf, size, offset);
return size;
}
static int erase(long offset, size_t size) {
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,17 @@
/* 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);
}
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

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-17. */
#include "Events.h"
uint16_t EventHandler_s::lastId = 1;

View File

@@ -0,0 +1,117 @@
/*
ESP8266WiFiGeneric.h - esp8266 Wifi support.
Based on WiFi.h from Ardiono WiFi shield library.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
Reworked by Markus Sattler, December 2015
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/WiFi/WiFiEvents.h>
#include <functional>
typedef enum {
ARDUINO_EVENT_WIFI_READY = 0, /**< ESP32 WiFi ready */
ARDUINO_EVENT_WIFI_SCAN_DONE, /**< ESP32 finish scanning AP */
ARDUINO_EVENT_WIFI_STA_START, /**< ESP32 station start */
ARDUINO_EVENT_WIFI_STA_STOP, /**< ESP32 station stop */
ARDUINO_EVENT_WIFI_STA_CONNECTED, /**< ESP32 station connected to AP */
ARDUINO_EVENT_WIFI_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */
ARDUINO_EVENT_WIFI_STA_GOT_IP,
ARDUINO_EVENT_WIFI_STA_GOT_IP6,
ARDUINO_EVENT_WIFI_STA_LOST_IP,
ARDUINO_EVENT_WIFI_AP_START, /**< ESP32 soft-AP start */
ARDUINO_EVENT_WIFI_AP_STOP, /**< ESP32 soft-AP stop */
ARDUINO_EVENT_WIFI_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */
ARDUINO_EVENT_WIFI_AP_STADISCONNECTED, /**< a station disconnected from ESP32 soft-AP */
ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED,
ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED, /**< Receive probe request packet in soft-AP interface */
ARDUINO_EVENT_WIFI_AP_GOT_IP6,
ARDUINO_EVENT_WIFI_FTM_REPORT, /**< Receive report of FTM procedure */
ARDUINO_EVENT_ETH_START,
ARDUINO_EVENT_ETH_STOP,
ARDUINO_EVENT_ETH_CONNECTED,
ARDUINO_EVENT_ETH_DISCONNECTED,
ARDUINO_EVENT_ETH_GOT_IP,
ARDUINO_EVENT_ETH_GOT_IP6,
ARDUINO_EVENT_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */
ARDUINO_EVENT_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */
ARDUINO_EVENT_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */
ARDUINO_EVENT_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */
ARDUINO_EVENT_WPS_ER_PBC_OVERLAP, /**< ESP32 station wps overlap in enrollee mode */
ARDUINO_EVENT_SC_SCAN_DONE,
ARDUINO_EVENT_SC_FOUND_CHANNEL,
ARDUINO_EVENT_SC_GOT_SSID_PSWD,
ARDUINO_EVENT_SC_SEND_ACK_DONE,
ARDUINO_EVENT_PROV_INIT,
ARDUINO_EVENT_PROV_DEINIT,
ARDUINO_EVENT_PROV_START,
ARDUINO_EVENT_PROV_END,
ARDUINO_EVENT_PROV_CRED_RECV,
ARDUINO_EVENT_PROV_CRED_FAIL,
ARDUINO_EVENT_PROV_CRED_SUCCESS,
ARDUINO_EVENT_MAX
} arduino_event_id_t;
typedef union {
wifi_event_sta_scan_done_t wifi_scan_done;
wifi_event_sta_authmode_change_t wifi_sta_authmode_change;
wifi_event_sta_connected_t wifi_sta_connected;
wifi_event_sta_disconnected_t wifi_sta_disconnected;
wifi_event_sta_wps_er_pin_t wps_er_pin;
wifi_event_sta_wps_fail_reason_t wps_fail_reason;
wifi_event_ap_probe_req_rx_t wifi_ap_probereqrecved;
wifi_event_ap_staconnected_t wifi_ap_staconnected;
wifi_event_ap_stadisconnected_t wifi_ap_stadisconnected;
wifi_event_ftm_report_t wifi_ftm_report;
ip_event_ap_staipassigned_t wifi_ap_staipassigned;
ip_event_got_ip_t got_ip;
ip_event_got_ip6_t got_ip6;
// smartconfig_event_got_ssid_pswd_t sc_got_ssid_pswd;
// esp_eth_handle_t eth_connected;
// wifi_sta_config_t prov_cred_recv;
// wifi_prov_sta_fail_reason_t prov_fail_reason;
} arduino_event_info_t;
typedef struct {
arduino_event_id_t event_id;
arduino_event_info_t event_info;
} arduino_event_t;
#define EventId arduino_event_id_t
#define EventId_t arduino_event_id_t
#define EventInfo arduino_event_info_t
#define EventInfo_t arduino_event_info_t
#define Event_t arduino_event_t
typedef void (*EventCb)(EventId event);
typedef std::function<void(EventId event, EventInfo info)> EventFuncCb;
typedef void (*EventSysCb)(Event_t *event);
typedef struct EventHandler_s {
static uint16_t lastId;
uint16_t id;
EventCb cb;
EventFuncCb fcb;
EventSysCb scb;
EventId eventId;
EventHandler_s() : id(lastId++), cb(NULL), fcb(NULL), scb(NULL) {}
} EventHandler;

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

@@ -1,17 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "LibreTuyaAPI.h"
__weak char *strdup(const char *s) {
size_t len = strlen(s) + 1;
void *newp = malloc(len);
if (newp == NULL)
return NULL;
return (char *)memcpy(newp, s, len);
}
String ipToString(const IPAddress &ip) {
char szRet[16];
sprintf(szRet, "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]);
return String(szRet);
}

View File

@@ -17,7 +17,7 @@
#include <stdint.h>
#include <stdlib.h>
#include "api/String.h"
#include <api/String.h>
typedef enum {
PT_I8,

View File

@@ -1,177 +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 "WiFiType.h"
// TODO wifi events
// TODO WiFiMulti library
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);
};
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,46 @@
/* 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: ");
dest.println(SSID());
if (isConnected()) {
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());
}
}
if (getMode() & WIFI_MODE_AP) {
dest.println("-- Access Point --");
dest.print("SSID: ");
dest.println(softAPSSID());
dest.print("IP: ");
dest.println(softAPIP());
dest.print("MAC: ");
dest.println(softAPmacAddress());
dest.print("Hostname: ");
dest.println(softAPgetHostname());
}
}
WiFiClass WiFi;
WiFiClass *pWiFi = NULL;

View File

@@ -0,0 +1,194 @@
/*
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);
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

@@ -0,0 +1,84 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-17. */
#include "WiFi.h"
std::vector<EventHandler> WiFiClass::handlers;
uint16_t WiFiClass::onEvent(EventCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.cb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
uint16_t WiFiClass::onEvent(EventFuncCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.fcb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
uint16_t WiFiClass::onEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return 0;
EventHandler handler;
handler.scb = callback;
handler.eventId = eventId;
handlers.push_back(handler);
return handler.id;
}
void WiFiClass::removeEvent(EventCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.cb == callback && handler.eventId == eventId) {
handlers.erase(handlers.begin() + i);
}
}
}
void WiFiClass::removeEvent(EventSysCb callback, EventId eventId) {
if (!callback)
return;
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.scb == callback && handler.eventId == eventId) {
handlers.erase(handlers.begin() + i);
}
}
}
void WiFiClass::removeEvent(uint16_t id) {
for (uint16_t i = 0; i < handlers.size(); i++) {
EventHandler handler = handlers[i];
if (handler.id == id) {
handlers.erase(handlers.begin() + i);
}
}
}
void WiFiClass::postEvent(EventId eventId, EventInfo eventInfo) {
for (auto handler : handlers) {
if (handler.eventId != ARDUINO_EVENT_MAX && handler.eventId != eventId)
continue;
if (handler.cb) {
handler.cb(eventId);
} else if (handler.fcb) {
handler.fcb(eventId, eventInfo);
} else if (handler.scb) {
Event_t event = {
.event_id = eventId,
.event_info = eventInfo,
};
handler.scb(&event);
}
}
}

View File

@@ -0,0 +1,173 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "WiFiType.h"
/** Argument structure for WIFI_EVENT_SCAN_DONE event */
typedef struct {
uint32_t status; /**< status of scanning APs: 0 - success, 1 - failure */
uint8_t number; /**< number of scan results */
uint8_t scan_id; /**< scan sequence number, used for block scan */
} wifi_event_sta_scan_done_t;
/** Argument structure for WIFI_EVENT_STA_CONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of connected AP */
uint8_t ssid_len; /**< SSID length of connected AP */
uint8_t bssid[6]; /**< BSSID of connected AP*/
uint8_t channel; /**< channel of connected AP*/
wifi_auth_mode_t authmode; /**< authentication mode used by AP*/
} wifi_event_sta_connected_t;
/** Argument structure for WIFI_EVENT_STA_DISCONNECTED event */
typedef struct {
uint8_t ssid[32]; /**< SSID of disconnected AP */
uint8_t ssid_len; /**< SSID length of disconnected AP */
uint8_t bssid[6]; /**< BSSID of disconnected AP */
uint8_t reason; /**< reason of disconnection */
} wifi_event_sta_disconnected_t;
/** Argument structure for WIFI_EVENT_STA_AUTHMODE_CHANGE event */
typedef struct {
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
} wifi_event_sta_authmode_change_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_PIN event */
typedef struct {
uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */
} wifi_event_sta_wps_er_pin_t;
/** Argument structure for WIFI_EVENT_STA_WPS_ER_FAILED event */
typedef enum {
WPS_FAIL_REASON_NORMAL = 0, /**< ESP32 WPS normal fail reason */
WPS_FAIL_REASON_RECV_M2D, /**< ESP32 WPS receive M2D frame */
WPS_FAIL_REASON_MAX
} wifi_event_sta_wps_fail_reason_t;
#define MAX_SSID_LEN 32
#define MAX_PASSPHRASE_LEN 64
#define MAX_WPS_AP_CRED 3
/** Argument structure for WIFI_EVENT_STA_WPS_ER_SUCCESS event */
typedef struct {
uint8_t ap_cred_cnt; /**< Number of AP credentials received */
struct {
uint8_t ssid[MAX_SSID_LEN]; /**< SSID of AP */
uint8_t passphrase[MAX_PASSPHRASE_LEN]; /**< Passphrase for the AP */
} ap_cred[MAX_WPS_AP_CRED]; /**< All AP credentials received from WPS handshake */
} wifi_event_sta_wps_er_success_t;
/** Argument structure for WIFI_EVENT_AP_STACONNECTED event */
typedef struct {
uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */
uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */
bool is_mesh_child; /**< flag to identify mesh child */
} wifi_event_ap_staconnected_t;
/** Argument structure for WIFI_EVENT_AP_STADISCONNECTED event */
typedef struct {
uint8_t mac[6]; /**< MAC address of the station disconnects to ESP32 soft-AP */
uint8_t aid; /**< the aid that ESP32 soft-AP gave to the station disconnects to */
bool is_mesh_child; /**< flag to identify mesh child */
} wifi_event_ap_stadisconnected_t;
/** Argument structure for WIFI_EVENT_AP_PROBEREQRECVED event */
typedef struct {
int rssi; /**< Received probe request signal strength */
uint8_t mac[6]; /**< MAC address of the station which send probe request */
} wifi_event_ap_probe_req_rx_t;
/**
* @brief FTM operation status types
*
*/
typedef enum {
FTM_STATUS_SUCCESS = 0, /**< FTM exchange is successful */
FTM_STATUS_UNSUPPORTED, /**< Peer does not support FTM */
FTM_STATUS_CONF_REJECTED, /**< Peer rejected FTM configuration in FTM Request */
FTM_STATUS_NO_RESPONSE, /**< Peer did not respond to FTM Requests */
FTM_STATUS_FAIL, /**< Unknown error during FTM exchange */
} wifi_ftm_status_t;
/** Argument structure for */
typedef struct {
uint8_t dlog_token; /**< Dialog Token of the FTM frame */
int8_t rssi; /**< RSSI of the FTM frame received */
uint32_t rtt; /**< Round Trip Time in pSec with a peer */
uint64_t t1; /**< Time of departure of FTM frame from FTM Responder in pSec */
uint64_t t2; /**< Time of arrival of FTM frame at FTM Initiator in pSec */
uint64_t t3; /**< Time of departure of ACK from FTM Initiator in pSec */
uint64_t t4; /**< Time of arrival of ACK at FTM Responder in pSec */
} wifi_ftm_report_entry_t;
/** Argument structure for WIFI_EVENT_FTM_REPORT event */
typedef struct {
uint8_t peer_mac[6]; /**< MAC address of the FTM Peer */
wifi_ftm_status_t status; /**< Status of the FTM operation */
uint32_t rtt_raw; /**< Raw average Round-Trip-Time with peer in Nano-Seconds */
uint32_t rtt_est; /**< Estimated Round-Trip-Time with peer in Nano-Seconds */
uint32_t dist_est; /**< Estimated one-way distance in Centi-Meters */
wifi_ftm_report_entry_t
*ftm_report_data; /**< Pointer to FTM Report with multiple entries, should be freed after use */
uint8_t ftm_report_num_entries; /**< Number of entries in the FTM Report data */
} wifi_event_ftm_report_t;
#define WIFI_STATIS_BUFFER (1 << 0)
#define WIFI_STATIS_RXTX (1 << 1)
#define WIFI_STATIS_HW (1 << 2)
#define WIFI_STATIS_DIAG (1 << 3)
#define WIFI_STATIS_PS (1 << 4)
#define WIFI_STATIS_ALL (-1)
/** Argument structure for WIFI_EVENT_ACTION_TX_STATUS event */
typedef struct {
int ifx; /**< WiFi interface to send request to */
uint32_t context; /**< Context to identify the request */
uint8_t da[6]; /**< Destination MAC address */
uint8_t status; /**< Status of the operation */
} wifi_event_action_tx_status_t;
/** Argument structure for WIFI_EVENT_ROC_DONE event */
typedef struct {
uint32_t context; /**< Context to identify the request */
} wifi_event_roc_done_t;
/** Event structure for IP_EVENT_STA_GOT_IP, IP_EVENT_ETH_GOT_IP events */
typedef struct {
esp_ip4_addr_t ip; /**< Interface IPV4 address */
esp_ip4_addr_t netmask; /**< Interface IPV4 netmask */
esp_ip4_addr_t gw; /**< Interface IPV4 gateway address */
} esp_netif_ip_info_t;
/** @brief IPV6 IP address information
*/
typedef struct {
esp_ip6_addr_t ip; /**< Interface IPV6 address */
} esp_netif_ip6_info_t;
typedef struct {
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
esp_netif_ip_info_t ip_info; /*!< IP address, netmask, gatway IP address */
bool ip_changed; /*!< Whether the assigned IP has changed or not */
} ip_event_got_ip_t;
/** Event structure for IP_EVENT_GOT_IP6 event */
typedef struct {
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
void *esp_netif; /*!< Pointer to corresponding esp-netif object */
esp_netif_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
int ip_index; /*!< IPv6 address index */
} ip_event_got_ip6_t;
/** Event structure for IP_EVENT_AP_STAIPASSIGNED event */
typedef struct {
esp_ip4_addr_t ip; /*!< IP address which was assigned to the station */
} ip_event_ap_staipassigned_t;

View File

@@ -0,0 +1,93 @@
/* 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_D_WG("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));
// actually change the mode
LT_HEAP_I();
return modePriv(mode, sta, ap);
}
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;
}
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

@@ -0,0 +1,149 @@
/*
ESP8266WiFiType.h - esp8266 Wifi support.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
Reworked by Markus Sattler, December 2015
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>
#define WIFI_SCAN_RUNNING (-1)
#define WIFI_SCAN_FAILED (-2)
#define WiFiMode_t wifi_mode_t
#define WiFiMode wifi_mode_t
#define WiFiStatus wl_status_t
#define WiFiAuthMode wifi_auth_mode_t
#define WIFI_OFF WIFI_MODE_NULL
#define WIFI_STA WIFI_MODE_STA
#define WIFI_AP WIFI_MODE_AP
#define WIFI_AP_STA WIFI_MODE_APSTA
#define WiFiEvent_t arduino_event_id_t
#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;
};
struct esp_ip4_addr {
uint32_t addr;
};
typedef struct esp_ip4_addr esp_ip4_addr_t;
typedef struct esp_ip6_addr esp_ip6_addr_t;
typedef enum {
WIFI_MODE_NULL = 0, /**< null mode */
WIFI_MODE_STA, /**< WiFi station mode */
WIFI_MODE_AP, /**< WiFi soft-AP mode */
WIFI_MODE_APSTA, /**< WiFi station + soft-AP mode */
WIFI_MODE_MAX
} wifi_mode_t;
typedef enum {
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL = 1,
WL_SCAN_COMPLETED = 2,
WL_CONNECTED = 3,
WL_CONNECT_FAILED = 4,
WL_CONNECTION_LOST = 5,
WL_DISCONNECTED = 6,
} wl_status_t;
typedef enum {
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
WIFI_AUTH_WPA,
WIFI_AUTH_WPA2,
WIFI_AUTH_AUTO = 200,
WIFI_AUTH_INVALID = 255,
WIFI_AUTH_MAX
} wifi_auth_mode_t;
typedef enum {
WIFI_REASON_UNSPECIFIED = 1,
WIFI_REASON_AUTH_EXPIRE = 2,
WIFI_REASON_AUTH_LEAVE = 3,
WIFI_REASON_ASSOC_EXPIRE = 4,
WIFI_REASON_ASSOC_TOOMANY = 5,
WIFI_REASON_NOT_AUTHED = 6,
WIFI_REASON_NOT_ASSOCED = 7,
WIFI_REASON_ASSOC_LEAVE = 8,
WIFI_REASON_ASSOC_NOT_AUTHED = 9,
WIFI_REASON_DISASSOC_PWRCAP_BAD = 10,
WIFI_REASON_DISASSOC_SUPCHAN_BAD = 11,
WIFI_REASON_BSS_TRANSITION_DISASSOC = 12,
WIFI_REASON_IE_INVALID = 13,
WIFI_REASON_MIC_FAILURE = 14,
WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
WIFI_REASON_IE_IN_4WAY_DIFFERS = 17,
WIFI_REASON_GROUP_CIPHER_INVALID = 18,
WIFI_REASON_PAIRWISE_CIPHER_INVALID = 19,
WIFI_REASON_AKMP_INVALID = 20,
WIFI_REASON_UNSUPP_RSN_IE_VERSION = 21,
WIFI_REASON_INVALID_RSN_IE_CAP = 22,
WIFI_REASON_802_1X_AUTH_FAILED = 23,
WIFI_REASON_CIPHER_SUITE_REJECTED = 24,
WIFI_REASON_INVALID_PMKID = 53,
WIFI_REASON_BEACON_TIMEOUT = 200,
WIFI_REASON_NO_AP_FOUND = 201,
WIFI_REASON_AUTH_FAIL = 202,
WIFI_REASON_ASSOC_FAIL = 203,
WIFI_REASON_HANDSHAKE_TIMEOUT = 204,
WIFI_REASON_CONNECTION_FAIL = 205,
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;
uint8_t count = 0;
WiFiScanAP *ap = NULL;
} WiFiScanData;
typedef enum {
WLMODE_NONE = 0,
WLMODE_DISABLE = 1,
WLMODE_ENABLE = 2,
} WiFiModeAction;

View File

@@ -1,66 +0,0 @@
/*
ESP8266WiFiType.h - esp8266 Wifi support.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
Reworked by Markus Sattler, December 2015
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
#define WIFI_SCAN_RUNNING (-1)
#define WIFI_SCAN_FAILED (-2)
#define WiFiMode_t wifi_mode_t
#define WiFiMode wifi_mode_t
#define WiFiStatus wl_status_t
#define WiFiAuthMode wifi_auth_mode_t
#define WIFI_OFF WIFI_MODE_NULL
#define WIFI_STA WIFI_MODE_STA
#define WIFI_AP WIFI_MODE_AP
#define WIFI_AP_STA WIFI_MODE_APSTA
typedef enum {
WIFI_MODE_NULL = 0,
WIFI_MODE_STA,
WIFI_MODE_AP,
WIFI_MODE_APSTA,
WIFI_MODE_MAX,
} wifi_mode_t;
typedef enum {
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL = 1,
WL_SCAN_COMPLETED = 2,
WL_CONNECTED = 3,
WL_CONNECT_FAILED = 4,
WL_CONNECTION_LOST = 5,
WL_DISCONNECTED = 6,
} wl_status_t;
typedef enum {
WIFI_AUTH_INVALID = 255,
WIFI_AUTH_AUTO = 200,
WIFI_AUTH_OPEN = 0,
WIFI_AUTH_WEP = 1,
WIFI_AUTH_WPA = 5,
WIFI_AUTH_WPA2 = 6,
WIFI_AUTH_WPA_PSK = 2,
WIFI_AUTH_WPA2_PSK = 3,
WIFI_AUTH_WPA_WPA2_PSK = 4,
} wifi_auth_mode_t;

View File

@@ -0,0 +1,66 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-09. */
#include <api/Stream.h>
class ITwoWire : public Stream {
protected:
int8_t _sda = -1;
int8_t _scl = -1;
uint32_t _freq = 0;
void (*onRequestCallback)(void);
void (*onReceiveCallback)(int);
public:
bool begin() {
return begin(_sda, _scl, _freq);
}
bool begin(uint8_t address) {
return begin(address, _sda, _scl, _freq);
}
virtual bool setPins(int8_t sda, int8_t scl) = 0;
virtual bool begin(int8_t sda, int8_t scl, uint32_t frequency = 0) = 0;
virtual bool begin(uint8_t address, int8_t sda, int8_t scl, uint32_t frequency = 0) = 0;
virtual bool end() = 0;
virtual bool setClock(uint32_t freq) = 0;
virtual void beginTransmission(uint8_t address) = 0;
virtual uint8_t endTransmission(bool stopBit) = 0;
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
virtual size_t write(const uint8_t *data, size_t len) = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
virtual void flush() = 0;
uint32_t getClock() {
return _freq;
}
uint8_t endTransmission() {
return endTransmission(true);
}
size_t requestFrom(uint8_t address, size_t len) {
return requestFrom(address, len, true);
}
virtual size_t write(uint8_t data) {
return write(&data, 1);
}
void onReceive(void (*cb)(int)) {
onReceiveCallback = cb;
}
void onRequest(void (*cb)(void)) {
onRequestCallback = cb;
}
};

View File

@@ -0,0 +1,53 @@
/* 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 init() __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 main_task(const void *arg) {
setup();
for (;;) {
loop();
if (serialEventRun)
serialEventRun();
yield();
}
}
int main(void) {
// print a startup banner
LT_BANNER();
// initialize Arduino framework
init();
// initialize C library
__libc_init_array();
// 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
startMainTask();
while (1) {}
return 0;
}

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,29 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#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_BK7231T = 0x675A40B0, // Beken 7231T
F_BK7231N = 0x7B3EF230, // Beken 7231N
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
};

View File

@@ -0,0 +1,63 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "LibreTuyaAPI.h"
String ipToString(const IPAddress &ip) {
char szRet[16];
sprintf(szRet, "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]);
return String(szRet);
}
/**
* @brief Generate random bytes using rand().
*
* @param buf destination pointer
* @param len how many bytes to generate
*/
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)) {
data[i++] = rand();
}
if (len) {
int rem = rand();
unsigned char *pRem = (unsigned char *)&rem;
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(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,11 @@
#pragma once
// C standard libraries
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
// LibreTuya version macros
#ifndef LT_VERSION
#define LT_VERSION 1.0.0
@@ -15,7 +20,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
@@ -24,6 +32,7 @@ extern "C" {
#endif // __cplusplus
#include "lt_logger.h"
#include "lt_posix_api.h"
#ifdef __cplusplus
} // extern "C"
@@ -38,14 +47,11 @@ 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 *
void lt_rand_bytes(uint8_t *buf, size_t len);
// C functions
extern char *strdup(const char *);
// C++ only functions
#ifdef __cplusplus
String ipToString(const IPAddress &ip);
void hexdump(uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
#else
void hexdump(uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
#endif

View File

@@ -0,0 +1,132 @@
/* 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 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
}
static uint8_t otaRunningIndex = 0;
/**
* @brief Get the currently running firmware OTA index.
*/
uint8_t LibreTuya::otaGetRunning() {
if (otaRunningIndex)
return otaRunningIndex;
// otaRunningIndex will be correct even after switchOta()
return otaRunningIndex = otaGetStoredIndex();
}
/**
* @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.
*
* @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,142 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
#pragma once
#ifdef __cplusplus
#include "LibreTuyaAPI.h"
#include <core/ChipType.h>
/**
* @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();
uint32_t getCpuFreqMHz();
uint32_t getFlashChipSize();
uint8_t otaGetRunning();
uint8_t otaGetTarget();
bool otaRollback();
bool otaCanRollback();
public: /* Family-defined methods */
/**
* @brief Reboot the CPU.
*/
void restart();
/**
* @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 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.
*/
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);
};
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

@@ -44,6 +44,16 @@
#define LT_LOGLEVEL LT_LEVEL_INFO
#endif
// Free heap size debugging
#ifndef LT_LOG_HEAP
#define LT_LOG_HEAP 0
#endif
// Debug errno values using LT_ERRNO()
#ifndef LT_LOG_ERRNO
#define LT_LOG_ERRNO 0
#endif
// Per-module debugging
#ifndef LT_DEBUG_WIFI
#define LT_DEBUG_WIFI 0

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,87 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
#pragma once
#include "LibreTuyaAPI.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Run main_task & start OS kernel (family-defined)
*/
extern bool startMainTask();
/**
* @brief Main setup() and loop() task.
* Not to be called directly.
*/
extern void main_task(const void *arg);
#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

@@ -4,7 +4,7 @@
#include <Arduino.h>
#if LT_LOGGER_TASK
#if LT_LOGGER_TASK && LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
@@ -57,7 +57,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);
@@ -90,16 +90,15 @@ 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]
#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 +112,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

View File

@@ -51,6 +51,12 @@ void lt_log(const uint8_t level, const char *format, ...);
#define LT_F(...)
#endif
#if LT_LOG_HEAP
#define LT_HEAP_I() LT_I("Free heap: %u", LT_HEAP_FUNC());
#else
#define LT_HEAP_I()
#endif
// ESP32 compat
#define log_printf(...) LT_I(__VA_ARGS__)
#define log_v(...) LT_V(__VA_ARGS__)
@@ -75,6 +81,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 ets_printf(...) LT_I(__VA_ARGS__)
#define ETS_PRINTF(...) LT_I(__VA_ARGS__)
#define LT_T_MOD(module, ...) \
do { \
@@ -126,6 +134,16 @@ void lt_log(const uint8_t level, const char *format, ...);
return ret; \
}
#if LT_LOG_ERRNO
#define LT_ERRNO() \
if (errno) { \
LT_E("errno=%d", errno); \
errno = 0; \
}
#else
#define LT_ERRNO()
#endif
// 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__)

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,11 +25,13 @@
* Adapted in October 2018
*/
#if LT_ARD_HAS_WIFI
#include <Arduino.h>
#ifdef HTTPCLIENT_1_1_COMPATIBLE
#include <WiFi.h>
// #include <WiFiClientSecure.h>
#include <WiFiClientSecure.h>
#endif
// #include <StreamString.h>
@@ -63,7 +65,7 @@ class TLSTraits : public TransportTraits {
TLSTraits(const char *CAcert, const char *clicert = nullptr, const char *clikey = nullptr)
: _cacert(CAcert), _clicert(clicert), _clikey(clikey) {}
/* std::unique_ptr<WiFiClient> create() override {
std::unique_ptr<WiFiClient> create() override {
return std::unique_ptr<WiFiClient>(new WiFiClientSecure());
}
@@ -77,7 +79,7 @@ class TLSTraits : public TransportTraits {
wcs.setPrivateKey(_clikey);
}
return true;
} */
}
protected:
const char *_cacert;
@@ -1623,3 +1625,5 @@ bool HTTPClient::generateCookieString(String *cookieString) {
return found;
}
#endif // LT_ARD_HAS_WIFI

View File

@@ -33,7 +33,7 @@
#include <Arduino.h>
#include <WiFiClient.h>
// #include <WiFiClientSecure.h>
#include <WiFiClientSecure.h>
#include <memory>
/// Cookie jar support

View File

@@ -0,0 +1,31 @@
/* 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
// 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,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_V_WC("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_V_WC("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_V_WC("~LwIPClient()");
stop();
}
WiFiClient &WiFiClient::operator=(const WiFiClient &other) {
LwIPClient &LwIPClient::operator=(const LwIPClient &other) {
stop();
_connected = other._connected;
_sock = other._sock;
@@ -47,29 +69,37 @@ 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_D_WC("socket failed");
return -1;
}
if (timeout <= 0)
timeout = _timeout; // use default when -1 passed as 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;
@@ -80,7 +110,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_sec = 0;
tv.tv_usec = timeout * 1000;
tv.tv_usec = timeout * 1000; // millis -> micros
int res = lwip_connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (res < 0 && errno != EINPROGRESS) {
@@ -117,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;
@@ -146,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;
@@ -189,36 +223,38 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) {
}
}
}
LT_D_WC("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)
@@ -228,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);
@@ -239,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();
@@ -250,7 +286,7 @@ int WiFiClient::peek() {
return res;
}
void WiFiClient::flush() {
void LwIPClient::flush() {
int res;
size_t len = available();
if (!len)
@@ -269,14 +305,14 @@ void WiFiClient::flush() {
free(buf);
}
void WiFiClient::stop() {
LT_V_WC("stop()");
void LwIPClient::stop() {
LT_V_WC("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) {
@@ -320,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_D_WC("_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_D_WC("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)
@@ -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();
@@ -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

@@ -1,31 +1,33 @@
/* 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() {}
MbedTLSClient::MbedTLSClient() : WiFiClient() {
init(); // ensure the context is zero filled
}
MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {}
MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {
init(); // ensure the context is zero filled
}
MbedTLSClient::~MbedTLSClient() {
LT_V_WC("~MbedTLSClient()");
stop();
}
void MbedTLSClient::stop() {
WiFiClient::stop();
LT_V_SSL("Closing SSL connection");
LT_V_SSL("Stopping SSL");
if (_sslCfg.ca_chain) {
mbedtls_x509_crt_free(&_caCert);
@@ -36,6 +38,7 @@ void MbedTLSClient::stop() {
}
mbedtls_ssl_free(&_sslCtx);
mbedtls_ssl_config_free(&_sslCfg);
LT_HEAP_I();
}
void MbedTLSClient::init() {
@@ -46,73 +49,71 @@ void MbedTLSClient::init() {
}
int MbedTLSClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
return connect(ipToString(ip).c_str(), port, timeout) == 0;
return connect(ipToString(ip).c_str(), port, timeout);
}
int MbedTLSClient::connect(const char *host, uint16_t port, int32_t timeout) {
if (_pskIdentStr && _pskStr)
return connect(host, port, NULL, NULL, NULL, _pskIdentStr, _pskStr, _alpnProtocols) == 0;
return connect(host, port, _caCertStr, _clientCertStr, _clientKeyStr, NULL, NULL, _alpnProtocols) == 0;
return connect(host, port, timeout, NULL, NULL, NULL, _pskIdentStr, _pskStr) == 0;
return connect(host, port, timeout, _caCertStr, _clientCertStr, _clientKeyStr, NULL, NULL) == 0;
}
int MbedTLSClient::connect(
IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
) {
return connect(ipToString(ip).c_str(), port, rootCABuf, clientCert, clientKey, NULL, NULL, _alpnProtocols) == 0;
return connect(ipToString(ip).c_str(), port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
}
int MbedTLSClient::connect(
const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
) {
return connect(host, port, rootCABuf, clientCert, clientKey, NULL, NULL, _alpnProtocols) == 0;
return connect(host, port, 0, rootCABuf, clientCert, clientKey, NULL, NULL) == 0;
}
int MbedTLSClient::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) {
return connect(ipToString(ip).c_str(), port, NULL, NULL, NULL, pskIdent, psk, _alpnProtocols) == 0;
return connect(ipToString(ip).c_str(), port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
}
int MbedTLSClient::connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) {
return connect(host, port, NULL, NULL, NULL, pskIdent, psk, _alpnProtocols) == 0;
return connect(host, port, 0, NULL, NULL, NULL, pskIdent, psk) == 0;
}
static int ssl_random(void *data, unsigned char *output, size_t len) {
int *buf = (int *)output;
size_t i;
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
buf[i++] = rand();
}
if (len) {
int rem = rand();
unsigned char *pRem = (unsigned char *)&rem;
memcpy(output + i * sizeof(int), pRem, len);
}
lt_rand_bytes((uint8_t *)output, len);
return 0;
}
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_I("%04d: |%d| %s", line, level, msg);
}
int MbedTLSClient::connect(
const char *host,
uint16_t port,
int32_t timeout,
const char *rootCABuf,
const char *clientCert,
const char *clientKey,
const char *pskIdent,
const char *psk,
const char **alpnProtocols
const char *psk
) {
LT_D_SSL("Free heap before TLS: TODO");
LT_HEAP_I();
if (!rootCABuf && !pskIdent && !psk && !_insecure && !_useRootCA)
return -1;
if (timeout <= 0)
timeout = _timeout; // use default when -1 passed as timeout
IPAddress addr = WiFi.hostByName(host);
if (!(uint32_t)addr)
return -1;
int ret = WiFiClient::connect(addr, port, _timeout);
int ret = WiFiClient::connect(addr, port, timeout);
if (ret < 0) {
LT_E("SSL socket failed");
return ret;
@@ -122,6 +123,7 @@ int MbedTLSClient::connect(
LT_V_SSL("Init SSL");
init();
LT_HEAP_I();
// mbedtls_debug_set_threshold(4);
// mbedtls_ssl_conf_dbg(&_sslCfg, debug_cb, NULL);
@@ -135,8 +137,8 @@ int MbedTLSClient::connect(
LT_RET_NZ(ret);
#ifdef MBEDTLS_SSL_ALPN
if (alpnProtocols) {
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, alpnProtocols);
if (_alpnProtocols) {
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, _alpnProtocols);
LT_RET_NZ(ret);
}
#endif
@@ -208,10 +210,13 @@ int MbedTLSClient::connect(
_sockTls = fd();
mbedtls_ssl_set_bio(&_sslCtx, &_sockTls, mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_net_set_nonblock((mbedtls_net_context *)&_sockTls);
LT_HEAP_I();
LT_V_SSL("SSL handshake");
if (_handshakeTimeout == 0)
_handshakeTimeout = _timeout * 1000;
_handshakeTimeout = timeout;
unsigned long start = millis();
while (ret = mbedtls_ssl_handshake(&_sslCtx)) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
@@ -224,6 +229,8 @@ int MbedTLSClient::connect(
delay(2);
}
LT_HEAP_I();
if (clientCert && clientKey) {
LT_D_SSL(
"Protocol %s, ciphersuite %s",
@@ -441,3 +448,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:
@@ -42,17 +39,18 @@ class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
int connect(
const char *host,
uint16_t port,
int32_t timeout,
const char *rootCABuf,
const char *clientCert,
const char *clientKey,
const char *pskIdent,
const char *psk,
const char **alpnProtocols
const char *psk
);
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,66 @@
/**
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) {
const unsigned int newlen = len + size;
if(reserve(newlen + 1)) {
memcpy((void *) (buffer + len), (const void *) data, size);
changeBuffer(newlen);
*(buffer + newlen) = 0x00; // add null for string end
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,203 @@
/* 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();
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
info = uf2_info_init();
if (!size)
return errorArd(UPDATE_ERROR_SIZE);
if (command != U_FLASH)
return errorArd(UPDATE_ERROR_BAD_ARGUMENT);
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
return errorArd(UPDATE_ERROR_ABORT);
}
// TODO what is evenIfRemaining for?
if (!LT.otaSwitch())
// try to activate the second OTA
return errorArd(UPDATE_ERROR_ACTIVATE);
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;
/* 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;
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError())
// return on errors
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
errorArd(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
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;
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 (errorUf2(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 (errorUf2(uf2_parse_header(ctx, block, info)))
// header is invalid
return 0;
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
return errorArd(UPDATE_ERROR_SIZE);
}
} else {
// write data blocks normally
if (errorUf2(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,150 @@
#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();
bool errorUf2(uf2_err_t err);
bool errorArd(uint8_t err);
void bufAlloc();
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;
}
void clearError() {
errorUf2(UF2_ERR_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,162 @@
/* 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() {
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;
errUf2 = UF2_ERR_OK;
errArd = UPDATE_ERROR_OK;
}
/**
* @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::errorUf2(uf2_err_t err) {
if (err <= UF2_ERR_IGNORE)
return false;
cleanup();
errUf2 = err;
errArd = errorMap[err];
return true;
}
/**
* @brief Set errUf2 and errArd according to given Arduino error code.
* Abort the update.
* Use like: "return errorArd(...);"
* @return false - always
*/
bool UpdateClass::errorArd(uint8_t err) {
cleanup();
errUf2 = UF2_ERR_OK;
errArd = err;
return false;
}
/**
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
errorArd(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 Get string representation of the error in format
* "ard=..,uf2=..". Returns "" if no error.
*/
const char *UpdateClass::errorString() {
if (!errArd)
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

@@ -0,0 +1,32 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_err_t uf2_binpatch(uint8_t *data, const uint8_t *binpatch, uint8_t binpatch_len) {
const uint8_t *binpatch_end = binpatch + binpatch_len;
// +2 to make sure opcode and length is present
while ((binpatch + 2) < binpatch_end) {
uf2_opcode_t opcode = binpatch[0];
uint8_t len = binpatch[1];
switch (opcode) {
case UF2_OPC_DIFF32:
uf2_binpatch_diff32(data, binpatch + 1);
break;
}
// advance by opcode + length + data
binpatch += len + 2;
}
return UF2_ERR_OK;
}
void uf2_binpatch_diff32(uint8_t *data, const uint8_t *patch) {
uint8_t num_offs = patch[0] - 4; // read offset count
uint32_t diff = *((uint32_t *)(patch + 1)); // read diff value
patch += 5; // skip num_offs and diff value
for (uint8_t i = 0; i < num_offs; i++) {
// patch the data
uint8_t offs = patch[i];
uint32_t *value = (uint32_t *)(data + offs);
*(value) += diff;
}
}

View File

@@ -0,0 +1,26 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#pragma once
#include "uf2types.h"
/**
* @brief Apply binary patch to data.
*
* @param data input data
* @param data_len input data length
* @param binpatch binary patch data
* @param binpatch_len binary patch data length
* @return uf2_err_t error code
*/
uf2_err_t uf2_binpatch(uint8_t *data, const uint8_t *binpatch, uint8_t binpatch_len);
/**
* Apply DIFF32 binary patch.
*
* @param data input data
* @param len input data length
* @param patch patch data, incl. length byte
* @return uf2_err_t error code
*/
void uf2_binpatch_diff32(uint8_t *data, const uint8_t *patch);

View File

@@ -0,0 +1,100 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_ota_t *uf2_ctx_init(uint8_t ota_idx, uint32_t family_id) {
uf2_ota_t *ctx = (uf2_ota_t *)zalloc(sizeof(uf2_ota_t));
ctx->ota_idx = ota_idx;
ctx->family_id = family_id;
return ctx;
}
uf2_info_t *uf2_info_init() {
uf2_info_t *info = (uf2_info_t *)zalloc(sizeof(uf2_info_t));
return info;
}
void uf2_info_free(uf2_info_t *info) {
if (!info)
return;
free(info->fw_name);
free(info->fw_version);
free(info->lt_version);
free(info->board);
free(info);
}
uf2_err_t uf2_check_block(uf2_ota_t *ctx, uf2_block_t *block) {
if (block->magic1 != UF2_MAGIC_1)
return UF2_ERR_MAGIC;
if (block->magic2 != UF2_MAGIC_2)
return UF2_ERR_MAGIC;
if (block->magic3 != UF2_MAGIC_3)
return UF2_ERR_MAGIC;
if (block->file_container)
// ignore file containers, for now
return UF2_ERR_IGNORE;
if (!block->has_family_id || block->file_size != ctx->family_id)
// require family_id
return UF2_ERR_FAMILY;
return UF2_ERR_OK;
}
uf2_err_t uf2_parse_header(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info) {
if (!block->has_tags || block->file_container || block->len)
// header must have tags and no data
return UF2_ERR_NOT_HEADER;
uf2_err_t err = uf2_parse_block(ctx, block, info);
if (err)
return err;
if ((ctx->ota_idx == 1 && !ctx->has_ota1) || !ctx->has_ota2)
return UF2_ERR_OTA_WRONG;
return UF2_ERR_OK;
}
uf2_err_t uf2_write(uf2_ota_t *ctx, uf2_block_t *block) {
if (ctx->seq == 0)
return uf2_parse_header(ctx, block, NULL);
if (block->not_main_flash || !block->len)
// ignore blocks not meant for flashing
return UF2_ERR_IGNORE;
uf2_err_t err = uf2_parse_block(ctx, block, NULL);
if (err)
return err;
if (!ctx->part1 && !ctx->part2)
// no partitions set at all
return UF2_ERR_PART_UNSET;
fal_partition_t part = uf2_get_target_part(ctx);
if (!part)
// image is not for current OTA scheme
return UF2_ERR_IGNORE;
if (ctx->ota_idx == 2 && ctx->binpatch_len) {
// apply binpatch
err = uf2_binpatch(block->data, ctx->binpatch, ctx->binpatch_len);
if (err)
return err;
}
int ret;
// erase sectors if needed
if (!uf2_is_erased(ctx, block->addr, block->len)) {
ret = fal_partition_erase(part, block->addr, block->len);
if (ret < 0)
return UF2_ERR_ERASE_FAILED;
ctx->erased_offset = block->addr;
ctx->erased_length = ret;
}
// write data to flash
ret = fal_partition_write(part, block->addr, block->data, block->len);
if (ret < 0)
return UF2_ERR_WRITE_FAILED;
if (ret != block->len)
return UF2_ERR_WRITE_LENGTH;
return UF2_ERR_OK;
}

View File

@@ -0,0 +1,68 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "uf2types.h"
/**
* @brief Create an UF2 OTA context.
*
* @param ota_idx target OTA index
* @param family_id expected family ID
* @return uf2_ota_t* heap-allocated structure
*/
uf2_ota_t *uf2_ctx_init(uint8_t ota_idx, uint32_t family_id);
/**
* @brief Create an UF2 Info structure.
*
* @return uf2_info_t* heap-allocated structure
*/
uf2_info_t *uf2_info_init();
/**
* @brief Free values in the info structure AND the structure itself.
*
* @param info structure to free; may be NULL
*/
void uf2_info_free(uf2_info_t *info);
/**
* @brief Check if block is valid.
*
* @param ctx context
* @param block block to check
* @return uf2_err_t error code; UF2_ERR_OK and UF2_ERR_IGNORE denote valid blocks
*/
uf2_err_t uf2_check_block(uf2_ota_t *ctx, uf2_block_t *block);
/**
* @brief Parse header block (LibreTuya UF2 first block).
*
* Note: caller should call uf2_check_block() first.
*
* @param ctx context
* @param block block to parse
* @param info structure to write firmware info, NULL if not used
* @return uf2_err_t error code
*/
uf2_err_t uf2_parse_header(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info);
/**
* @brief Write the block to flash memory.
*
* Note: caller should call uf2_check_block() first.
*
* @param ctx context
* @param block block to write
* @return uf2_err_t error code
*/
uf2_err_t uf2_write(uf2_ota_t *ctx, uf2_block_t *block);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,146 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-29. */
#include "uf2priv.h"
uf2_err_t uf2_parse_block(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info) {
if (block->block_seq != ctx->seq)
// sequence number must match
return UF2_ERR_SEQ_MISMATCH;
ctx->seq++; // increment sequence number after checking it
if (!block->has_tags)
// no tags in this block, no further processing needed
return UF2_ERR_OK;
if (block->len > (476 - 4 - 4))
// at least one tag + last tag must fit
return UF2_ERR_DATA_TOO_LONG;
uint8_t *tags_start = block->data + block->len;
uint8_t tags_len = 476 - block->len;
uint8_t tags_pos = 0;
if (block->has_md5)
tags_len -= 24;
ctx->binpatch_len = 0; // binpatch applies to one block only
char *part1 = NULL;
char *part2 = NULL;
uf2_tag_type_t type;
while (tags_pos < tags_len) {
uint8_t len = uf2_read_tag(tags_start + tags_pos, &type);
if (!len)
break;
tags_pos += 4; // skip tag header
uint8_t *tag = tags_start + tags_pos;
char **str_dest = NULL; // char* to copy the tag into
switch (type) {
case UF2_TAG_OTA_VERSION:
if (tag[0] != 1)
return UF2_ERR_OTA_VER;
break;
case UF2_TAG_FIRMWARE:
if (info)
str_dest = &(info->fw_name);
break;
case UF2_TAG_VERSION:
if (info)
str_dest = &(info->fw_version);
break;
case UF2_TAG_LT_VERSION:
if (info)
str_dest = &(info->lt_version);
break;
case UF2_TAG_BOARD:
if (info)
str_dest = &(info->board);
break;
case UF2_TAG_HAS_OTA1:
ctx->has_ota1 = tag[0];
break;
case UF2_TAG_HAS_OTA2:
ctx->has_ota2 = tag[0];
break;
case UF2_TAG_PART_1:
str_dest = &(part1);
break;
case UF2_TAG_PART_2:
str_dest = &(part2);
break;
case UF2_TAG_BINPATCH:
ctx->binpatch = tag;
ctx->binpatch_len = len;
break;
default:
break;
}
if (str_dest) {
*str_dest = (char *)zalloc(len + 1);
memcpy(*str_dest, tag, len);
}
// align position to 4 bytes
tags_pos += (((len - 1) / 4) + 1) * 4;
}
if (part1 && part2) {
// update current target partition
uf2_err_t err = uf2_update_parts(ctx, part1, part2);
if (err)
return err;
} else if (part1 || part2) {
// only none or both partitions can be specified
return UF2_ERR_PART_ONE;
}
return UF2_ERR_OK;
}
uint8_t uf2_read_tag(const uint8_t *data, uf2_tag_type_t *type) {
uint8_t len = data[0];
if (!len)
return 0;
uint32_t tag_type = *((uint32_t *)data);
if (!tag_type)
return 0;
*type = tag_type >> 8; // remove tag length byte
return len - 4;
}
uf2_err_t uf2_update_parts(uf2_ota_t *ctx, char *part1, char *part2) {
// reset both target partitions
ctx->part1 = NULL;
ctx->part2 = NULL;
// reset offsets as they probably don't apply to this partition
ctx->erased_offset = 0;
ctx->erased_length = 0;
if (part1[0]) {
ctx->part1 = (fal_partition_t)fal_partition_find(part1);
if (!ctx->part1)
return UF2_ERR_PART_404;
}
if (part2[0]) {
ctx->part2 = (fal_partition_t)fal_partition_find(part2);
if (!ctx->part2)
return UF2_ERR_PART_404;
}
return UF2_ERR_OK;
}
fal_partition_t uf2_get_target_part(uf2_ota_t *ctx) {
if (ctx->ota_idx == 1)
return ctx->part1;
if (ctx->ota_idx == 2)
return ctx->part2;
return NULL;
}
bool uf2_is_erased(uf2_ota_t *ctx, uint32_t offset, uint32_t length) {
uint32_t erased_end = ctx->erased_offset + ctx->erased_length;
uint32_t end = offset + length;
return (offset >= ctx->erased_offset) && (end <= erased_end);
}

View File

@@ -0,0 +1,61 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
// include family stdlib APIs
#include <WVariant.h>
#include "uf2binpatch.h"
#include "uf2types.h"
/**
* @brief Parse a block and extract information from tags.
*
* @param ctx context
* @param block block to parse
* @param info structure to write firmware info, NULL if not used
* @return uf2_err_t error code
*/
uf2_err_t uf2_parse_block(uf2_ota_t *ctx, uf2_block_t *block, uf2_info_t *info);
/**
* @brief Parse a tag.
*
* @param data pointer to tag header beginning
* @param type [out] parsed tag type
* @return uint8_t parsed tag data length (excl. header); 0 if invalid/last tag
*/
uint8_t uf2_read_tag(const uint8_t *data, uf2_tag_type_t *type);
/**
* @brief Update destination partitions in context.
*
* Partition names cannot be NULL.
*
* Returns UF2_ERR_IGNORE if specified partitions don't match the
* current OTA index.
*
* @param ctx context
* @param part1 partition 1 name or empty string
* @param part2 partition 2 name or empty string
* @return uf2_err_t error code
*/
uf2_err_t uf2_update_parts(uf2_ota_t *ctx, char *part1, char *part2);
/**
* @brief Get target flashing partition, depending on OTA index.
*
* @param ctx context
* @return fal_partition_t target partition or NULL if not set
*/
fal_partition_t uf2_get_target_part(uf2_ota_t *ctx);
/**
* Check if specified flash memory region was already erased during update.
*
* @param ctx context
* @param offset offset to check
* @param length length to check
* @return bool true/false
*/
bool uf2_is_erased(uf2_ota_t *ctx, uint32_t offset, uint32_t length);

View File

@@ -0,0 +1,104 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <fal.h>
#define UF2_MAGIC_1 0x0A324655
#define UF2_MAGIC_2 0x9E5D5157
#define UF2_MAGIC_3 0x0AB16F30
#define UF2_BLOCK_SIZE sizeof(uf2_block_t)
typedef struct __attribute__((packed)) {
// 32 byte header
uint32_t magic1;
uint32_t magic2;
// flags split as bitfields
bool not_main_flash : 1;
uint16_t dummy1 : 11;
bool file_container : 1;
bool has_family_id : 1;
bool has_md5 : 1;
bool has_tags : 1;
uint16_t dummy2 : 16;
uint32_t addr;
uint32_t len;
uint32_t block_seq;
uint32_t block_count;
uint32_t file_size; // or familyID;
uint8_t data[476];
uint32_t magic3;
} uf2_block_t;
typedef struct {
uint32_t seq; // current block sequence number
uint8_t *binpatch; // current block's binpatch (if any) -> pointer inside block->data
uint8_t binpatch_len; // binpatch length
bool has_ota1; // image has any data for OTA1
bool has_ota2; // image has any data for OTA2
uint8_t ota_idx; // target OTA index
uint32_t family_id; // expected family ID
uint32_t erased_offset; // offset of region erased during update
uint32_t erased_length; // length of erased region
fal_partition_t part1; // OTA1 target partition
fal_partition_t part2; // OTA2 target partition
} uf2_ota_t;
typedef struct {
char *fw_name;
char *fw_version;
char *lt_version;
char *board;
} uf2_info_t;
typedef enum {
UF2_TAG_VERSION = 0x9FC7BC, // version of firmware file - UTF8 semver string
UF2_TAG_PAGE_SIZE = 0x0BE9F7, // page size of target device (32 bit unsigned number)
UF2_TAG_SHA2 = 0xB46DB0, // SHA-2 checksum of firmware (can be of various size)
UF2_TAG_DEVICE = 0x650D9D, // description of device (UTF8)
UF2_TAG_DEVICE_ID = 0xC8A729, // device type identifier
// LibreTuya custom, tags
UF2_TAG_OTA_VERSION = 0x5D57D0, // format version
UF2_TAG_BOARD = 0xCA25C8, // board name (lowercase code)
UF2_TAG_FIRMWARE = 0x00DE43, // firmware description / name
UF2_TAG_BUILD_DATE = 0x822F30, // build date/time as Unix timestamp
UF2_TAG_LT_VERSION = 0x59563D, // LT version (semver)
UF2_TAG_PART_1 = 0x805946, // OTA1 partition name
UF2_TAG_PART_2 = 0xA1E4D7, // OTA2 partition name
UF2_TAG_HAS_OTA1 = 0xBBD965, // image has any data for OTA1
UF2_TAG_HAS_OTA2 = 0x92280E, // image has any data for OTA2
UF2_TAG_BINPATCH = 0xB948DE, // binary patch to convert OTA1->OTA2
} uf2_tag_type_t;
typedef enum {
UF2_OPC_DIFF32 = 0xFE,
} uf2_opcode_t;
typedef enum {
UF2_ERR_OK = 0,
UF2_ERR_IGNORE, // block should be ignored
UF2_ERR_MAGIC, // wrong magic numbers
UF2_ERR_FAMILY, // family ID mismatched
UF2_ERR_NOT_HEADER, // block is not a header
UF2_ERR_OTA_VER, // unknown/invalid OTA format version
UF2_ERR_OTA_WRONG, // no data for current OTA index
UF2_ERR_PART_404, // no partition with that name
UF2_ERR_PART_ONE, // only one partition tag in a block
UF2_ERR_PART_UNSET, // image broken - attempted to write without target partition
UF2_ERR_DATA_TOO_LONG, // data too long - tags won't fit
UF2_ERR_SEQ_MISMATCH, // sequence number mismatched
UF2_ERR_ERASE_FAILED, // erasing flash failed
UF2_ERR_WRITE_FAILED, // writing to flash failed
UF2_ERR_WRITE_LENGTH, // wrote fewer data than requested
} uf2_err_t;

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>
@@ -203,11 +205,14 @@ 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());
// TODO fix this after implementing IP format for printf()
ip = WiFi.localIP();
LT_D("IP: %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
LT_D("MAC: %s", WiFi.BSSIDstr().c_str());
LT_D("Channel: %d", WiFi.channel());
break;
@@ -236,3 +241,5 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) {
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

@@ -0,0 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-23. */
#pragma once
#include "mDNS.h"

View File

@@ -0,0 +1,154 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-23. */
#ifdef LT_HAS_LWIP2
#include "mDNS.h"
extern "C" {
#include <lwip/apps/mdns.h>
#include <lwip/netif.h>
}
static u8_t mdns_netif_client_id = 0; // TODO fix this
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";
mDNS::mDNS() {}
mDNS::~mDNS() {}
bool mDNS::begin(const char *hostname) {
mdns_resp_init();
struct netif *netif = netif_list;
uint8_t enabled = 0;
while (netif != NULL) {
// TODO: detect mdns_netif_client_id by checking netif_get_client_data()
// and finding the requested hostname in struct mdns_host
if (netif_is_up(netif) && mdns_resp_add_netif(netif, hostname, 255) == ERR_OK) {
enabled++;
}
netif = netif->next;
}
return enabled > 0;
}
void mDNS::end() {
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif))
mdns_resp_remove_netif(netif);
netif = netif->next;
}
}
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;
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);
}
netif = netif->next;
}
}
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;
}
}
}
netif = netif->next;
}
free(txt);
return true;
}
MDNSResponder MDNS;
#endif

View File

@@ -0,0 +1,119 @@
/*
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
Version 1.1
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com)
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
Rewritten for ESP32 by Hristo Gochkov (hristo@espressif.com)
This is a simple implementation of multicast DNS query support for an Arduino
running on ESP32 chip.
Usage:
- Include the ESP32 Multicast DNS library in the sketch.
- Call the begin method in the sketch's setup and provide a domain name (without
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
for the DNS record--the default is 1 hour.
- Call the update method in each iteration of the sketch's loop function.
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once
#include <Arduino.h>
#include <api/IPv6Address.h>
class mDNS {
public:
mDNS();
~mDNS();
bool begin(const char *hostname);
void end();
void setInstanceName(String name);
bool addService(char *service, char *proto, uint16_t port);
bool addServiceTxt(char *name, char *proto, char *key, char *value);
// void enableArduino(uint16_t port = 3232, bool auth = false);
// void disableArduino();
// void enableWorkstation(esp_interface_t interface = ESP_IF_WIFI_STA);
// void disableWorkstation();
IPAddress queryHost(char *host, uint32_t timeout = 2000);
int queryService(char *service, char *proto);
String hostname(int idx);
IPAddress IP(int idx);
IPv6Address IPv6(int idx);
uint16_t port(int idx);
int numTxt(int idx);
bool hasTxt(int idx, const char *key);
String txt(int idx, const char *key);
String txt(int idx, int txtIdx);
String txtKey(int idx, int txtIdx);
void setInstanceName(const char *name) {
setInstanceName(String(name));
}
void setInstanceName(char *name) {
setInstanceName(String(name));
}
bool addService(const char *service, const char *proto, uint16_t port) {
return addService((char *)service, (char *)proto, port);
}
bool addService(String service, String proto, uint16_t port) {
return addService(service.c_str(), proto.c_str(), port);
}
void addServiceTxt(const char *name, const char *proto, const char *key, const char *value) {
addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value);
}
void addServiceTxt(String name, String proto, String key, String value) {
addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
}
IPAddress queryHost(const char *host, uint32_t timeout = 2000) {
return queryHost((char *)host, timeout);
}
IPAddress queryHost(String host, uint32_t timeout = 2000) {
return queryHost(host.c_str(), timeout);
}
int queryService(const char *service, const char *proto) {
return queryService((char *)service, (char *)proto);
}
int queryService(String service, String proto) {
return queryService(service.c_str(), proto.c_str());
}
};
typedef mDNS MDNSResponder;
extern MDNSResponder MDNS;

View File

@@ -0,0 +1,38 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-24. */
#pragma once
inline void printf_nop(const char *fmt, ...) {}
#define FAL_PRINTF printf_nop
#define FAL_DEBUG 0
// Flash device configuration
extern const struct fal_flash_dev flash0;
#define FAL_FLASH_DEV_NAME "flash0"
#define FAL_FLASH_DEV_TABLE \
{ &flash0, }
#define FAL_DEV_NAME_MAX 16 // no need for 24 chars (default)
// Partition table
#define FAL_PART_HAS_TABLE_CFG
#define FAL_PART_TABLE_ITEM(part_lower, part_upper) \
{ \
.magic_word = FAL_PART_MAGIC_WORD, /* magic word */ \
.name = #part_lower, /* lowercase name as string */ \
.flash_name = FAL_FLASH_DEV_NAME, /* flash device name */ \
.offset = FLASH_##part_upper##_OFFSET, /* partition offset macro as uppercase string */ \
.len = FLASH_##part_upper##_LENGTH, /* partition length macro as uppercase string */ \
},
// for fal_partition_t
#include <fal_def.h>
/**
* @brief Root partition table, representing the entire flash.
*/
extern fal_partition_t fal_root_part;

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