105 Commits

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

* clang format

* PR comments

* Update wiring_irq.c

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

* Update mkdocs.yml

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

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

* Fix comment formatting

* Cover case when millis overflows

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

* renamed to cammelCase, moved #defines and clang-formatted
2022-10-03 15:14:31 +02:00
Kuba Szczodrzyński
18653781d5 [beken-72xx] Replace delay() define with inline function 2022-10-03 15:12:15 +02:00
Kuba Szczodrzyński
2a75bdbeda [beken-72xx] Update TLV and calibration flash layout 2022-10-01 15:01:38 +02:00
Kuba Szczodrzyński
4a6926eb6c [beken-72xx] Fix restarting AP with shorter password 2022-10-01 15:00:08 +02:00
Kuba Szczodrzyński
3dc4007fa7 [core] Update ltchiptool to fix realtek-ambz OTA issue 2022-09-30 18:42:09 +02:00
Kuba Szczodrzyński
9c3833d66c [realtek-ambz] Provide unknown reset reason 2022-09-27 12:53:08 +02:00
Kuba Szczodrzyński
41966f33c2 [github] Add FUNDING.yml 2022-09-25 22:26:25 +02:00
Kuba Szczodrzyński
50fea2a8f0 [boards] Add RTL8710BX board with 4 MiB flash 2022-09-23 12:25:30 +02:00
Kuba Szczodrzyński
05cb7f7947 [realtek-ambz] Implement watchdog API 2022-09-21 21:46:11 +02:00
Kuba Szczodrzyński
5b6b80e3cd [tools] Migrate to bk7231tools from PyPI 2022-09-21 21:41:08 +02:00
Kuba Szczodrzyński
2c59af8399 [docs] Add Beken flash support list 2022-09-21 21:24:59 +02:00
Kuba Szczodrzyński
b404f9fa42 [core] Append revision info to version string 2022-09-15 19:59:34 +02:00
Kuba Szczodrzyński
8dc8340ce4 [realtek-ambz] Use GCC 5.2.1 to fix ranlib missing 2022-09-15 18:37:19 +02:00
Kuba Szczodrzyński
30f08a145e [realtek-ambz] Export SoftwareSerial availability macro 2022-09-15 18:31:20 +02:00
Kuba Szczodrzyński
d3d140fa60 [builder] Fix Python lint 2022-09-15 17:38:08 +02:00
Kuba Szczodrzyński
5f8e687e68 [core] Fix getResetReasonName() API 2022-09-15 14:51:14 +02:00
Kuba Szczodrzyński
fab640901d [core] Add textual reset reason API 2022-09-15 13:55:16 +02:00
Kuba Szczodrzyński
9a053fb6ff [core] Export LT class as ESP alias 2022-09-15 13:53:12 +02:00
Kuba Szczodrzyński
24e4b0b376 [builder] Use ranlib from the toolchain 2022-09-15 12:16:22 +02:00
Kuba Szczodrzyński
b8eee999e2 [docs] Add remaining config options, add warning about latest version 2022-09-15 11:47:25 +02:00
Kuba Szczodrzyński
7fe9a114b7 [beken-72xx] Auto-reboot after detecting download-mode commands 2022-09-14 19:57:23 +02:00
Kuba Szczodrzyński
05b2fb4fcb [beken-72xx] Implement micros() properly; use high-res timer for OS 2022-09-14 19:56:51 +02:00
Kuba Szczodrzyński
830d8762cd [beken-72xx] Disable watchdog feeding in BDK 2022-09-14 19:55:10 +02:00
Kuba Szczodrzyński
3f81666369 [beken-72xx] Implement watchdog & reset reason APIs 2022-09-13 17:42:13 +02:00
Kuba Szczodrzyński
d40f4003b5 [boards] Add FCC ID to board docs 2022-09-11 16:39:15 +02:00
Kuba Szczodrzyński
1585e425ce [boards] Add CB2L board 2022-09-11 16:20:48 +02:00
Kuba Szczodrzyński
bbc7613079 [boards] Add remaining CB3x series boards 2022-09-11 14:26:02 +02:00
Kuba Szczodrzyński
ec54745174 [docs] Update Tuya all boards list 2022-09-11 14:24:51 +02:00
Kuba Szczodrzyński
a29453de12 [docs] Add 3-rd party libs used in ESPHome 2022-09-10 19:08:57 +02:00
Kuba Szczodrzyński
db5bf4fc46 [docs] Add dumping instructions 2022-09-10 18:24:10 +02:00
Kuba Szczodrzyński
9495e3418c [beken-72xx] Fake enabling AP to fix connection issues, set default IP 2022-09-10 16:44:45 +02:00
Kuba Szczodrzyński
83573a743b [core] Implement WiFiUDP based on lwIP 2022-09-10 16:15:28 +02:00
Kuba Szczodrzyński
f5dcd82747 [core] Add printf format attribute to lt_log() 2022-09-10 16:14:38 +02:00
Kuba Szczodrzyński
070ec2f48f [core] Update ArduinoCore-API to support Print::printf() 2022-09-10 16:14:10 +02:00
Kuba Szczodrzyński
96c7fdc02c [beken-72xx] Reset scan state after timeout 2022-09-09 20:21:33 +02:00
Kuba Szczodrzyński
dfde2d8a62 [release] v0.10.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-09-03 21:40:04 +02:00
Kuba Szczodrzyński
a3f57114da [core] Update ltchiptool to v1.5.2 2022-09-03 21:38:46 +02:00
Kuba Szczodrzyński
904af10914 [core] Implement POSIX gettimeofday() and settimeofday() 2022-09-03 21:26:07 +02:00
Kuba Szczodrzyński
9b5013a694 [beken-72xx] Move periodic heap logging to core 2022-09-03 21:25:39 +02:00
Kuba Szczodrzyński
922adfd3d4 [core] Print full error details in Update library 2022-09-03 21:24:46 +02:00
Kuba Szczodrzyński
362144033b [core] Move main.cpp to core directory 2022-09-03 20:01:24 +02:00
Kuba Szczodrzyński
caf9a579d3 [beken-72xx] Fix writing 0% and 100% with PWM 2022-09-03 19:01:17 +02:00
Kuba Szczodrzyński
cb40fdcdbe [beken-72xx] Unprotect flash before writing 2022-09-03 17:50:36 +02:00
Kuba Szczodrzyński
ef15e754c9 [core] Make LT API usable from C 2022-09-03 17:49:19 +02:00
Kuba Szczodrzyński
d55568c146 [beken-72xx] Fix PWM on BK7231N 2022-09-02 23:58:38 +02:00
Kuba Szczodrzyński
e134863db1 [realtek-ambz] Fix mDNS compilation problem on lwIP < 2.1.0 2022-09-02 21:11:12 +02:00
Kuba Szczodrzyński
c37ae51dd3 [core] Update default logging options, fix disabling logger 2022-09-02 14:35:21 +02:00
Kuba Szczodrzyński
4fc2ff43c1 [core] Make info, warning and error logging per-module configurable 2022-09-02 14:12:05 +02:00
Kuba Szczodrzyński
607f13d935 [core] Refactor per-module logger macros 2022-09-02 14:02:00 +02:00
Kuba Szczodrzyński
44c1a3f695 [beken-72xx] Fix digitalRead() setting wrong pin mode 2022-09-01 21:13:04 +02:00
Kuba Szczodrzyński
d30decfbc8 [core] Fix lwipopts.h invalid include 2022-09-01 19:09:19 +02:00
Kuba Szczodrzyński
705b2f794e [beken-72xx] Increase TCP/IP stack size to fix mDNS stack overflow 2022-08-31 22:39:49 +02:00
Kuba Szczodrzyński
4958690d9e [core] Make lwIP debugging configurable 2022-08-31 22:38:07 +02:00
Kuba Szczodrzyński
d7749d3a24 [core] Export Update combined error code 2022-08-31 22:28:47 +02:00
Kuba Szczodrzyński
73e07a594f [core] Add mDNS debug logging, start IGMP if not enabled 2022-08-31 22:28:13 +02:00
Kuba Szczodrzyński
755c2ef400 [core] Add periodical free heap logging 2022-08-31 22:26:12 +02:00
Kuba Szczodrzyński
3f588e970a [core] Rework mDNS responder to fix TXT records 2022-08-27 00:00:45 +02:00
Kuba Szczodrzyński
3264807e77 [core] Fix StreamString clearing data using println() 2022-08-26 20:03:01 +02:00
Kuba Szczodrzyński
01225d4648 [beken-72xx] Update to external lwIP v2.1.3 2022-08-26 20:02:57 +02:00
Kuba Szczodrzyński
48aa809c98 [beken-72xx] Move to external lwIP v2.0.2 2022-08-25 23:34:54 +02:00
Kuba Szczodrzyński
4a722d4069 [core] Add option to debug FlashDB 2022-08-25 13:45:01 +02:00
Kuba Szczodrzyński
07aca2a0e7 [beken-72xx] Unprotect flash after initializing 2022-08-25 13:44:24 +02:00
Kuba Szczodrzyński
10000d9b6c [beken-72xx] Fix enabling WiFi AP mode only 2022-08-18 14:15:41 +02:00
Kuba Szczodrzyński
f0e247f31e [core] Print error if WiFi mode changing failed 2022-08-18 14:15:05 +02:00
Kuba Szczodrzyński
593dec5e88 [beken-72xx] Update ltchiptool to fix corrupted output binaries 2022-08-17 22:40:56 +02:00
Kuba Szczodrzyński
c45b86c993 [beken-72xx] Fix enabling AP mode 2022-08-17 15:17:14 +02:00
Kuba Szczodrzyński
02f01f2199 [beken-72xx] Initialize WiFiData when needed, null-terminate SSIDs 2022-08-17 11:24:32 +02:00
Kuba Szczodrzyński
88b1adc2d6 [github] Fix docs custom domain deployment 2022-08-16 23:24:20 +02:00
Kuba Szczodrzyński
a0a0e6775b [beken-72xx] Improve WiFi status recognition, fix null pointer access 2022-08-16 22:52:05 +02:00
Kuba Szczodrzyński
4096253694 [github] Add custom domain for docs workflow 2022-08-16 18:22:29 +02:00
Kuba Szczodrzyński
33ba44ebda [core] Move running OTA detection to family code 2022-08-16 18:01:38 +02:00
Kuba Szczodrzyński
9b4cf2a92b [core] Pin GitHub packages to known-good versions 2022-08-07 19:48:54 +02:00
Kuba Szczodrzyński
e6b915d8e3 [tools] Move dumptool to ltchiptool 2022-08-06 19:33:21 +02:00
Kuba Szczodrzyński
bb73fb5f55 [core] Force updating ltchiptool to fix linking problems 2022-08-06 17:07:28 +02:00
Kuba Szczodrzyński
10c5945afb [core] Allow exporting UF2 under a custom filename 2022-08-06 17:06:08 +02:00
Kuba Szczodrzyński
0bd613d556 [core] Support upload_flags property 2022-08-06 16:50:12 +02:00
231 changed files with 7866 additions and 845 deletions

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

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

View File

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

View File

@@ -66,7 +66,7 @@ SPIFFS | ❌ | ❌
BLE | - | ❌
NTP | ❌ | ❌
OTA | ✔️ | ✔️
MDNS | ✔️ | BK7231T only
MDNS | ✔️ | ✔️
MQTT | ✅ | ❌
SD | ❌ | ❌

View File

@@ -6,16 +6,19 @@
* Examples
* [PinScan](examples/PinScan/README.md)
* [ESPHome port](docs/projects/esphome.md)
* [Using tuya-cloudcutter](docs/cloudcutter.md)
* [Using tuya-cloudcutter](docs/getting-started/cloudcutter.md)
* [Auto-download-reboot](docs/getting-started/adr.md)
* [💻 Boards & CPU list](docs/status/supported.md)
* [✔️ Implementation status](docs/status/arduino.md)
* Supported chip families
* Beken BK72xx
* [General info](docs/platform/beken-72xx/README.md)
* [Flashing](docs/platform/beken-72xx/flashing.md)
* [Dumping](docs/platform/beken-72xx/dumping.md)
* Realtek AmebaZ Series
* [General info](docs/platform/realtek/README.md)
* [Flashing (AmebaZ)](docs/platform/realtek-ambz/flashing.md)
* [Dumping (AmebaZ)](docs/platform/realtek-ambz/dumping.md)
* [Debugging](docs/platform/realtek/debugging.md)
* [Exception decoder](docs/platform/realtek/exception-decoder.md)
* C library

14
TODO.md
View File

@@ -2,6 +2,16 @@
## General
### Environment stability
Do not publish *any* SDK functions, macros, defines and includes. Define only what's needed in LT's public headers (like `Arduino.h`). Everything else is taken from `sdk_extern.h` or `WVariant.h` (TODO decide whether to keep WV public / make both private / get rid of WV and use sdk_extern only). Private headers are included by LT's .cpp units (maybe a dedicated private header that would include sdk_extern + Arduino.h).
Developers wanting to use SDK functions need to include them.
Explicit is better than implicit.
- consider moving to C++17 (GNU)? or any newer than C++11
### New families
- BL602
@@ -10,6 +20,7 @@
- RTL8720D
- W600 and/or W800
- LN8825
- BK7231Q
- host-native family
### Tools
@@ -30,7 +41,6 @@
## BK7231
- implement OTA
- fix WiFi on BK7231N, test other functionality
- fix SSL (mbedTLS)
- I2C (Wire)
@@ -39,5 +49,5 @@
## RTL8710B
- move to GNU++11 (and verify that it works) - take all stdio functions from stdio.h
- take all stdio functions from stdio.h
- rewrite most of Wiring (it was copied from `ambd_arduino`, and is ugly)

View File

@@ -6,8 +6,10 @@
#include "WCharacterFixup.h"
#endif
#define delay delayMilliseconds // change delay()'s signature - it's defined as static inline in WVariant.h
#include <api/ArduinoAPI.h>
#include <core/LibreTuyaAPI.h>
#undef delay
// Include family-specific code
#include "WVariant.h"

View File

@@ -17,11 +17,14 @@ extern "C" {
#include <flash_pub.h>
#include <param_config.h>
#include <start_type_pub.h>
#include <sys_ctrl.h>
#include <sys_rtos.h>
#include <wdt_pub.h>
#include <wlan_ui_pub.h>
extern uint8_t system_mac[];
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
} // extern "C"
@@ -29,6 +32,36 @@ void LibreTuya::restart() {
bk_reboot();
}
void LibreTuya::restartDownloadMode() {
bk_reboot();
}
ResetReason LibreTuya::getResetReason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return RESET_REASON_POWER;
case RESET_SOURCE_REBOOT:
return RESET_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return RESET_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return RESET_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
return RESET_REASON_SLEEP;
}
return RESET_REASON_UNKNOWN;
}
/* CPU-related */
ChipType LibreTuya::getChipType() {
@@ -112,6 +145,11 @@ uint32_t LibreTuya::getMaxAllocHeap() {
static int8_t otaImage2Valid = -1;
uint8_t LibreTuya::otaGetRunning() {
// Beken has bootloader-based OTA, running app is always index 1
return 1;
}
uint8_t LibreTuya::otaGetStoredIndex() {
return otaHasImage2() ? 2 : 1;
}
@@ -149,6 +187,22 @@ bool LibreTuya::otaSwitch(bool force) {
return otaHasImage2(); // false if second image is not valid
}
/* Watchdog */
bool LibreTuya::wdtEnable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
}
void LibreTuya::wdtDisable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void LibreTuya::wdtFeed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}
/* Global instance */
LibreTuya LT;
LibreTuya ESP = LT;

View File

@@ -24,10 +24,29 @@ SerialClass::SerialClass(uint8_t port) {
this->buf = NULL;
}
#if LT_AUTO_DOWNLOAD_REBOOT
static uint8_t adrState = 0;
static const uint8_t adrCmd[] = {0x01, 0xE0, 0xFC, 0x01, 0x00};
static void adrParse(uint8_t c) {
// parse and respond to link check command (CMD_LinkCheck=0)
adrState = (adrState + 1) * (c == adrCmd[adrState]);
if (adrState == 5) {
LT_I("Auto download mode: rebooting");
LT.restart();
}
}
#endif
static void callback(int port, void *param) {
RingBuffer *buf = (RingBuffer *)param;
int ch;
while ((ch = uart_read_byte(port)) != -1) {
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX)
// parse UART protocol commands on UART1
if (port == UART1_PORT)
adrParse(ch);
#endif
buf->store_char(ch);
}
}

View File

@@ -9,9 +9,10 @@ extern "C" {
#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
// define an inline delay() which overrides BDK's delay()
static inline __attribute__((always_inline)) void delay(unsigned long ms) {
delayMilliseconds(ms);
}
// from fixups/arch_main.c
extern unsigned char __bk_rf_is_init;

View File

@@ -16,6 +16,10 @@ beken_thread_t mainThread;
void initArduino() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX) && defined(PIN_SERIAL1_TX)
// initialize auto-download-reboot parser
Serial1.begin(115200);
#endif
}
bool startMainTask() {

View File

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

View File

@@ -38,7 +38,7 @@ static GPIO_INDEX adcToGpio[] = {
#endif
static uint8_t gpioToPwm(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(pwmToGpio); i++) {
for (uint8_t i = 0; i < sizeof(pwmToGpio) / sizeof(GPIO_INDEX); i++) {
if (pwmToGpio[i] == gpio)
return i;
}
@@ -46,7 +46,7 @@ static uint8_t gpioToPwm(GPIO_INDEX gpio) {
}
static uint8_t gpioToAdc(GPIO_INDEX gpio) {
for (uint8_t i = 0; i < sizeof(adcToGpio); i++) {
for (uint8_t i = 0; i < sizeof(adcToGpio) / sizeof(GPIO_INDEX); i++) {
if (adcToGpio[i] == gpio)
return i;
}
@@ -94,39 +94,50 @@ void analogWrite(pin_size_t pinNumber, int value) {
if (!pinSupported(pin, PIN_PWM))
return;
float percent = value * 1.0 / (1 << _analogWriteResolution);
uint32_t dutyCycle = percent * _analogWritePeriod * 26 - 1;
float percent = value * 1.0 / ((1 << _analogWriteResolution) - 1);
uint32_t frequency = 26 * _analogWritePeriod - 1;
uint32_t dutyCycle = percent * frequency;
pwm.channel = gpioToPwm(pin->gpio);
#if CFG_SOC_NAME != SOC_BK7231N
pwm.duty_cycle = dutyCycle;
#else
pwm.duty_cycle1 = dutyCycle;
pwm.duty_cycle2 = dutyCycle;
pwm.duty_cycle3 = dutyCycle;
pwm.duty_cycle2 = 0;
pwm.duty_cycle3 = 0;
#endif
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;
if (value) {
if (!pinEnabled(pin, PIN_PWM)) {
// enable PWM and set its value
pwm.cfg.bits.en = PWM_ENABLE;
pwm.cfg.bits.int_en = PWM_INT_DIS;
pwm.cfg.bits.mode = PWM_PWM_MODE;
pwm.cfg.bits.clk = PWM_CLK_26M;
pwm.end_value = frequency;
pwm.p_Int_Handler = NULL;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_PARAM, &pwm);
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_LEVL_SET_HIGH, &pwm.channel);
sddev_control(PWM_DEV_NAME, CMD_PWM_UNIT_ENABLE, &pwm.channel);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_GPIO;
pin->enabled |= PIN_PWM;
} else {
// update duty cycle
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
}
} else {
// update duty cycle
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
if (pinEnabled(pin, PIN_PWM)) {
// disable PWM
pwm.cfg.bits.en = PWM_DISABLE;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, &pwm);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_PWM;
}
// force level as LOW
pinMode(pinNumber, OUTPUT);
digitalWrite(pinNumber, LOW);
}
}

View File

@@ -55,7 +55,7 @@ PinStatus digitalRead(pin_size_t pinNumber) {
if (!pin)
return 0;
// pin is not GPIO yet or not INPUT; change the mode
if (!pinEnabled(pin, PIN_GPIO) || !pinIsOutput(pin))
if (!pinEnabled(pin, PIN_GPIO) || !pinIsInput(pin))
pinMode(pinNumber, INPUT);
// read the value
return gpio_input(pin->gpio);

View File

@@ -4,6 +4,7 @@
static void *irqHandlerList[PINS_COUNT] = {NULL};
static void *irqHandlerArgs[PINS_COUNT] = {NULL};
static bool irqChangeList[PINS_COUNT];
static void irqHandler(unsigned char gpio) {
int pin = -1;
@@ -17,6 +18,15 @@ static void irqHandler(unsigned char gpio) {
return;
if (!irqHandlerList[pin])
return;
if (irqChangeList[pin]) {
if (pinTable[pin].mode == INPUT_PULLDOWN) {
pinTable[pin].mode = INPUT_PULLUP;
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_FALLING, irqHandler);
} else if (pinTable[pin].mode == INPUT_PULLUP) {
pinTable[pin].mode = INPUT_PULLDOWN;
gpio_int_enable(pinTable[pin].gpio, GPIO_INT_LEVEL_RISING, irqHandler);
}
}
if (irqHandlerArgs[pin] == NULL) {
((voidFuncPtr)irqHandlerList[pin])();
} else {
@@ -36,28 +46,40 @@ void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback,
return;
uint32_t event = 0;
PinMode modeNew = 0;
bool change = 0;
switch (mode) {
case LOW:
event = GPIO_INT_LEVEL_LOW;
modeNew = INPUT_PULLUP;
change = false;
break;
case HIGH:
event = GPIO_INT_LEVEL_HIGH;
modeNew = INPUT_PULLDOWN;
change = false;
break;
case FALLING:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = false;
break;
case RISING:
event = GPIO_INT_LEVEL_RISING;
modeNew = INPUT_PULLDOWN;
change = false;
break;
case CHANGE:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = true;
break;
default:
return;
}
irqHandlerList[interruptNumber] = callback;
irqHandlerArgs[interruptNumber] = param;
irqChangeList[interruptNumber] = change;
gpio_int_enable(pin->gpio, event, irqHandler);
pin->enabled |= PIN_IRQ | PIN_GPIO;
pin->mode = modeNew;
@@ -71,6 +93,7 @@ void detachInterrupt(pin_size_t interruptNumber) {
return;
irqHandlerList[interruptNumber] = NULL;
irqHandlerArgs[interruptNumber] = NULL;
irqChangeList[interruptNumber] = false;
gpio_int_disable(pin->gpio);
pin->enabled &= ~PIN_IRQ;
}

View File

@@ -3,6 +3,7 @@
#include "WiFiPriv.h"
WiFiClass::WiFiClass() {
memset(&data, 0x00, sizeof(WiFiData));
data.scanSem = xSemaphoreCreateBinary();
}
@@ -10,6 +11,30 @@ WiFiClass::~WiFiClass() {
vSemaphoreDelete(data.scanSem);
}
void WiFiClass::dataInitialize() {
if (data.statusIp)
return;
LT_DM(WIFI, "Data init");
data.configSta = zalloc(sizeof(network_InitTypeDef_st));
data.configAp = zalloc(sizeof(network_InitTypeDef_ap_st));
data.statusIp = malloc(sizeof(IPStatusTypedef));
data.statusLink = malloc(sizeof(LinkStatusTypeDef));
STA_CFG->dhcp_mode = DHCP_CLIENT;
LT_DM(WIFI, "Data = %p", data.configSta);
}
void WiFiClass::dataFree() {
LT_DM(WIFI, "Data free");
free(data.configSta);
free(data.configAp);
free(data.statusIp);
free(data.statusLink);
data.configSta = NULL;
data.configAp = NULL;
data.statusIp = NULL;
data.statusLink = NULL;
}
WiFiStatus eventTypeToStatus(uint8_t type) {
// rw_msg_pub.h:9
switch (type) {

View File

@@ -10,6 +10,9 @@ bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bo
LT_HEAP_I();
// Beken SDK bug: bk_wlan_ap_init_adv() doesn't null-terminate the passphrase
memset(g_ap_param_ptr->key, '\0', 65);
strcpy(AP_CFG->wifi_ssid, ssid);
if (passphrase) {
strcpy(AP_CFG->wifi_key, passphrase);
@@ -25,21 +28,29 @@ bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bo
AP_CFG->dhcp_mode = DHCP_SERVER;
AP_CFG->wifi_retry_interval = 100;
LT_I("Creating SoftAP %s", ssid);
LT_IM(WIFI, "Creating SoftAP %s", ssid);
if (!AP_CFG->local_ip_addr[0]) {
LT_DM(WIFI, "Setting default IP config");
softAPConfig((uint32_t)0, (uint32_t)0, (uint32_t)0);
}
LT_DM(WIFI, "Static IP: %s / %s / %s", AP_CFG->local_ip_addr, AP_CFG->net_mask, AP_CFG->gateway_ip_addr);
__wrap_bk_printf_disable();
OSStatus ret = bk_wlan_start_ap_adv(AP_CFG);
__wrap_bk_printf_enable();
if (ret != 0) {
LT_E("SoftAP failed; ret=%d", ret);
LT_EM(WIFI, "SoftAP failed; ret=%d", ret);
return false;
}
LT_D_WG("Start OK");
LT_DM(WIFI, "AP start OK");
return true;
}
bool WiFiClass::softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet) {
dataInitialize();
if (!localIP) {
localIP = gateway = IPAddress(192, 168, 43, 1);
subnet = IPAddress(255, 255, 255, 0);
@@ -64,6 +75,9 @@ bool WiFiClass::softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress sub
}
bool WiFiClass::softAPdisconnect(bool wifiOff) {
if (!(getMode() & WIFI_MODE_AP))
// do not call SDK methods before even initializing WiFi first
return true;
return bk_wlan_stop(BK_SOFT_AP) == kNoErr;
}
@@ -72,14 +86,14 @@ uint8_t WiFiClass::softAPgetStationNum() {
}
IPAddress WiFiClass::softAPIP() {
bk_wlan_get_ip_status(IP_STATUS, BK_SOFT_AP);
AP_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->ip);
return ip;
}
IPAddress WiFiClass::softAPSubnetMask() {
bk_wlan_get_ip_status(IP_STATUS, BK_SOFT_AP);
AP_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->mask);
return ip;
@@ -108,5 +122,6 @@ String WiFiClass::softAPmacAddress(void) {
}
const String WiFiClass::softAPSSID(void) {
AP_GET_LINK_STATUS_RETURN("");
return AP_CFG->wifi_ssid;
}

View File

@@ -7,6 +7,7 @@
extern "C" {
#include <FreeRTOS.h>
#include <rw_msg_pub.h>
#include <semphr.h>
} // extern "C"
@@ -18,4 +19,7 @@ typedef struct {
SemaphoreHandle_t scanSem;
void *statusIp;
void *statusLink;
rw_evt_type lastStaEvent;
rw_evt_type lastApEvent;
bool apEnabled;
} WiFiData;

View File

@@ -50,7 +50,12 @@ void wifiEventHandler(rw_evt_type event) {
if (!pWiFi)
return; // failsafe
LT_D_WG("WiFi event %u", event);
LT_DM(WIFI, "BK event %u", event);
if (event <= RW_EVT_STA_GOT_IP)
pWiFi->data.lastStaEvent = event;
else
pWiFi->data.lastApEvent = event;
EventId eventId;
EventInfo eventInfo;

View File

@@ -6,35 +6,36 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
__wrap_bk_printf_disable();
startWifiTask();
if (mode && !data.statusIp) {
data.configSta = zalloc(sizeof(network_InitTypeDef_st));
data.configAp = zalloc(sizeof(network_InitTypeDef_ap_st));
data.statusIp = malloc(sizeof(IPStatusTypedef));
data.statusLink = malloc(sizeof(LinkStatusTypeDef));
STA_CFG->dhcp_mode = DHCP_CLIENT;
}
if (!__bk_rf_is_init) {
LT_D_WG("Initializing func&app");
LT_DM(WIFI, "Initializing func&app");
func_init_extended();
app_pre_start();
// wait for the init_thread to finish its job
while (xTaskGetHandle("init_thread")) {
LT_V_WG("Waiting for init_thread");
LT_VM(WIFI, "Waiting for init_thread");
delay(10);
}
LT_D_WG("Success");
LT_DM(WIFI, "Init OK");
__bk_rf_is_init = true;
}
LT_HEAP_I();
if (mode) {
LT_DM(WIFI, "Wakeup RF");
uint32_t reg = 1; // this is only checked for being true-ish
sddev_control(SCTRL_DEV_NAME, CMD_RF_HOLD_BIT_SET, &reg);
}
if (sta == WLMODE_ENABLE) {
LT_D_WG("Enabling STA");
LT_DM(WIFI, "Enabling STA");
bk_wlan_sta_init(NULL);
#if CFG_WPA_CTRL_IFACE
wlan_sta_enable();
#endif
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_START);
} else if (sta == WLMODE_DISABLE) {
LT_D_WG("Disabling STA");
LT_DM(WIFI, "Disabling STA");
bk_wlan_stop(BK_STATION);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_STOP);
}
@@ -42,25 +43,22 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
LT_HEAP_I();
if (ap == WLMODE_ENABLE) {
LT_D_WG("Enabling AP");
bk_wlan_ap_init(NULL);
LT_DM(WIFI, "Enabling AP");
// fake it - on BK7231, enabling the AP without starting it breaks all connection attempts
data.apEnabled = true;
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_START);
} else if (ap == WLMODE_DISABLE) {
LT_D_WG("Disabling AP");
LT_DM(WIFI, "Disabling AP");
bk_wlan_stop(BK_SOFT_AP);
data.apEnabled = false;
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_STOP);
}
if (!mode) {
free(data.configSta);
free(data.configAp);
free(data.statusIp);
free(data.statusLink);
data.configSta = NULL;
data.configAp = NULL;
data.statusIp = NULL;
data.statusLink = NULL;
}
// force checking actual mode again
mode = getMode();
if (!mode)
dataFree();
LT_HEAP_I();
@@ -69,15 +67,13 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
}
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));
uint8_t sta = !!bk_wlan_has_role(VIF_STA) * WIFI_MODE_STA;
uint8_t ap = data.apEnabled * WIFI_MODE_AP; // report the faked value
return (WiFiMode)(sta | ap);
}
WiFiStatus WiFiClass::status() {
rw_evt_type status = mhdr_get_station_status();
rw_evt_type status = data.lastStaEvent;
if (status == RW_EVT_STA_CONNECTED && STA_CFG->dhcp_mode == DHCP_DISABLE)
status = RW_EVT_STA_GOT_IP;
return eventTypeToStatus(status);

View File

@@ -20,6 +20,9 @@ extern "C" {
#include <main_none.h>
#include <param_config.h>
#include <rw_msg_rx.h>
#include <sa_ap.h>
#include <sys_ctrl_pub.h>
#include <vif_mgmt.h>
#include <wlan_ui_pub.h>
#include <wpa_supplicant_i.h>
@@ -54,4 +57,34 @@ extern void wifiEventHandler(rw_evt_type event);
#define IP_STATUS ((IPStatusTypedef *)data.statusIp)
#define LINK_STATUS ((LinkStatusTypeDef *)data.statusLink)
#define STA_GET_LINK_STATUS_RETURN(ret) \
{ \
if (!sta_ip_is_start()) \
return ret; \
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef)); \
bk_wlan_get_link_status(LINK_STATUS); \
}
#define STA_GET_IP_STATUS_RETURN(ret) \
{ \
if (!sta_ip_is_start()) \
return ret; \
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
bk_wlan_get_ip_status(IP_STATUS, BK_STATION); \
}
#define AP_GET_LINK_STATUS_RETURN(ret) \
{ \
if (!uap_ip_is_start()) \
return ret; \
}
#define AP_GET_IP_STATUS_RETURN(ret) \
{ \
if (!uap_ip_is_start()) \
return ret; \
memset(IP_STATUS, 0x00, sizeof(IPStatusTypedef)); \
bk_wlan_get_ip_status(IP_STATUS, BK_SOFT_AP); \
}
} // extern "C"

View File

@@ -11,6 +11,8 @@ WiFiClass::begin(const char *ssid, const char *passphrase, int32_t channel, cons
LT_HEAP_I();
disconnect(false);
strcpy(STA_CFG->wifi_ssid, ssid);
if (passphrase) {
strcpy(STA_CFG->wifi_key, passphrase);
@@ -25,8 +27,7 @@ WiFiClass::begin(const char *ssid, const char *passphrase, int32_t channel, cons
}
bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
// need to initialize data struct first
enableSTA(true);
dataInitialize();
STA_CFG->dhcp_mode = localIP ? DHCP_DISABLE : DHCP_CLIENT;
if (localIP) {
@@ -35,8 +36,16 @@ bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, I
sprintf(STA_CFG->gateway_ip_addr, IP_FMT, gateway[0], gateway[1], gateway[2], gateway[3]);
if (dns1) {
sprintf(STA_CFG->dns_server_ip_addr, IP_FMT, dns1[0], dns1[1], dns1[2], dns1[3]);
} else {
STA_CFG->dns_server_ip_addr[0] = '\0';
}
} else {
STA_CFG->local_ip_addr[0] = '\0';
STA_CFG->net_mask[0] = '\0';
STA_CFG->gateway_ip_addr[0] = '\0';
STA_CFG->dns_server_ip_addr[0] = '\0';
}
// from wlan_ui.c:1370
if (sta_ip_is_start()) {
sta_ip_down();
@@ -54,17 +63,20 @@ bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, I
}
bool WiFiClass::reconnect(const uint8_t *bssid) {
dataInitialize();
if (!bssid && !STA_CFG->wifi_ssid[0]) {
LT_E("(B)SSID not specified");
LT_EM(WIFI, "(B)SSID not specified");
goto error;
}
if (bssid) {
LT_D_WG("Connecting to " MACSTR, MAC2STR(bssid));
LT_IM(WIFI, "Connecting to " MACSTR, MAC2STR(bssid));
} else {
LT_D_WG("Connecting to %s", STA_CFG->wifi_ssid);
LT_IM(WIFI, "Connecting to %s", STA_CFG->wifi_ssid);
}
LT_DM(WIFI, "Data = %p", data.configSta);
STA_CFG->wifi_mode = BK_STATION;
STA_CFG->wifi_retry_interval = 100;
if (bssid)
@@ -73,19 +85,19 @@ bool WiFiClass::reconnect(const uint8_t *bssid) {
memset(STA_CFG->wifi_bssid, 0x00, 6);
if (STA_CFG->dhcp_mode == DHCP_DISABLE) {
LT_D_WG("Static IP: %s / %s / %s", STA_CFG->local_ip_addr, STA_CFG->net_mask, STA_CFG->gateway_ip_addr);
LT_D_WG("Static DNS: %s", STA_CFG->dns_server_ip_addr);
LT_DM(WIFI, "Static IP: %s / %s / %s", STA_CFG->local_ip_addr, STA_CFG->net_mask, STA_CFG->gateway_ip_addr);
LT_DM(WIFI, "Static DNS: %s", STA_CFG->dns_server_ip_addr);
} else {
LT_D_WG("Using DHCP");
LT_DM(WIFI, "Using DHCP");
}
LT_D_WG("Starting WiFi...");
LT_DM(WIFI, "Starting WiFi...");
__wrap_bk_printf_disable();
bk_wlan_start_sta(STA_CFG);
__wrap_bk_printf_enable();
LT_D_WG("Start OK");
LT_DM(WIFI, "Start OK");
return true;
error:
@@ -93,6 +105,11 @@ error:
}
bool WiFiClass::disconnect(bool wifiOff) {
#if LT_DEBUG_WIFI
memset(LINK_STATUS, 0x00, sizeof(LinkStatusTypeDef));
bk_wlan_get_link_status(LINK_STATUS);
LT_DM(WIFI, "Disconnecting from %s (wifiOff=%d)", LINK_STATUS ? LINK_STATUS->ssid : NULL, wifiOff);
#endif
bk_wlan_connection_loss();
if (wifiOff)
enableSTA(false);
@@ -108,28 +125,28 @@ bool WiFiClass::getAutoReconnect() {
}
IPAddress WiFiClass::localIP() {
bk_wlan_get_ip_status(IP_STATUS, BK_STATION);
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->ip);
return ip;
}
IPAddress WiFiClass::subnetMask() {
bk_wlan_get_ip_status(IP_STATUS, BK_STATION);
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->mask);
return ip;
}
IPAddress WiFiClass::gatewayIP() {
bk_wlan_get_ip_status(IP_STATUS, BK_STATION);
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->gate);
return ip;
}
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
bk_wlan_get_ip_status(IP_STATUS, BK_STATION);
STA_GET_IP_STATUS_RETURN((uint32_t)0);
IPAddress ip;
ip.fromString(IP_STATUS->dns);
return ip;
@@ -157,7 +174,7 @@ uint8_t *WiFiClass::macAddress(uint8_t *mac) {
bool WiFiClass::setMacAddress(const uint8_t *mac) {
if (mac[0] & 0x01) {
LT_E("Invalid MAC address");
LT_EM(WIFI, "Invalid MAC address");
return false;
}
// ensure "mac_inited" is true
@@ -173,7 +190,7 @@ bool WiFiClass::setMacAddress(const uint8_t *mac) {
}
const String WiFiClass::SSID() {
bk_wlan_get_link_status(LINK_STATUS);
STA_GET_LINK_STATUS_RETURN("");
return (char *)LINK_STATUS->ssid;
}
@@ -187,21 +204,21 @@ const String WiFiClass::psk() {
}
uint8_t *WiFiClass::BSSID() {
bk_wlan_get_link_status(LINK_STATUS);
STA_GET_LINK_STATUS_RETURN(NULL);
return LINK_STATUS->bssid;
}
int32_t WiFiClass::channel() {
bk_wlan_get_link_status(LINK_STATUS);
STA_GET_LINK_STATUS_RETURN(0);
return LINK_STATUS->channel;
}
int8_t WiFiClass::RSSI() {
bk_wlan_get_link_status(LINK_STATUS);
STA_GET_LINK_STATUS_RETURN(0);
return LINK_STATUS->wifi_strength;
}
WiFiAuthMode WiFiClass::getEncryption() {
bk_wlan_get_link_status(LINK_STATUS);
STA_GET_LINK_STATUS_RETURN(WIFI_AUTH_INVALID);
return securityTypeToAuthMode(LINK_STATUS->security);
}

View File

@@ -6,25 +6,25 @@ static void scanHandler(void *ctx, uint8_t param) {
LT_HEAP_I();
WiFiClass *cls = (WiFiClass *)ctx;
if (!cls) {
LT_W("Called without ctx");
LT_WM(WIFI, "Called without ctx");
return;
}
WiFiScanData *scan = cls->scan;
if (!scan) {
LT_W("Called without cls->scan");
LT_WM(WIFI, "Called without cls->scan");
return;
}
ScanResult_adv result;
if (wlan_sta_scan_result(&result)) {
LT_E("Failed to get scan result");
LT_EM(WIFI, "Failed to get scan result");
goto end;
}
LT_D_WG("Found %d APs", result.ApNum);
LT_IM(WIFI, "Found %d APs", result.ApNum);
cls->scanAlloc(result.ApNum);
if (!scan->ap) {
LT_W("scan->ap alloc failed");
LT_WM(WIFI, "scan->ap alloc failed");
goto end;
}
@@ -41,20 +41,30 @@ static void scanHandler(void *ctx, uint8_t param) {
wifiEventSendArduino(ARDUINO_EVENT_WIFI_SCAN_DONE);
end:
scan->running = false;
xSemaphoreGive(cls->data.scanSem);
scan->timeout = 0;
if (scan->running) {
// running == false means it was discarded (timeout)
scan->running = false;
xSemaphoreGive(cls->data.scanSem);
}
LT_HEAP_I();
return;
}
int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint32_t maxMsPerChannel, uint8_t channel) {
if (scan && scan->running)
return WIFI_SCAN_RUNNING;
if (scan && scan->running) {
if (scan->timeout && millis() > scan->timeout) {
LT_WM(WIFI, "Scan timeout, discarding");
scan->running = false;
} else {
return WIFI_SCAN_RUNNING;
}
}
enableSTA(true);
scanDelete();
scanInit();
LT_I("Starting WiFi scan");
LT_IM(WIFI, "Starting WiFi scan");
__wrap_bk_printf_disable();
mhdr_scanu_reg_cb(scanHandler, this);
@@ -63,10 +73,11 @@ int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint3
LT_HEAP_I();
scan->running = true;
scan->timeout = millis() + maxMsPerChannel * 20 + 1000;
int16_t ret = WIFI_SCAN_RUNNING;
if (!async) {
LT_I("Waiting for results");
LT_IM(WIFI, "Waiting for results");
xSemaphoreTake(data.scanSem, 1); // reset the semaphore quickly
xSemaphoreTake(data.scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
if (scan->running) {

View File

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

View File

@@ -1,5 +1,6 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <lt_logger.h>
#include <sdk_extern.h>
#include <fal.h>
@@ -8,10 +9,27 @@
#define FLASH_ERASE_MIN_SIZE (4 * 1024)
extern uint32_t flash_ctrl(uint32_t cmd, void *param);
extern PROTECT_TYPE get_flash_protect();
extern void flash_protection_op(uint8_t mode, PROTECT_TYPE type);
static void unprotect() {
PROTECT_TYPE type = get_flash_protect();
if (type != FLASH_PROTECT_NONE) {
flash_protection_op(0, FLASH_PROTECT_NONE);
#if LT_LOGLEVEL <= LT_LEVEL_DEBUG
LT_D("Flash protect: %u -> %u", type, get_flash_protect());
uint16_t sr = 0;
flash_ctrl(CMD_FLASH_READ_SR, &sr);
LT_D("SR = %04x", sr);
#endif
}
}
static int init() {
__wrap_bk_printf_disable();
flash_init();
flash_ctrl(CMD_FLASH_WRITE_ENABLE, NULL);
unprotect();
__wrap_bk_printf_enable();
return 0;
}
@@ -22,11 +40,13 @@ static int read(long offset, uint8_t *buf, size_t size) {
}
static int write(long offset, const uint8_t *buf, size_t size) {
unprotect();
flash_write((char *)buf, size, offset);
return size;
}
static int erase(long offset, size_t size) {
unprotect();
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
for (uint16_t i = 0; i < size; i++) {
uint32_t addr = offset + i * FLASH_ERASE_MIN_SIZE;

View File

@@ -2,6 +2,8 @@
#include "SoftwareSerial.h"
#ifdef LT_ARD_HAS_SOFTSERIAL
SoftwareSerial::SoftwareSerial(pin_size_t receivePin, pin_size_t transmitPin, bool inverted) {
data.rx.buf = NULL;
data.tx.buf = NULL;
@@ -39,3 +41,5 @@ size_t SoftwareSerial::write(uint8_t c) {
}
return 1;
}
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#ifdef LT_ARD_HAS_SOFTSERIAL
#include <Arduino.h>
#include <api/HardwareSerial.h>
#include <api/RingBuffer.h>
@@ -71,4 +73,4 @@ class SoftwareSerial : public HardwareSerial {
using Print::write;
};
#define HAS_SERIAL_CLASS 1
#endif

View File

@@ -12,8 +12,8 @@ void WiFiClass::printDiag(Print &dest) {
if (getMode() & WIFI_MODE_STA) {
dest.println("-- Station --");
dest.print("SSID: ");
dest.println(SSID());
if (isConnected()) {
dest.println(SSID());
dest.print("Channel: ");
dest.println(channel());
dest.print("BSSID: ");
@@ -28,40 +28,50 @@ void WiFiClass::printDiag(Print &dest) {
dest.println(macAddress());
dest.print("Hostname: ");
dest.println(getHostname());
} else {
dest.println("disconnected");
}
}
if (getMode() & WIFI_MODE_AP) {
dest.println("-- Access Point --");
dest.print("SSID: ");
dest.println(softAPSSID());
dest.print("IP: ");
dest.println(softAPIP());
dest.print("MAC: ");
dest.println(softAPmacAddress());
dest.print("Hostname: ");
dest.println(softAPgetHostname());
if (softAPSSID().length()) {
dest.println(softAPSSID());
dest.print("IP: ");
dest.println(softAPIP());
dest.print("MAC: ");
dest.println(softAPmacAddress());
dest.print("Hostname: ");
dest.println(softAPgetHostname());
} else {
dest.println("disconnected");
}
}
}
bool WiFiClass::validate(const char *ssid, const char *passphrase) {
if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) {
LT_W("SSID not specified or too long");
LT_WM(WIFI, "SSID not specified or too long");
return false;
}
if (passphrase) {
uint16_t length = strlen(passphrase);
if (length < 8) {
LT_W("Passphrase too short (%u)", length);
LT_WM(WIFI, "Passphrase too short (%u)", length);
return false;
}
if (length > 63) {
LT_W("Passphrase too long (%u)", length);
LT_WM(WIFI, "Passphrase too long (%u)", length);
return false;
}
}
return true;
}
__attribute__((weak)) void WiFiClass::dataInitialize() {}
__attribute__((weak)) void WiFiClass::dataFree() {}
WiFiClass WiFi;
WiFiClass *pWiFi = NULL;

View File

@@ -47,6 +47,8 @@ class WiFiClass {
~WiFiClass();
void printDiag(Print &dest);
bool validate(const char *ssid, const char *passphrase);
void dataInitialize();
void dataFree();
public: /* WiFiGeneric.cpp */
bool mode(WiFiMode mode);

View File

@@ -7,7 +7,7 @@ bool WiFiClass::mode(WiFiMode mode) {
pWiFi = this;
WiFiMode currentMode = getMode();
LT_D_WG("Mode changing %u -> %u", currentMode, mode);
LT_DM(WIFI, "Mode changing %u -> %u", currentMode, mode);
if (mode == currentMode)
return true;
@@ -15,11 +15,19 @@ bool WiFiClass::mode(WiFiMode mode) {
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));
sta = WiFiModeAction(sta + sta * !!(mode & WIFI_MODE_STA));
ap = WiFiModeAction(ap + ap * !!(mode & WIFI_MODE_AP));
// initialize data structures if wifi is enabled
if (mode)
dataInitialize();
// actually change the mode
LT_HEAP_I();
return modePriv(mode, sta, ap);
if (!modePriv(mode, sta, ap))
return false;
if (getMode() != mode) {
LT_WM(WIFI, "Mode changed to %d (requested %d)", getMode(), mode);
}
return true;
}
bool WiFiClass::enableSTA(bool enable) {

View File

@@ -137,9 +137,10 @@ typedef struct {
} WiFiScanAP;
typedef struct {
bool running = false;
uint8_t count = 0;
WiFiScanAP *ap = NULL;
bool running = false;
unsigned long timeout = 0;
uint8_t count = 0;
WiFiScanAP *ap = NULL;
} WiFiScanData;
typedef enum {

View File

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

View File

@@ -11,8 +11,9 @@ enum ChipFamily {
F_RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz)
F_RTL8720C = 0xE08F7564, // Realtek AmebaZ2
F_RTL8720D = 0x3379CFE2, // Realtek AmebaD
F_BK7231T = 0x675A40B0, // Beken 7231T
F_BK7231U = 0x675A40B0, // Beken 7231U/7231T
F_BK7231N = 0x7B3EF230, // Beken 7231N
F_BK7251 = 0x6A82CC42, // Beken 7251/7252
F_BL602 = 0xDE1270B7, // Boufallo 602
F_XR809 = 0x51E903A8, // Xradiotech 809
F_NATIVE = 0xDEADBEEF, // Host-native
@@ -29,6 +30,8 @@ enum ChipType {
RTL8711BN = CHIP_TYPE(F_RTL8710B, 0xFD), // CHIPID_8711BN / QFN48
RTL8711BU = CHIP_TYPE(F_RTL8710B, 0xFC), // CHIPID_8711BG / QFN68
// Beken 72XX
BK7231T = CHIP_TYPE(F_BK7231T, 0x1A), // *SCTRL_CHIP_ID = 0x7231a
BK7231T = CHIP_TYPE(F_BK7231U, 0x1A), // *SCTRL_CHIP_ID = 0x7231a
BK7231N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
BL2028N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
BK7252 = CHIP_TYPE(F_BK7251, 0x00), // TODO
};

View File

@@ -14,6 +14,7 @@ String ipToString(const IPAddress &ip) {
* @param buf destination pointer
* @param len how many bytes to generate
*/
extern "C" {
void lt_rand_bytes(uint8_t *buf, size_t len) {
int *data = (int *)buf;
size_t i;
@@ -37,7 +38,7 @@ void lt_rand_bytes(uint8_t *buf, size_t len) {
* @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) {
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
uint16_t pos = 0;
while (pos < len) {
// print hex offset
@@ -61,3 +62,4 @@ void hexdump(uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
pos += lineWidth;
}
}
}

View File

@@ -64,11 +64,17 @@ extern "C" {
"LibreTuya v" LT_VERSION_STR " on " LT_BOARD_STR ", compiled at " __DATE__ " " __TIME__ \
)
void lt_rand_bytes(uint8_t *buf, size_t len);
#ifdef __cplusplus
String ipToString(const IPAddress &ip);
void hexdump(uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
extern "C" {
void lt_rand_bytes(uint8_t *buf, size_t len);
void hexdump(const uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
}
#else
void hexdump(uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
void lt_rand_bytes(uint8_t *buf, size_t len);
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
#endif

View File

@@ -50,6 +50,33 @@ const char *LibreTuya::getDeviceName() {
return deviceName;
}
/**
* @brief Get a textual representation of a reset reason.
*
* @param reason value to convert to text, uses getResetReason() by default
*/
const char *LibreTuya::getResetReasonName(ResetReason reason) {
if (reason >= RESET_REASON_MAX)
reason = getResetReason();
switch (reason) {
case RESET_REASON_POWER:
return "Power-On";
case RESET_REASON_BROWNOUT:
return "Brownout";
case RESET_REASON_HARDWARE:
return "HW Reboot";
case RESET_REASON_SOFTWARE:
return "SW Reboot";
case RESET_REASON_WATCHDOG:
return "WDT Reset";
case RESET_REASON_CRASH:
return "Crash";
case RESET_REASON_SLEEP:
return "Sleep Wakeup";
}
return "Unknown";
}
/**
* @brief Get CPU frequency in MHz.
*/
@@ -74,18 +101,6 @@ __attribute__((weak)) uint32_t LibreTuya::getFlashChipSize() {
#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.
*

View File

@@ -7,6 +7,18 @@
#include "LibreTuyaAPI.h"
#include <core/ChipType.h>
typedef enum {
RESET_REASON_UNKNOWN = 0,
RESET_REASON_POWER = 1,
RESET_REASON_BROWNOUT = 2,
RESET_REASON_HARDWARE = 3,
RESET_REASON_SOFTWARE = 4,
RESET_REASON_WATCHDOG = 5,
RESET_REASON_CRASH = 6,
RESET_REASON_SLEEP = 7,
RESET_REASON_MAX = 8,
} ResetReason;
/**
* @brief Flash chip ID structure.
*/
@@ -31,18 +43,34 @@ class LibreTuya {
ChipFamily getChipFamily();
const char *getChipFamilyName();
const char *getDeviceName();
const char *getResetReasonName(ResetReason reason = RESET_REASON_MAX);
uint32_t getCpuFreqMHz();
uint32_t getFlashChipSize();
uint8_t otaGetRunning();
uint8_t otaGetTarget();
bool otaRollback();
bool otaCanRollback();
public: /* Compatibility methods */
/**
* @brief Alias of getMaxAllocHeap().
*/
inline uint32_t getMaxFreeBlockSize() {
return getMaxAllocHeap();
}
public: /* Family-defined methods */
/**
* @brief Reboot the CPU.
*/
void restart();
/**
* @brief Reboot the CPU and stay in download mode (if possible).
*/
void restartDownloadMode();
/**
* @brief Get the reason of last chip reset.
*/
ResetReason getResetReason();
/**
* @brief Reconfigure GPIO pins used for debugging
* (SWD/JTAG), so that they can be used as normal I/O.
@@ -109,6 +137,10 @@ class LibreTuya {
uint32_t getMaxAllocHeap();
public: /* OTA-related */
/**
* @brief Get the currently running firmware OTA index.
*/
uint8_t otaGetRunning();
/**
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
*/
@@ -136,6 +168,23 @@ class LibreTuya {
* @return false if writing failed; true otherwise
*/
bool otaSwitch(bool force = false);
public: /* Watchdog */
/**
* @brief Enable the hardware watchdog.
*
* @param timeout watchdog timeout, milliseconds (defaults to 10s)
* @return whether the chip has a hardware watchdog
*/
bool wdtEnable(uint32_t timeout = 10000);
/**
* @brief Disable the hardware watchdog.
*/
void wdtDisable();
/**
* @brief Feed/reset the hardware watchdog timer.
*/
void wdtFeed();
};
extern LibreTuya LT;

View File

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

View File

@@ -21,6 +21,13 @@ extern bool startMainTask();
*/
extern void mainTask(const void *arg);
/**
* @brief Run periodic tasks, like printing free heap or checking millis() overflow.
*
* This is called during delaying operations, like yield() or delay().
*/
extern void runPeriodicTasks();
#define PIN_NONE (1 << 0)
#define PIN_GPIO (1 << 1)
#define PIN_IRQ (1 << 2)

View File

@@ -7,10 +7,23 @@
#if LT_LOGGER_CALLER
#define LT_LOG(level, caller, line, ...) lt_log(level, caller, line, __VA_ARGS__)
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...);
#define LT_LOGM(level, module, caller, line, ...) \
do { \
if (LT_DEBUG_##module) { \
lt_log(level, caller, line, #module ": " __VA_ARGS__); \
} \
} while (0)
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...)
__attribute__((format(printf, 4, 5)));
#else
#define LT_LOG(level, caller, line, ...) lt_log(level, __VA_ARGS__)
void lt_log(const uint8_t level, const char *format, ...);
#define LT_LOGM(level, module, caller, line, ...) \
do { \
if (LT_DEBUG_##module) { \
lt_log(level, #module ": " __VA_ARGS__); \
} \
} while (0)
void lt_log(const uint8_t level, const char *format, ...) __attribute__((format(printf, 2, 3)));
#endif
/**
@@ -26,41 +39,55 @@ void lt_log_set_port(uint8_t port);
void lt_log_disable();
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_TM(module, ...) LT_LOGM(LT_LEVEL_TRACE, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_VM(module, ...) LT_LOGM(LT_LEVEL_TRACE, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_T(...)
#define LT_V(...)
#define LT_TM(...)
#define LT_VM(...)
#endif
#if LT_LEVEL_DEBUG >= LT_LOGLEVEL
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_DM(module, ...) LT_LOGM(LT_LEVEL_DEBUG, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_D(...)
#define LT_DM(...)
#endif
#if LT_LEVEL_INFO >= LT_LOGLEVEL
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_IM(module, ...) LT_LOGM(LT_LEVEL_INFO, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_I(...)
#define LT_IM(...)
#endif
#if LT_LEVEL_WARN >= LT_LOGLEVEL
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_WM(module, ...) LT_LOGM(LT_LEVEL_WARN, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_W(...)
#define LT_WM(...)
#endif
#if LT_LEVEL_ERROR >= LT_LOGLEVEL
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_EM(module, ...) LT_LOGM(LT_LEVEL_ERROR, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_E(...)
#define LT_EM(...)
#endif
#if LT_LEVEL_FATAL >= LT_LOGLEVEL
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_FM(module, ...) LT_LOGM(LT_LEVEL_FATAL, module, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define LT_F(...)
#define LT_FM(...)
#endif
#if LT_LOG_HEAP
@@ -96,20 +123,6 @@ void lt_log_disable();
#define ets_printf(...) LT_I(__VA_ARGS__)
#define ETS_PRINTF(...) LT_I(__VA_ARGS__)
#define LT_T_MOD(module, ...) \
do { \
if (module) { \
LT_T(__VA_ARGS__); \
} \
} while (0)
#define LT_D_MOD(module, ...) \
do { \
if (module) { \
LT_D(__VA_ARGS__); \
} \
} while (0)
#define LT_RET(ret) \
LT_E("ret=%d", ret); \
return ret;
@@ -155,38 +168,3 @@ void lt_log_disable();
#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__)
#define LT_D_WG(...) LT_D_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
// WiFiClient.cpp
#define LT_T_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_V_WC(...) LT_T_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
#define LT_D_WC(...) LT_D_MOD(LT_DEBUG_WIFI_CLIENT, __VA_ARGS__)
// WiFiServer.cpp
#define LT_T_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_V_WS(...) LT_T_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
#define LT_D_WS(...) LT_D_MOD(LT_DEBUG_WIFI_SERVER, __VA_ARGS__)
// WiFiSTA.cpp
#define LT_T_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_V_WSTA(...) LT_T_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
#define LT_D_WSTA(...) LT_D_MOD(LT_DEBUG_WIFI_STA, __VA_ARGS__)
// WiFiAP.cpp
#define LT_T_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_V_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
#define LT_D_WAP(...) LT_D_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
// WiFiClientSecure.cpp & implementations
#define LT_T_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_V_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
#define LT_D_SSL(...) LT_D_MOD(LT_DEBUG_SSL, __VA_ARGS__)
// Update.cpp
#define LT_T_OTA(...) LT_T_MOD(LT_DEBUG_OTA, __VA_ARGS__)
#define LT_V_OTA(...) LT_T_MOD(LT_DEBUG_OTA, __VA_ARGS__)
#define LT_D_OTA(...) LT_D_MOD(LT_DEBUG_OTA, __VA_ARGS__)

View File

@@ -32,13 +32,32 @@ void mainTask(const void *arg) {
}
}
static unsigned long periodicTasks[] = {0, 0};
void runPeriodicTasks() {
#if LT_LOG_HEAP
if (millis() - periodicTasks[0] > 1000) {
LT_HEAP_I();
periodicTasks[0] = millis();
}
#endif
#if LT_USE_TIME
if (millis() - periodicTasks[1] > 10000) {
gettimeofday(NULL, NULL);
periodicTasks[1] = millis();
}
#endif
}
int main(void) {
// print a startup banner
LT_BANNER();
// initialize Arduino framework
initArduino();
// initialize C library
__libc_init_array();
// inform about the reset reason
LT_I("Reset reason: %u", LT.getResetReason());
// initialize Arduino framework
initArduino();
// optionally initialize per-variant code
initVariant();
// initialize FAL
@@ -47,7 +66,7 @@ int main(void) {
fal_root_part = (fal_partition_t)fal_partition_find("root");
// start the main task and OS kernel
if (!startMainTask()) {
LT_E("Couldn't start the main task");
LT_F("Couldn't start the main task");
}
while (1) {}

View File

@@ -37,7 +37,7 @@ class SocketHandle {
};
LwIPClient::LwIPClient() {
LT_V_WC("LwIPClient()");
LT_VM(CLIENT, "LwIPClient()");
_connected = false;
_sock = NULL;
_rxBuffer = NULL;
@@ -45,7 +45,7 @@ LwIPClient::LwIPClient() {
}
LwIPClient::LwIPClient(int sock) {
LT_V_WC("LwIPClient(%d)", sock);
LT_VM(CLIENT, "LwIPClient(%d)", sock);
_connected = true;
_sock = std::make_shared<SocketHandle>(sock);
_rxBuffer = std::make_shared<LwIPRxBuffer>(sock);
@@ -53,7 +53,7 @@ LwIPClient::LwIPClient(int sock) {
}
LwIPClient::~LwIPClient() {
LT_V_WC("~LwIPClient()");
LT_VM(CLIENT, "~LwIPClient()");
stop();
}
@@ -89,7 +89,7 @@ int LwIPClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
stop();
int sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
LT_D_WC("socket failed");
LT_DM(CLIENT, "socket failed");
return -1;
}
@@ -114,19 +114,19 @@ int LwIPClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
int res = lwip_connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (res < 0 && errno != EINPROGRESS) {
LT_E("Connect failed; errno=%d", errno);
LT_EM(CLIENT, "Connect failed; errno=%d", errno);
lwip_close(sock);
return -1;
}
res = lwip_select(sock + 1, NULL, &fdset, NULL, timeout < 0 ? NULL : &tv);
if (res < 0) {
LT_E("Select failed; errno=%d", errno);
LT_EM(CLIENT, "Select failed; errno=%d", errno);
lwip_close(sock);
return 0;
}
if (res == 0) {
LT_E("Select timeout; errno=%d", errno);
LT_EM(CLIENT, "Select timeout; errno=%d", errno);
lwip_close(sock);
return 0;
}
@@ -136,7 +136,7 @@ int LwIPClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
res = lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &sockerr, &len);
if (res < 0 || sockerr != 0) {
LT_E("Socket error; res=%d, sockerr=%d", res, sockerr);
LT_EM(CLIENT, "Socket error; res=%d, sockerr=%d", res, sockerr);
lwip_close(sock);
return 0;
}
@@ -198,7 +198,7 @@ size_t LwIPClient::write(const uint8_t *buf, size_t size) {
retry--;
if (lwip_select(fd() + 1, NULL, &fdset, NULL, &tv) < 0) {
LT_W("Select failed; errno=%d", errno);
LT_WM(CLIENT, "Select failed; errno=%d", errno);
return 0;
}
@@ -214,7 +214,7 @@ size_t LwIPClient::write(const uint8_t *buf, size_t size) {
retry = WIFI_CLIENT_WRITE_RETRY;
}
} else if (res < 0 && errno != EAGAIN) {
LT_W("Send failed; errno=%d", errno);
LT_WM(CLIENT, "Send failed; errno=%d", errno);
setWriteError(res);
_connected = false;
retry = 0;
@@ -223,7 +223,7 @@ size_t LwIPClient::write(const uint8_t *buf, size_t size) {
}
}
}
LT_D_WC("wrote %d bytes", written);
LT_DM(CLIENT, "wrote %d bytes", written);
return written;
}
@@ -306,7 +306,7 @@ void LwIPClient::flush() {
}
void LwIPClient::stop() {
LT_V_WC("Stopping TCP");
LT_VM(CLIENT, "Stopping TCP");
_connected = false;
_sock = NULL;
_rxBuffer = NULL;
@@ -327,11 +327,11 @@ uint8_t LwIPClient::connected() {
case ECONNRESET:
case ECONNREFUSED:
case ECONNABORTED:
LT_W("Connection closed; errno=%d", errno);
LT_IM(CLIENT, "Connection closed; errno=%d", errno);
_connected = false;
break;
default:
LT_I("Connection status unknown; errno=%d", errno);
LT_WM(CLIENT, "Connection status unknown; errno=%d", errno);
_connected = true;
break;
}

View File

@@ -14,13 +14,13 @@ extern "C" {
size_t LwIPRxBuffer::r_available() {
if (_sock < 0) {
LT_D_WC("_sock < 0");
LT_DM(CLIENT, "_sock < 0");
return 0;
}
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);
LT_DM(CLIENT, "lwip_ioctl()=%d, errno=%d", res, errno);
_failed = true;
return 0;
}

View File

@@ -32,7 +32,7 @@ bool LwIPServer::begin(uint16_t port, bool reuseAddr) {
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sock < 0) {
LT_E("Socket failed; errno=%d", errno);
LT_EM(SERVER, "Socket failed; errno=%d", errno);
return false;
}
@@ -45,17 +45,17 @@ bool LwIPServer::begin(uint16_t port, bool reuseAddr) {
addr.sin_port = htons(_port);
if (lwip_bind(_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
LT_E("Bind failed; errno=%d", errno);
LT_EM(SERVER, "Bind failed; errno=%d", errno);
return false;
}
if (lwip_listen(_sock, _maxClients) < 0) {
LT_E("Bind failed; errno=%d", errno);
LT_EM(SERVER, "Bind failed; errno=%d", errno);
return false;
}
uint8_t *addrB = (uint8_t *)&_addr;
LT_I("Server running on %hhu.%hhu.%hhu.%hhu:%hu", addrB[0], addrB[1], addrB[2], addrB[3], _port);
LT_IM(SERVER, "Server running on %hhu.%hhu.%hhu.%hhu:%hu", addrB[0], addrB[1], addrB[2], addrB[3], _port);
lwip_fcntl(_sock, F_SETFL, O_NONBLOCK);
_active = true;
@@ -99,7 +99,7 @@ WiFiClient LwIPServer::accept() {
// and receive data, so LwIP still sees a connected client that sends nothing. At least
// that's what I understand. And any loop that doesn't call delay() seems to block the TCP
// stack completely and prevents it from even being pinged.
LT_D_WS("Got client");
LT_DM(SERVER, "Got client");
delay(5);
return WiFiClient(sock);
}

View File

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

View File

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

View File

@@ -22,12 +22,12 @@ MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {
}
MbedTLSClient::~MbedTLSClient() {
LT_V_WC("~MbedTLSClient()");
LT_VM(CLIENT, "~MbedTLSClient()");
stop();
}
void MbedTLSClient::stop() {
LT_V_SSL("Stopping SSL");
LT_VM(SSL, "Stopping SSL");
if (_sslCfg.ca_chain) {
mbedtls_x509_crt_free(&_caCert);
@@ -88,7 +88,7 @@ void debug_cb(void *ctx, int level, const char *file, int line, const char *str)
uint16_t len = strlen(str);
char *msg = (char *)str;
msg[len - 1] = '\0';
LT_I("%04d: |%d| %s", line, level, msg);
LT_IM(SSL, "%04d: |%d| %s", line, level, msg);
}
int MbedTLSClient::connect(
@@ -115,13 +115,13 @@ int MbedTLSClient::connect(
int ret = WiFiClient::connect(addr, port, timeout);
if (ret < 0) {
LT_E("SSL socket failed");
LT_EM(SSL, "SSL socket failed");
return ret;
}
char *uid = "lt-ssl"; // TODO
LT_V_SSL("Init SSL");
LT_VM(SSL, "Init SSL");
init();
LT_HEAP_I();
@@ -160,7 +160,7 @@ int MbedTLSClient::connect(
#ifdef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED
uint16_t len = strlen(psk);
if ((len & 1) != 0 || len > 2 * MBEDTLS_PSK_MAX_LEN) {
LT_E("PSK length invalid");
LT_EM(SSL, "PSK length invalid");
return -1;
}
unsigned char pskBin[MBEDTLS_PSK_MAX_LEN] = {};
@@ -185,13 +185,13 @@ int MbedTLSClient::connect(
if (!_insecure && clientCert && clientKey) {
mbedtls_x509_crt_init(&_clientCert);
mbedtls_pk_init(&_clientKey);
LT_V_SSL("Loading client cert");
LT_VM(SSL, "Loading client cert");
ret = mbedtls_x509_crt_parse(&_clientCert, (const unsigned char *)clientCert, strlen(clientCert) + 1);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
LT_RET(ret);
}
LT_V_SSL("Loading private key");
LT_VM(SSL, "Loading private key");
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)clientKey, strlen(clientKey) + 1, NULL, 0);
if (ret < 0) {
mbedtls_x509_crt_free(&_clientCert);
@@ -200,7 +200,7 @@ int MbedTLSClient::connect(
mbedtls_ssl_conf_own_cert(&_sslCfg, &_clientCert, &_clientKey);
}
LT_V_SSL("Setting TLS hostname");
LT_VM(SSL, "Setting TLS hostname");
ret = mbedtls_ssl_set_hostname(&_sslCtx, host);
LT_RET_NZ(ret);
@@ -214,7 +214,7 @@ int MbedTLSClient::connect(
LT_HEAP_I();
LT_V_SSL("SSL handshake");
LT_VM(SSL, "SSL handshake");
if (_handshakeTimeout == 0)
_handshakeTimeout = timeout;
unsigned long start = millis();
@@ -223,7 +223,7 @@ int MbedTLSClient::connect(
LT_RET(ret);
}
if ((millis() - start) > _handshakeTimeout) {
LT_E("SSL handshake timeout");
LT_EM(SSL, "SSL handshake timeout");
return -1;
}
delay(2);
@@ -232,26 +232,27 @@ int MbedTLSClient::connect(
LT_HEAP_I();
if (clientCert && clientKey) {
LT_D_SSL(
LT_DM(
SSL,
"Protocol %s, ciphersuite %s",
mbedtls_ssl_get_version(&_sslCtx),
mbedtls_ssl_get_ciphersuite(&_sslCtx)
);
ret = mbedtls_ssl_get_record_expansion(&_sslCtx);
if (ret >= 0)
LT_D_SSL("Record expansion: %d", ret);
LT_DM(SSL, "Record expansion: %d", ret);
else {
LT_W("Record expansion unknown");
LT_WM(SSL, "Record expansion unknown");
}
}
LT_V_SSL("Verifying certificate");
LT_VM(SSL, "Verifying certificate");
ret = mbedtls_ssl_get_verify_result(&_sslCtx);
if (ret) {
char buf[512];
memset(buf, 0, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", ret);
LT_E("Failed to verify peer certificate! Verification info: %s", buf);
LT_EM(SSL, "Failed to verify peer certificate! Verification info: %s", buf);
return ret;
}
@@ -417,7 +418,7 @@ bool MbedTLSClient::verify(const char *fingerprint, const char *domainName) {
return false;
if (memcmp(fpLocal, fpRemote, 32)) {
LT_D_SSL("Fingerprints don't match");
LT_DM(SSL, "Fingerprints don't match");
return false;
}
@@ -438,7 +439,7 @@ void MbedTLSClient::setAlpnProtocols(const char **alpnProtocols) {
bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&_sslCtx);
if (!cert) {
LT_E("Failed to get peer certificate");
LT_EM(SSL, "Failed to get peer certificate");
return false;
}
mbedtls_sha256_context shaCtx;

View File

@@ -25,13 +25,8 @@
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;
}
concat(data, size);
return size;
}
return 0;
}

View File

@@ -18,16 +18,20 @@ bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3,
return false;
cleanup();
LT_D_OTA("begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget());
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget());
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
info = uf2_info_init();
if (!size)
return errorArd(UPDATE_ERROR_SIZE);
if (!size) {
cleanup(UPDATE_ERROR_SIZE);
return false;
}
if (command != U_FLASH)
return errorArd(UPDATE_ERROR_BAD_ARGUMENT);
if (command != U_FLASH) {
cleanup(UPDATE_ERROR_BAD_ARGUMENT);
return false;
}
bytesTotal = size;
return true;
@@ -46,12 +50,15 @@ bool UpdateClass::end(bool evenIfRemaining) {
if (!isFinished() && !evenIfRemaining) {
// abort if not finished
return errorArd(UPDATE_ERROR_ABORT);
cleanup(UPDATE_ERROR_ABORT);
return false;
}
// TODO what is evenIfRemaining for?
if (!LT.otaSwitch())
if (!LT.otaSwitch()) {
// try to activate the second OTA
return errorArd(UPDATE_ERROR_ACTIVATE);
cleanup(UPDATE_ERROR_ACTIVATE);
return false;
}
cleanup();
return true;
@@ -72,7 +79,7 @@ size_t UpdateClass::write(uint8_t *data, size_t len) {
// 0 if not running
return 0;
LT_D_OTA("write(%u) / buf %u/512", len, bufSize());
LT_VM(OTA, "write(%u) / buf %u/512", len, bufSize());
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
// buffer empty and entire block is in data
@@ -89,9 +96,11 @@ size_t UpdateClass::write(uint8_t *data, size_t len) {
uint16_t toWrite; // 1..512
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError())
if (hasError()) {
// return on errors
printErrorContext2(data, toWrite);
return written;
}
data += toWrite;
len -= toWrite;
written += toWrite;
@@ -113,7 +122,7 @@ size_t UpdateClass::writeStream(Stream &data) {
if (available <= 0) {
if (millis() - lastData > UPDATE_TIMEOUT_MS) {
// waited for data too long; abort with error
errorArd(UPDATE_ERROR_STREAM);
cleanup(UPDATE_ERROR_STREAM);
return written;
}
continue;
@@ -127,9 +136,11 @@ size_t UpdateClass::writeStream(Stream &data) {
bufPos += read;
written += read;
tryWriteData();
if (hasError())
if (hasError()) {
// return on errors
printErrorContext2(NULL, read); // buf is not valid anymore
return written;
}
}
return written;
}
@@ -145,7 +156,7 @@ size_t UpdateClass::writeStream(Stream &data) {
size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
uf2_block_t *block = NULL;
LT_V_OTA("Writing %u to buffer (%u/512)", len, bufSize());
LT_VM(OTA, "Writing %u to buffer (%u/512)", len, bufSize());
if (len == UF2_BLOCK_SIZE) {
// data has a complete block
@@ -164,7 +175,7 @@ size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
// a complete block has been found
if (block) {
if (errorUf2(uf2_check_block(ctx, block)))
if (checkUf2Error(uf2_check_block(ctx, block)))
// block is invalid
return 0;
@@ -174,23 +185,24 @@ size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
if (!bytesWritten) {
// parse header block to allow retrieving firmware info
if (errorUf2(uf2_parse_header(ctx, block, info)))
if (checkUf2Error(uf2_parse_header(ctx, block, info)))
// header is invalid
return 0;
LT_I("OTA: %s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board);
LT_IM(OTA, "%s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board);
if (bytesTotal == UPDATE_SIZE_UNKNOWN) {
// set total update size from block count info
bytesTotal = block->block_count * UF2_BLOCK_SIZE;
} else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) {
// given update size does not match the block count
LT_D_OTA("Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE);
return errorArd(UPDATE_ERROR_SIZE);
LT_EM(OTA, "Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE);
cleanup(UPDATE_ERROR_SIZE);
return 0;
}
} else {
// write data blocks normally
if (errorUf2(uf2_write(ctx, block)))
if (checkUf2Error(uf2_write(ctx, block)))
// block writing failed
return 0;
}

View File

@@ -75,10 +75,11 @@ class UpdateClass {
const char *getBoardName();
private: /* UpdateUtil.cpp */
void cleanup();
bool errorUf2(uf2_err_t err);
bool errorArd(uint8_t err);
void cleanup(uint8_t ardErr = UPDATE_ERROR_OK, uf2_err_t uf2Err = UF2_ERR_OK);
bool checkUf2Error(uf2_err_t err);
void bufAlloc();
void printErrorContext1();
void printErrorContext2(const uint8_t *data, size_t len);
uint16_t bufLeft();
uint16_t bufSize();
@@ -117,8 +118,12 @@ class UpdateClass {
return errUf2;
}
uint16_t getErrorCode() {
return (errArd << 8) | errUf2;
}
void clearError() {
errorArd(UPDATE_ERROR_OK);
cleanup(UPDATE_ERROR_OK);
}
bool hasError() {

View File

@@ -30,7 +30,15 @@ UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) {
return *this;
}
void UpdateClass::cleanup() {
void UpdateClass::cleanup(uint8_t ardErr, uf2_err_t uf2Err) {
errUf2 = uf2Err;
errArd = ardErr;
#if LT_DEBUG_OTA
if (hasError())
printErrorContext1();
#endif
free(ctx); // NULL in constructor
ctx = NULL;
uf2_info_free(info); // NULL in constructor
@@ -40,8 +48,6 @@ void UpdateClass::cleanup() {
bytesWritten = 0;
bytesTotal = 0;
errUf2 = UF2_ERR_OK;
errArd = UPDATE_ERROR_OK;
}
/**
@@ -51,38 +57,19 @@ void UpdateClass::cleanup() {
* Use like: "if (errorUf2(...)) return false;"
* @return true if err is not OK, false otherwise
*/
bool UpdateClass::errorUf2(uf2_err_t err) {
if (err)
LT_D_OTA("[%4d] errorUf2(%d)", ctx ? ctx->seq : 0, err);
bool UpdateClass::checkUf2Error(uf2_err_t err) {
if (err <= UF2_ERR_IGNORE)
return false;
cleanup();
errUf2 = err;
errArd = errorMap[err];
cleanup(errorMap[err], 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) {
if (err)
LT_D_OTA("[%4d] errorArd(%d)", ctx ? ctx->seq : 0, err);
cleanup();
errUf2 = UF2_ERR_OK;
errArd = err;
return false;
}
/**
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
LT_D_OTA("Aborting update");
errorArd(UPDATE_ERROR_ABORT);
LT_DM(OTA, "Aborting update");
cleanup(UPDATE_ERROR_ABORT);
}
void UpdateClass::bufAlloc() {
@@ -105,6 +92,42 @@ void UpdateClass::printError(Print &out) {
out.println(errorString());
}
/**
* @brief Print details about the error and current OTA state.
*/
void UpdateClass::printErrorContext1() {
#if LT_DEBUG_OTA
LT_EM(OTA, "Error: %s", errorString());
if (errArd == UPDATE_ERROR_ABORT)
return;
LT_EM(OTA, "- written: %u of %u", bytesWritten, bytesTotal);
LT_EM(OTA, "- buf: size=%u, left=%u", bufSize(), bufLeft());
hexdump(buf, bufSize());
if (ctx)
LT_EM(
OTA,
"- ctx: seq=%u, part1=%s, part2=%s",
ctx->seq - 1, // print last parsed block seq
ctx->part1 ? ctx->part1->name : NULL,
ctx->part2 ? ctx->part2->name : NULL
);
uf2_block_t *block = (uf2_block_t *)buf;
if (buf)
LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
#endif
}
void UpdateClass::printErrorContext2(const uint8_t *data, size_t len) {
#if LT_DEBUG_OTA
LT_EM(OTA, "- while writing %u bytes", len);
if (data)
hexdump(data, len);
#endif
}
/**
* @brief Get string representation of the error in format
* "ard=..,uf2=..". Returns "" if no error.

View File

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

View File

@@ -3,71 +3,66 @@
#ifdef LT_HAS_LWIP2
#include "mDNS.h"
#include <vector>
extern "C" {
#include <errno.h>
#include <lwip/apps/mdns.h>
#include <lwip/igmp.h>
#include <lwip/init.h>
#include <lwip/netif.h>
}
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";
static std::vector<char *> services;
static std::vector<uint8_t> protos;
static std::vector<std::vector<char *>> records;
mDNS::mDNS() {}
mDNS::~mDNS() {}
static void mdnsTxtCallback(struct mdns_service *service, void *userdata) {
size_t index = (size_t)userdata;
if (index >= records.size())
return;
for (const auto record : records[index]) {
err_t err = mdns_resp_add_service_txtitem(service, record, strlen(record));
if (err != ERR_OK)
return;
}
}
static void mdnsStatusCallback(struct netif *netif, uint8_t result) {
LT_DM(MDNS, "Status: netif %u, status %u", netif->num, result);
}
bool mDNS::begin(const char *hostname) {
setInstanceName(hostname);
LT_DM(MDNS, "Starting (%s)", hostname);
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 1
mdns_resp_register_name_result_cb(mdnsStatusCallback);
#endif
mdns_resp_init();
struct netif *netif = netif_list;
uint8_t enabled = 0;
while (netif != NULL) {
netif->flags |= NETIF_FLAG_IGMP;
// 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++;
uint8_t enabled = 0;
struct netif *netif;
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (!netif_is_up(netif))
continue;
LT_DM(MDNS, "Adding netif %u", netif->num);
if ((netif->flags & NETIF_FLAG_IGMP) == 0) {
LT_DM(MDNS, "Enabling IGMP");
netif->flags |= NETIF_FLAG_IGMP;
igmp_start(netif);
}
netif = netif->next;
err_t ret = mdns_resp_add_netif(netif, hostname, 255);
if (ret == ERR_OK)
enabled++;
else
LT_DM(MDNS, "Cannot add netif %u; ret=%d, errno=%d", netif->num, ret, errno);
}
return enabled > 0;
}
@@ -81,72 +76,53 @@ void mDNS::end() {
}
}
void mDNS::setInstanceName(String name) {
mdnsInstanceName = name;
}
bool mDNS::addService(char *service, char *proto, uint16_t port) {
char _service[strlen(service) + 2];
char _proto[strlen(proto) + 2];
_service[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_service + 1, service + (service[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
bool mDNS::addServiceImpl(const char *name, const char *service, uint8_t proto, uint16_t port) {
bool added = false;
struct netif *netif = netif_list;
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns_resp_add_service(netif, mdnsInstanceName.c_str(), _service, protocol, port, 255, NULL, NULL);
// register TXT callback;
// pass service index as userdata parameter
LT_DM(MDNS, "Add service: netif %u / %s / %s / %u / %u", netif->num, name, service, proto, port);
mdns_resp_add_service(
netif,
name,
service,
(mdns_sd_proto)proto,
port,
255,
mdnsTxtCallback,
(void *)services.size() // index of newly added service
);
added = true;
}
netif = netif->next;
}
if (!added)
return false;
// add the service to TXT record arrays
services.push_back(strdup(service));
protos.push_back(proto);
records.emplace_back();
return true;
}
bool mDNS::addServiceTxt(char *name, char *proto, char *key, char *value) {
char _name[strlen(name) + 2];
char _proto[strlen(proto) + 2];
_name[0] = '_';
_proto[0] = '_';
// prepend names with _
strcpy(_name + 1, name + (name[0] == '_'));
strcpy(_proto + 1, proto + (proto[0] == '_'));
mdns_sd_proto protocol = DNSSD_PROTO_UDP;
if (strncmp(_proto + 1, "tcp", 3) == 0)
protocol = DNSSD_PROTO_TCP;
struct netif *netif = netif_list;
struct mdns_host *mdns;
struct mdns_service *service;
uint8_t txt_len = strlen(key) + strlen(value) + 1;
char *txt = (char *)malloc(txt_len + 1);
sprintf(txt, "%s=%s", key, value);
while (netif != NULL) {
if (netif_is_up(netif)) {
mdns = (struct mdns_host *)netif_get_client_data(netif, mdns_netif_client_id);
for (uint8_t i = 0; i < MDNS_MAX_SERVICES; i++) {
service = mdns->services[i];
if (service == NULL)
continue;
if (strcmp(service->service, _name) || service->proto != protocol)
continue;
if (mdns_resp_add_service_txtitem(service, txt, txt_len) != ERR_OK) {
free(txt);
return false;
}
}
bool mDNS::addServiceTxtImpl(const char *service, uint8_t proto, const char *item) {
int8_t index = -1;
for (uint8_t i = 0; i < services.size(); i++) {
// find a matching service
if (strcmp(services[i], service) == 0 && protos[i] == proto) {
index = i;
break;
}
netif = netif->next;
}
free(txt);
if (index == -1)
return false;
records[index].push_back(strdup(item));
return true;
}

View File

@@ -0,0 +1,40 @@
/* Copyright (c) Kuba Szczodrzyński 2022-08-26. */
#include "mDNS.h"
static char *ensureUnderscore(const char *value) {
uint8_t len = strlen(value) + 1;
char *result = (char *)malloc(len);
result[0] = '_';
strcpy(result + 1, value + (value[0] == '_'));
return result;
}
void mDNS::setInstanceName(const char *name) {
if (instanceName)
free(instanceName);
instanceName = strdup(name);
}
bool mDNS::addService(char *service, char *proto, uint16_t port) {
char *_service = ensureUnderscore(service);
uint8_t _proto = strncmp(proto + (proto[0] == '_'), "tcp", 3) == 0 ? MDNS_TCP : MDNS_UDP;
bool result = addServiceImpl(instanceName ? instanceName : "LT mDNS", _service, _proto, port);
free(_service);
return result;
}
bool mDNS::addServiceTxt(char *service, char *proto, char *key, char *value) {
char *_service = ensureUnderscore(service);
uint8_t _proto = strncmp(proto + (proto[0] == '_'), "tcp", 3) == 0 ? MDNS_TCP : MDNS_UDP;
uint8_t txt_len = strlen(key) + strlen(value) + 1;
char *txt = (char *)malloc(txt_len + 1);
sprintf(txt, "%s=%s", key, value);
bool result = addServiceTxtImpl(_service, _proto, txt);
free(_service);
free(txt);
return result;
}

View File

@@ -44,7 +44,16 @@ License (MIT license):
#include <Arduino.h>
#include <api/IPv6Address.h>
#define MDNS_UDP 0
#define MDNS_TCP 1
class mDNS {
private:
bool addServiceImpl(const char *name, const char *service, uint8_t proto, uint16_t port);
bool addServiceTxtImpl(const char *service, uint8_t proto, const char *item);
char *instanceName = NULL;
public:
mDNS();
~mDNS();
@@ -52,9 +61,9 @@ class mDNS {
bool begin(const char *hostname);
void end();
void setInstanceName(String name);
void setInstanceName(const char *name);
bool addService(char *service, char *proto, uint16_t port);
bool addServiceTxt(char *name, char *proto, char *key, char *value);
bool addServiceTxt(char *service, 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);
@@ -73,12 +82,12 @@ class mDNS {
String txt(int idx, int txtIdx);
String txtKey(int idx, int txtIdx);
void setInstanceName(const char *name) {
setInstanceName(String(name));
void setInstanceName(String name) {
setInstanceName(name.c_str());
}
void setInstanceName(char *name) {
setInstanceName(String(name));
setInstanceName((const char *)name);
}
bool addService(const char *service, const char *proto, uint16_t port) {
@@ -89,12 +98,12 @@ class mDNS {
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(const char *service, const char *proto, const char *key, const char *value) {
addServiceTxt((char *)service, (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());
void addServiceTxt(String service, String proto, String key, String value) {
addServiceTxt(service.c_str(), proto.c_str(), key.c_str(), value.c_str());
}
IPAddress queryHost(const char *host, uint32_t timeout = 2000) {

View File

@@ -36,10 +36,14 @@
/* MCU Endian Configuration, default is Little Endian Order. */
// #define FDB_BIG_ENDIAN
/* log print macro. default EF_PRINT macro is printf() */
#define FDB_PRINT(...)
#include <printf_config.h>
/* print debug information */
// #define FDB_DEBUG_ENABLE
#if LT_DEBUG_FDB
#include <printf/printf.h>
#define FDB_PRINT(...) __wrap_printf(__VA_ARGS__)
#define FDB_DEBUG_ENABLE
#else
#define FDB_PRINT(...)
#endif
#endif /* _FDB_CFG_H_ */

View File

@@ -1,5 +1,7 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-16. */
#include <sys/time.h>
extern char *strdup(const char *);
extern int strcasecmp(const char *s1, const char *s2);
extern int strncasecmp(const char *s1, const char *s2, size_t n);

View File

@@ -0,0 +1,50 @@
/* Copyright (c) Kuba Szczodrzyński 2022-09-03. */
#include <Arduino.h>
#include <errno.h>
static uint32_t reset_epoch = 0; // epoch corresponding to millis() == 0
static uint32_t reset_millis = 0; // millis() when epoch reset was performed
int __wrap_gettimeofday(struct timeval *tv, void *tz) {
if (millis() < reset_millis) {
// the clock overflowed
reset_epoch += UINT32_MAX / 1000;
reset_millis = millis();
}
if (!tv) {
errno = EINVAL;
return -1;
}
unsigned long m = millis();
tv->tv_sec = reset_epoch + (m / 1000);
tv->tv_usec = (m % 1000) * 1000;
return 0;
}
int __wrap_settimeofday(const struct timeval *tv, const struct timezone *tz) {
if (!tv) {
errno = EINVAL;
return -1;
}
unsigned long m = millis();
reset_epoch = tv->tv_sec - (m / 1000);
reset_millis = m;
return 0;
}
int gettimeofday(struct timeval *tv, void *tz) {
return __wrap_gettimeofday(tv, tz);
}
int settimeofday(const struct timeval *tv, const struct timezone *tz) {
return __wrap_settimeofday(tv, tz);
}
int _gettimeofday(struct timeval *tv, void *tz) {
return __wrap_gettimeofday(tv, tz);
}
int _settimeofday(const struct timeval *tv, const struct timezone *tz) {
return __wrap_settimeofday(tv, tz);
}

View File

@@ -6,10 +6,25 @@ extern "C" {
#include <flash_api.h>
#include <rtl8710b.h>
#include <sys_api.h>
#include <wdt_api.h>
}
void LibreTuya::restart() {
// The Watchdog Way
wdtEnable(1L);
while (1) {}
}
void LibreTuya::restartDownloadMode() {
// mww 0x40000138 0x8
HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08);
// reboot it the ugly way
sys_reset();
while (1) {}
}
ResetReason LibreTuya::getResetReason() {
return RESET_REASON_UNKNOWN;
}
void LibreTuya::gpioRecover() {
@@ -24,7 +39,7 @@ void LibreTuya::gpioRecover() {
ChipType LibreTuya::getChipType() {
uint8_t chipId;
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
return CHIP_TYPE_ENUM(F_RTL8710B, chipId);
return CHIP_TYPE_ENUM(FAMILY, chipId);
}
const char *LibreTuya::getChipModel() {
@@ -99,6 +114,13 @@ uint32_t LibreTuya::getMaxAllocHeap() {
/* OTA-related */
uint8_t LibreTuya::otaGetRunning() {
// RTL8710B is XIP, so check the code offset in flash
uint32_t addr = (uint32_t)lt_log;
uint32_t offs = addr - SPI_FLASH_BASE;
return offs > FLASH_OTA2_OFFSET ? 2 : 1;
}
uint8_t LibreTuya::otaGetStoredIndex() {
uint32_t *otaAddress = (uint32_t *)0x8009000;
if (*otaAddress == 0xFFFFFFFF)
@@ -165,6 +187,22 @@ bool LibreTuya::otaSwitch(bool force) {
return true;
}
/* Watchdog */
bool LibreTuya::wdtEnable(uint32_t timeout) {
watchdog_init(timeout);
watchdog_start();
}
void LibreTuya::wdtDisable() {
watchdog_stop();
}
void LibreTuya::wdtFeed() {
watchdog_refresh();
}
/* Global instance */
LibreTuya LT;
LibreTuya ESP = LT;

View File

@@ -83,6 +83,8 @@ void SoftwareSerial::begin(unsigned long baudrate, uint16_t config) {
}
void SoftwareSerial::end() {
if (!(bool)this)
return;
gtimer_stop(OBJ);
gtimer_deinit(OBJ);
free(OBJ);

View File

@@ -34,7 +34,7 @@ bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bo
dhcps_deinit();
LT_I("Creating SoftAP %s", ssid);
LT_IM(WIFI, "Creating SoftAP %s", ssid);
int ret;
if (!ssidHidden) {
@@ -60,7 +60,7 @@ bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bo
wifi_indication(WIFI_EVENT_CONNECT, NULL, ARDUINO_EVENT_WIFI_AP_START, -2);
if (ret < 0) {
LT_E("SoftAP failed; ret=%d", ret);
LT_EM(WIFI, "SoftAP failed; ret=%d", ret);
return false;
}

View File

@@ -15,7 +15,7 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
if (!data.initialized) {
// initialize wifi first
LT_I("Initializing LwIP");
LT_IM(WIFI, "Initializing LwIP");
LwIP_Init();
reset_wifi_struct();
data.initialized = true;
@@ -23,7 +23,7 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
LT_HEAP_I();
if (getMode()) {
// stop wifi to change mode
LT_D_WG("Stopping WiFi to change mode");
LT_DM(WIFI, "Stopping WiFi to change mode");
if (wifi_off() != RTW_SUCCESS)
goto error;
vTaskDelay(20);
@@ -32,7 +32,7 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
}
if (wifi_on((rtw_mode_t)mode) != RTW_SUCCESS) {
LT_E("Error while changing mode(%u)", mode);
LT_EM(WIFI, "Error while changing mode(%u)", mode);
goto error;
}
@@ -72,12 +72,14 @@ WiFiStatus WiFiClass::status() {
}
bool WiFiClass::setSleep(bool enable) {
LT_D_WG("WiFi sleep mode %u", enable);
if (enable)
LT_DM(WIFI, "WiFi sleep mode %u", enable);
if (enable) {
if (wifi_enable_powersave() != RTW_SUCCESS)
return false;
else if (wifi_disable_powersave() != RTW_SUCCESS)
} else {
if (wifi_disable_powersave() != RTW_SUCCESS)
return false;
}
data.sleep = enable;
return true;
}

View File

@@ -63,7 +63,7 @@ bool WiFiClass::reconnect(const uint8_t *bssid) {
int ret;
uint8_t dhcpRet;
LT_I("Connecting to %s", wifi.ssid.val);
LT_IM(WIFI, "Connecting to %s", wifi.ssid.val);
__wrap_rtl_printf_disable();
__wrap_DiagPrintf_disable();
@@ -110,11 +110,11 @@ bool WiFiClass::reconnect(const uint8_t *bssid) {
__wrap_DiagPrintf_enable();
return true;
}
LT_E("DHCP failed; dhcpRet=%d", dhcpRet);
LT_EM(WIFI, "DHCP failed; dhcpRet=%d", dhcpRet);
wifi_disconnect();
goto error;
}
LT_E("Connection failed; ret=%d", ret);
LT_EM(WIFI, "Connection failed; ret=%d", ret);
error:
__wrap_rtl_printf_enable();
__wrap_DiagPrintf_enable();

View File

@@ -39,7 +39,7 @@ int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint3
scanDelete();
scanInit();
LT_I("Starting WiFi scan");
LT_IM(WIFI, "Starting WiFi scan");
if (wifi_scan_networks(scanHandler, this) != RTW_SUCCESS)
return WIFI_SCAN_FAILED;
@@ -47,7 +47,7 @@ int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint3
scan->running = true;
if (!async) {
LT_I("Waiting for results");
LT_IM(WIFI, "Waiting for results");
xSemaphoreTake(data.scanSem, 1); // reset the semaphore quickly
xSemaphoreTake(data.scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
return scan->count;

View File

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

View File

@@ -2,12 +2,18 @@
- [Generic - BK7231N (Tuya QFN32)](../boards/generic-bk7231n-qfn32-tuya/README.md)
- [Generic - BK7231T (Tuya QFN32)](../boards/generic-bk7231t-qfn32-tuya/README.md)
- [Generic - BK7252](../boards/generic-bk7252/README.md)
- [Generic - RTL8710BN (2M/468k)](../boards/generic-rtl8710bn-2mb-468k/README.md)
- [Generic - RTL8710BN (2M/788k)](../boards/generic-rtl8710bn-2mb-788k/README.md)
- [Generic - RTL8710BX (4M/980k)](../boards/generic-rtl8710bx-4mb-980k/README.md)
- [Generic - RTL8720CF (2M/992k)](../boards/generic-rtl8720cf-2mb-992k/README.md)
- [BW12](../boards/bw12/README.md)
- [BW15](../boards/bw15/README.md)
- [CB2L](../boards/cb2l/README.md)
- [CB2S](../boards/cb2s/README.md)
- [CB3L](../boards/cb3l/README.md)
- [CB3S](../boards/cb3s/README.md)
- [CB3SE](../boards/cb3se/README.md)
- [WB2L](../boards/wb2l/README.md)
- [WB2S](../boards/wb2s/README.md)
- [WB3L](../boards/wb3l/README.md)

View File

@@ -1,24 +1,6 @@
{
"build": {
"bkcrypt_coeffs": "510fb093a3cbeadc5993a17ec7adeb03",
"bkboot_version": "1.0.1-bk7231n",
"bkrbl_size_app": "0x107800"
},
"flash": {
"bootloader": "0x000000+0x11000",
"app": "0x011000+0x119000",
"download": "0x12A000+0xA6000",
"tlv": "0x1D0000+0x1000",
"net": "0x1D1000+0x2000",
"kvs": "0x1D3000+0x8000",
"userdata": "0x1DB000+0x25000"
},
"upload": {
"maximum_size": 1083136
},
"doc": {
"extra": [
"Bootloader and app partitions contain CRC16 sums every 32 bytes. That results in the actual flash offsets/sizes not aligned to sector boundaries. To simplify calculations, the values shown in the table (extracted from bootloader's partition table) were aligned to 4096 bytes."
]
"bkboot_version": "1.0.1-bk7231n"
}
}

View File

@@ -2,11 +2,27 @@
"build": {
"family": "BK7231N",
"ldscript_sdk": "bk7231n_bsp.ld",
"ldscript_arduino": "bk7231n_bsp.ld"
"ldscript_arduino": "bk7231n_bsp.ld",
"bkboot_version": "1.0.1-bk7231n",
"bkrbl_size_app": "0x107800"
},
"flash": {
"bootloader": "0x000000+0x11000",
"app": "0x011000+0x119000",
"download": "0x12A000+0xA6000",
"calibration": "0x1D0000+0x1000",
"net": "0x1D1000+0x1000",
"tlv": "0x1D2000+0x1000",
"kvs": "0x1D3000+0x8000",
"userdata": "0x1DB000+0x25000"
},
"upload": {
"maximum_size": 1083136,
"speed": 460800
},
"connectivity": [
"ble"
],
"doc": {
"params": {
"extra": {

View File

@@ -1,24 +1,6 @@
{
"build": {
"bkcrypt_coeffs": "510fb093a3cbeadc5993a17ec7adeb03",
"bkboot_version": "1.0.5-bk7231s",
"bkrbl_size_app": "0x107800"
},
"flash": {
"bootloader": "0x000000+0x11000",
"app": "0x011000+0x121000",
"download": "0x132000+0xA6000",
"kvs": "0x1D8000+0x8000",
"tlv": "0x1E0000+0x1000",
"net": "0x1E1000+0x2000",
"userdata": "0x1E3000+0x1D000"
},
"upload": {
"maximum_size": 1083136
},
"doc": {
"extra": [
"Bootloader and app partitions contain CRC16 sums every 32 bytes. That results in the actual flash offsets/sizes not aligned to sector boundaries. To simplify calculations, the values shown in the table (extracted from bootloader's partition table) were aligned to 4096 bytes."
]
"bkboot_version": "1.0.5-bk7231s"
}
}

View File

@@ -1,17 +0,0 @@
{
"build": {
"family": "BK7231T",
"ldscript_sdk": "bk7231_bsp.ld",
"ldscript_arduino": "bk7231_bsp.ld"
},
"upload": {
"speed": 921600
},
"doc": {
"params": {
"extra": {
"Bluetooth": "BLE v4.2"
}
}
}
}

View File

@@ -0,0 +1,33 @@
{
"build": {
"family": "BK7231U",
"ldscript_sdk": "bk7231_bsp.ld",
"ldscript_arduino": "bk7231_bsp.ld",
"bkboot_version": "1.0.8-bk7231u",
"bkrbl_size_app": "0x108700"
},
"flash": {
"bootloader": "0x000000+0x11000",
"app": "0x011000+0x121000",
"download": "0x132000+0xA6000",
"kvs": "0x1D8000+0x8000",
"calibration": "0x1E0000+0x1000",
"tlv": "0x1E1000+0x1000",
"net": "0x1E2000+0x1000",
"userdata": "0x1E3000+0x1D000"
},
"upload": {
"maximum_size": 1083136,
"speed": 921600
},
"connectivity": [
"ble"
],
"doc": {
"params": {
"extra": {
"Bluetooth": "BLE v4.2"
}
}
}
}

View File

@@ -0,0 +1,40 @@
{
"build": {
"family": "BK7251",
"f_cpu": "180000000L",
"ldscript_sdk": "bk7231_bsp.ld",
"ldscript_arduino": "bk7231_bsp.ld",
"bkboot_version": "0.1.3-bk7252",
"bkrbl_size_app": "0x1A0000"
},
"flash": {
"bootloader": "0x000000+0x11000",
"app": "0x011000+0x1BA000",
"filesystem": "0x1CB000+0x119000",
"kvs": "0x2E4000+0x8000",
"download": "0x2EC000+0x112000",
"calibration": "0x3FE000+0x1000",
"tlv": "0x3FF000+0x1000"
},
"debug": {
"gdb_init": [
"mem 0x200000 0x400000 ro"
]
},
"upload": {
"maximum_ram_size": 524288,
"flash_size": 4194304,
"maximum_size": 1703936,
"speed": 921600
},
"connectivity": [
"ble"
],
"doc": {
"params": {
"extra": {
"Bluetooth": "BLE v5.0"
}
}
}
}

View File

@@ -10,8 +10,7 @@
}
},
"connectivity": [
"wifi",
"ble"
"wifi"
],
"debug": {
"protocol": "openocd",
@@ -49,6 +48,9 @@
"General info": "../../docs/platform/beken-72xx/README.md",
"Flashing guide": "../../docs/platform/beken-72xx/flashing.md",
"BkWriter v1.6.0": "https://images.tuyacn.com/smart/bk_writer1.60/bk_writer1.60.exe"
}
},
"extra": [
"Bootloader and app partitions contain CRC16 sums every 32 bytes. That results in the actual flash offsets/sizes not aligned to sector boundaries. To simplify calculations, the values shown in the table (extracted from bootloader's partition table) were aligned to 4096 bytes."
]
}
}

View File

@@ -0,0 +1,186 @@
{
"pcb": {
"ic": {
"10": {
"C_NAME": "GPIO25",
"GPIO": "P25",
"IRQ": null,
"USB": "DP"
},
"11": {
"C_NAME": "GPIO33",
"GPIO": "P33",
"IRQ": null,
"DVP": "PD1"
},
"12": {
"C_NAME": "GPIO32",
"GPIO": "P32",
"IRQ": null,
"DVP": "PD0"
},
"13": {
"C_NAME": "GPIO29",
"GPIO": "P29",
"IRQ": null,
"DVP": "PCLK"
},
"14": {
"C_NAME": "GPIO27",
"GPIO": "P27",
"IRQ": null,
"DVP": "MCLK"
},
"15": {
"C_NAME": "GPIO30",
"GPIO": "P30",
"IRQ": null,
"DVP": "HSYNC"
},
"16": {
"C_NAME": "GPIO31",
"GPIO": "P31",
"IRQ": null,
"DVP": "VSYNC"
},
"17": {
"C_NAME": "GPIO34",
"GPIO": "P34",
"IRQ": null,
"DVP": "PD2"
},
"18": {
"C_NAME": "GPIO35",
"GPIO": "P35",
"IRQ": null,
"DVP": "PD3"
},
"19": {
"C_NAME": "GPIO36",
"GPIO": "P36",
"IRQ": null,
"DVP": "PD4"
},
"20": {
"C_NAME": "GPIO37",
"GPIO": "P37",
"IRQ": null,
"DVP": "PD5"
},
"21": {
"C_NAME": "GPIO38",
"GPIO": "P38",
"IRQ": null,
"DVP": "PD6"
},
"22": {
"C_NAME": "GPIO39",
"GPIO": "P39",
"IRQ": null,
"DVP": "PD7"
},
"23": {
"C_NAME": "GPIO14",
"GPIO": "P14",
"IRQ": null,
"SD": "CLK",
"SPI": "SCK"
},
"24": {
"C_NAME": "GPIO16",
"GPIO": "P16",
"IRQ": null,
"SD": "D0",
"SPI": "MOSI"
},
"25": {
"C_NAME": "GPIO15",
"GPIO": "P15",
"IRQ": null,
"SD": "CMD",
"SPI": "CS"
},
"26": {
"C_NAME": "GPIO17",
"GPIO": "P17",
"IRQ": null,
"SD": "D1",
"SPI": "MISO"
},
"27": {
"C_NAME": "GPIO23",
"GPIO": "P23",
"IRQ": null,
"ADC": 3,
"JTAG": "TDO",
"FLASH": "FSO"
},
"28": {
"C_NAME": "GPIO22",
"GPIO": "P22",
"IRQ": null,
"JTAG": "TDI",
"FLASH": "FSI"
},
"29": {
"C_NAME": "GPIO21",
"GPIO": "P21",
"IRQ": null,
"I2C": "1_SDA",
"JTAG": "TMS",
"I2S": "MCLK",
"FLASH": "^FCS"
},
"30": {
"C_NAME": "GPIO20",
"GPIO": "P20",
"IRQ": null,
"I2C": "1_SCL",
"JTAG": "TCK",
"FLASH": "FSCK"
},
"31": {
"IO": "I",
"CTRL": "CEN"
},
"32": {
"C_NAME": "GPIO8",
"GPIO": "P8",
"IRQ": null,
"PWM": 2
},
"33": {
"C_NAME": "GPIO9",
"GPIO": "P9",
"IRQ": null,
"PWM": 3
},
"34": {
"C_NAME": "GPIO10",
"GPIO": "P10",
"IRQ": null,
"UART": "1_RX"
},
"35": {
"C_NAME": "GPIO11",
"GPIO": "P11",
"IRQ": null,
"UART": "1_TX"
},
"36": {
"C_NAME": "GPIO1",
"GPIO": "P1",
"IRQ": null,
"UART": "2_RX",
"I2C": "2_SDA"
},
"37": {
"C_NAME": "GPIO0",
"GPIO": "P0",
"IRQ": null,
"UART": "2_TX",
"I2C": "2_SCL"
}
}
}
}

View File

@@ -0,0 +1,259 @@
{
"pcb": {
"ic": {
"13": {
"C_NAME": "GPIO28",
"GPIO": "P28",
"IRQ": null,
"USB": "DN"
},
"14": {
"C_NAME": "GPIO25",
"GPIO": "P25",
"IRQ": null,
"USB": "DP"
},
"16": {
"C_NAME": "GPIO12",
"GPIO": "P12",
"IRQ": null,
"ADC": 6,
"UART": "1_CTS"
},
"17": {
"C_NAME": "GPIO13",
"GPIO": "P13",
"IRQ": null,
"ADC": 7,
"UART": "1_RTS"
},
"18": {
"C_NAME": "GPIO33",
"GPIO": "P33",
"IRQ": null,
"DVP": "PD1"
},
"19": {
"C_NAME": "GPIO32",
"GPIO": "P32",
"IRQ": null,
"DVP": "PD0"
},
"20": {
"C_NAME": "GPIO29",
"GPIO": "P29",
"IRQ": null,
"DVP": "PCLK"
},
"21": {
"C_NAME": "GPIO27",
"GPIO": "P27",
"IRQ": null,
"DVP": "MCLK"
},
"22": {
"C_NAME": "GPIO30",
"GPIO": "P30",
"IRQ": null,
"DVP": "HSYNC"
},
"23": {
"C_NAME": "GPIO31",
"GPIO": "P31",
"IRQ": null,
"DVP": "VSYNC"
},
"24": {
"C_NAME": "GPIO34",
"GPIO": "P34",
"IRQ": null,
"DVP": "PD2"
},
"25": {
"C_NAME": "GPIO35",
"GPIO": "P35",
"IRQ": null,
"DVP": "PD3"
},
"26": {
"C_NAME": "GPIO36",
"GPIO": "P36",
"IRQ": null,
"DVP": "PD4"
},
"27": {
"C_NAME": "GPIO37",
"GPIO": "P37",
"IRQ": null,
"DVP": "PD5"
},
"28": {
"C_NAME": "GPIO38",
"GPIO": "P38",
"IRQ": null,
"DVP": "PD6"
},
"29": {
"C_NAME": "GPIO39",
"GPIO": "P39",
"IRQ": null,
"DVP": "PD7"
},
"30": {
"C_NAME": "GPIO19",
"GPIO": "P19",
"IRQ": null,
"SD": "D1"
},
"31": {
"C_NAME": "GPIO17",
"GPIO": "P17",
"IRQ": null,
"SD": "D1",
"SPI": "MISO"
},
"32": {
"C_NAME": "GPIO14",
"GPIO": "P14",
"IRQ": null,
"SD": "CLK",
"SPI": "SCK"
},
"33": {
"C_NAME": "GPIO16",
"GPIO": "P16",
"IRQ": null,
"SD": "D0",
"SPI": "MOSI"
},
"34": {
"C_NAME": "GPIO15",
"GPIO": "P15",
"IRQ": null,
"SD": "CMD",
"SPI": "CS"
},
"35": {
"C_NAME": "GPIO18",
"GPIO": "P18",
"IRQ": null,
"SD": "D2"
},
"36": {
"C_NAME": "GPIO24",
"GPIO": "P24",
"IRQ": null,
"PWM": 4
},
"37": {
"C_NAME": "GPIO26",
"GPIO": "P26",
"IRQ": null,
"PWM": 5,
"IRDA": null
},
"38": {
"C_NAME": "GPIO23",
"GPIO": "P23",
"IRQ": null,
"ADC": 3,
"JTAG": "TDO",
"FLASH": "FSO"
},
"39": {
"C_NAME": "GPIO22",
"GPIO": "P22",
"IRQ": null,
"JTAG": "TDI",
"FLASH": "FSI"
},
"40": {
"C_NAME": "GPIO21",
"GPIO": "P21",
"IRQ": null,
"I2C": "1_SDA",
"JTAG": "TMS",
"I2S": "MCLK",
"FLASH": "^FCS"
},
"41": {
"C_NAME": "GPIO20",
"GPIO": "P20",
"IRQ": null,
"I2C": "1_SCL",
"JTAG": "TCK",
"FLASH": "FSCK"
},
"43": {
"IO": "I",
"CTRL": "CEN"
},
"45": {
"C_NAME": "GPIO5",
"GPIO": "P5",
"IRQ": null,
"ADC": 2,
"I2S": "DOUT"
},
"46": {
"C_NAME": "GPIO3",
"GPIO": "P3",
"IRQ": null,
"ADC": 5,
"I2S": "WS"
},
"47": {
"C_NAME": "GPIO4",
"GPIO": "P4",
"IRQ": null,
"ADC": 1,
"I2S": "DIN"
},
"48": {
"C_NAME": "GPIO2",
"GPIO": "P2",
"IRQ": null,
"ADC": 4,
"I2S": "SCK"
},
"49": {
"C_NAME": "GPIO6",
"GPIO": "P6",
"IRQ": null,
"PWM": 0
},
"50": {
"C_NAME": "GPIO7",
"GPIO": "P7",
"IRQ": null,
"PWM": 1
},
"62": {
"C_NAME": "GPIO10",
"GPIO": "P10",
"IRQ": null,
"UART": "1_RX"
},
"63": {
"C_NAME": "GPIO11",
"GPIO": "P11",
"IRQ": null,
"UART": "1_TX"
},
"64": {
"C_NAME": "GPIO1",
"GPIO": "P1",
"IRQ": null,
"UART": "2_RX",
"I2C": "2_SDA"
},
"65": {
"C_NAME": "GPIO0",
"GPIO": "P0",
"IRQ": null,
"UART": "2_TX",
"I2C": "2_SCL"
}
}
}
}

View File

@@ -0,0 +1,120 @@
{
"pcb": {
"scale": 11,
"test_pads": {
"TRST": "cb2l.back.rst.anchor",
"TRX1": "cb2l.back.u1_rxd.anchor",
"TTX1": "cb2l.back.u1_txd.anchor",
"TTX2": "cb2l.back.u2_txd.anchor",
"TGND": "cb2l.back.gnd.anchor",
"TCSN": "cb2l.back.f_csn.anchor"
},
"back": [
{
"type": "rect",
"pos": "0,0",
"size": "15,17.3",
"preset": "${MASK_PRESET}"
},
{
"name": "pins_horz7_2mm_0.7mm",
"pos": "1.15,17.3",
"vars": {
"PINTYPE": "${PINTYPE_HORZ}",
"PINDIR": "down"
}
},
{
"name": "test_pad_1mm",
"pos": "1.5,7.5"
},
{
"name": "label_line_2mm_down",
"pos": "0.4,4.7",
"vars": {
"DIR": "right",
"W": 0.2,
"H": 2.7
}
},
{
"id": "u1_rxd.anchor",
"type": "rect",
"pos": "0.9,5.0",
"size": "0,0",
"label_dir": "left",
"label_size": 2
},
{
"name": "test_pad_1mm",
"pos": "2.2,5.9"
},
{
"id": "u2_txd",
"name": "label_line_2mm_up",
"pos": "2.2,5.1",
"vars": {
"DIR": "left",
"W": 1.5,
"H": 2
}
},
{
"name": "test_pad_1mm",
"pos": "4.0,5.9"
},
{
"id": "u1_txd",
"name": "label_line_2mm_up",
"pos": "4.0,5.1",
"vars": {
"DIR": "left",
"W": 3.3,
"H": 4
}
},
{
"name": "test_pad_1mm",
"pos": "9.2,5.9"
},
{
"id": "gnd",
"name": "label_line_2mm_up",
"pos": "9.2,5.1",
"vars": {
"DIR": "left",
"W": 8.5,
"H": 6
}
},
{
"name": "test_pad_1mm",
"pos": "2.2,10.7"
},
{
"id": "rst",
"name": "label_line_2mm_up",
"pos": "2.2,9.9",
"vars": {
"DIR": "left",
"W": 1.5,
"H": 1.0
}
},
{
"name": "test_pad_1mm",
"pos": "2.2,12.0"
},
{
"id": "f_csn",
"name": "label_line_2mm_down",
"pos": "2.2,12.8",
"vars": {
"DIR": "left",
"W": 1.5,
"H": 1.0
}
}
]
}
}

View File

@@ -0,0 +1,66 @@
{
"pcb": {
"templates": [
"tuya2l",
"rf-15mm-type1",
"tuya2l-shield"
],
"vars": {
"MASK_PRESET": "mask_white",
"TRACE_COLOR": "#E0E0E0",
"SILK_COLOR": "black",
"PINTYPE_HORZ": "pin_horz_2mm_cast_hole"
},
"pinout_hidden": "I2S,I2C,JTAG,FLASH",
"pinout": {
"1": {
"IC": 24,
"ARD": "D0"
},
"2": {
"IC": 23,
"ARD": "D1"
},
"3": {
"IC": 22,
"ARD": "D2"
},
"4": {
"IC": 15,
"ARD": "D3"
},
"5": {
"IC": 16,
"ARD": "D4"
},
"6": {
"GND": null
},
"7": {
"PWR": 3.3
},
"TGND": {
"GND": null
},
"TRST": {
"CTRL": "CEN"
},
"TRX1": {
"IC": 26,
"ARD": "D5"
},
"TTX2": {
"IC": 29,
"ARD": "D6"
},
"TTX1": {
"IC": 27,
"ARD": "D7"
},
"TCSN": {
"IC": 19,
"ARD": "D8"
}
}
}
}

View File

@@ -0,0 +1,83 @@
{
"pcb": {
"templates": [
"esp12s",
"esp12e-shield",
"tuya-16x24",
"rf-16mm-type1"
],
"vars": {
"MASK_PRESET": "mask_blue_light",
"TRACE_COLOR": "#58839B",
"SILK_COLOR": "white",
"PINTYPE_VERT": "pin_vert_2mm_cast_hole",
"PINTYPE_HORZ": "pin_horz_2mm_cast_hole"
},
"pinout_hidden": "I2S,SD,SPI,I2C,JTAG,FLASH",
"pinout": {
"1": {
"IC": 21
},
"2": {
"IC": 17,
"ARD": [
"D0",
"A0"
]
},
"3": {
"IC": 21
},
"4": {
"IC": 11,
"ARD": "D1"
},
"5": {
"IC": 15,
"ARD": "D2"
},
"6": {
"IC": 16,
"ARD": "D3"
},
"7": {
"IC": 22,
"ARD": "D4"
},
"8": {
"PWR": 3.3
},
"9": {
"GND": null
},
"10": {
"IC": 25,
"ARD": "D5"
},
"11": {
"IC": 29,
"ARD": "D6"
},
"12": {
"IC": 19,
"ARD": "D7"
},
"13": {
"IC": 24,
"ARD": "D8"
},
"14": {
"IC": 23,
"ARD": "D9"
},
"15": {
"IC": 26,
"ARD": "D10"
},
"16": {
"IC": 27,
"ARD": "D11"
}
}
}
}

108
boards/_base/pcb/cb3s.json Normal file
View File

@@ -0,0 +1,108 @@
{
"pcb": {
"templates": [
"esp12e-22",
"esp12e-shield-nohole",
"tuya-16x24",
"rf-16mm-type1"
],
"vars": {
"MASK_PRESET": "mask_blue_light",
"TRACE_COLOR": "#58839B",
"SILK_COLOR": "white",
"PINTYPE_VERT": "pin_vert_2mm_cast_nohole",
"PINTYPE_HORZ": "pin_horz_2mm_cast_nohole"
},
"pinout_hidden": "I2S,SD,SPI,SCL2",
"pinout": {
"1": {
"IC": 21
},
"2": {
"IC": 17,
"ARD": [
"D0",
"A0"
]
},
"3": {
"IC": 21
},
"4": {
"IC": 11,
"ARD": "D1"
},
"5": {
"IC": 15,
"ARD": "D2"
},
"6": {
"IC": 16,
"ARD": "D3"
},
"7": {
"IC": 22,
"ARD": "D4"
},
"8": {
"PWR": 3.3
},
"9": {
"GND": null
},
"10": {
"IC": 25,
"ARD": "D5"
},
"11": {
"IC": 29,
"ARD": "D6"
},
"12": {
"IC": 19,
"ARD": "D7"
},
"13": {
"IC": 24,
"ARD": "D8"
},
"14": {
"IC": 23,
"ARD": "D9"
},
"15": {
"IC": 26,
"ARD": "D10"
},
"16": {
"IC": 27,
"ARD": "D11"
},
"17": {
"IC": 17,
"ARD": [
"D0",
"A0"
]
},
"18": {
"IC": 18,
"ARD": "D12"
},
"19": {
"IC": 19,
"ARD": "D7"
},
"20": {
"IC": 20,
"ARD": "D13"
},
"21": {
"NC": null
},
"22": {
"NC": null
}
}
}
}

106
boards/_base/pcb/cb3se.json Normal file
View File

@@ -0,0 +1,106 @@
{
"pcb": {
"templates": [
"esp12e-22",
"esp12e-shield-nohole",
"tuya-16x24",
"rf-16mm-type1"
],
"vars": {
"MASK_PRESET": "mask_blue_light",
"TRACE_COLOR": "#58839B",
"SILK_COLOR": "white",
"PINTYPE_VERT": "pin_vert_2mm_cast_nohole",
"PINTYPE_HORZ": "pin_horz_2mm_cast_nohole"
},
"pinout_hidden": "I2S,SD,JTAG,FLASH,SCL1",
"pinout": {
"1": {
"IC": 21
},
"2": {
"IC": 17,
"ARD": [
"D0",
"A0"
]
},
"3": {
"IC": 21
},
"4": {
"IC": 11,
"ARD": "D1"
},
"5": {
"IC": 15,
"ARD": "D2"
},
"6": {
"IC": 16,
"ARD": "D3"
},
"7": {
"IC": 22,
"ARD": "D4"
},
"8": {
"PWR": 3.3
},
"9": {
"GND": null
},
"10": {
"IC": 25,
"ARD": "D5"
},
"11": {
"IC": 29,
"ARD": "D6"
},
"12": {
"IC": 28,
"ARD": "D7"
},
"13": {
"IC": 24,
"ARD": "D8"
},
"14": {
"IC": 23,
"ARD": "D9"
},
"15": {
"IC": 26,
"ARD": "D10"
},
"16": {
"IC": 27,
"ARD": "D11"
},
"17": {
"IC": 13,
"ARD": "D12"
},
"18": {
"IC": 18,
"ARD": "D13"
},
"19": {
"GND": null
},
"20": {
"IC": 20,
"ARD": "D14"
},
"21": {
"IC": 14,
"ARD": "D15"
},
"22": {
"IC": 12,
"ARD": "D16"
}
}
}
}

View File

@@ -0,0 +1,18 @@
{
"build": {
"ldscript_sdk": "rlx8711B-symbol-v02-img2_xip1_4M_cpp.ld",
"ldscript_arduino": "rlx8711B-symbol-v02-img2_xip1_4M_cpp.ld",
"amb_boot_all": "boot_all_C556.bin"
},
"flash": {
"ota1": "0x00B000+0xF5000",
"ota2": "0x100000+0xF5000",
"kvs": "0x1F5000+0x8000",
"userdata": "0x1FD000+0x202000",
"rdp": "0x3FF000+0x1000"
},
"upload": {
"flash_size": 4194304,
"maximum_size": 1003520
}
}

View File

@@ -3,7 +3,7 @@
"realtek-ambz",
"realtek-ambz-2mb-468k",
"realtek-ambz-bx",
"pcb/ic-rtl8710bn",
"ic/rtl8710bn",
"pcb/bw12"
],
"build": {
@@ -17,6 +17,7 @@
"symbol": "BW12"
},
"doc": {
"fccid": "2ARI3-BW1X",
"links": {
"Vendor datasheet": "https://docs.ai-thinker.com/_media/rtl8710/hardware/bw12_datasheet_en.pdf"
}

View File

@@ -11,7 +11,7 @@
- [Vendor datasheet](https://docs.ai-thinker.com/_media/rtl8710/hardware/bw12_datasheet_en.pdf)
Parameter | Value
-------------|----------------------------------
-------------|------------------------------------------
Board code | `bw12`
MCU | RTL8710BX
Manufacturer | Realtek
@@ -22,6 +22,7 @@ RAM size | 256 KiB
Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
FCC ID | [2ARI3-BW1X](https://fccid.io/2ARI3-BW1X)
## Usage
@@ -36,6 +37,15 @@ board = bw12
framework = arduino
```
In ESPHome YAML:
```yaml
libretuya:
board: bw12
framework:
version: dev
```
## Pinout
![Pinout](pinout_bw12.svg)

View File

@@ -49,3 +49,32 @@
#define PIN_SERIAL0_TX 10u // PA_23
#define PIN_SERIAL2_RX 1u // PA_29
#define PIN_SERIAL2_TX 5u // PA_30
// Pin function macros
// -------------------
#define PIN_FUNCTION_ADC1 3u // PA_19
#define PIN_FUNCTION_CS0 3u // PA_19
#define PIN_FUNCTION_CS1 3u // PA_19
#define PIN_FUNCTION_CTS0 3u // PA_19
#define PIN_FUNCTION_MISO0 4u // PA_22
#define PIN_FUNCTION_MISO1 4u // PA_22
#define PIN_FUNCTION_MOSI0 10u // PA_23
#define PIN_FUNCTION_MOSI1 10u // PA_23
#define PIN_FUNCTION_PA00 2u // PA_0
#define PIN_FUNCTION_PA05 0u // PA_5
#define PIN_FUNCTION_PA12 7u // PA_12
#define PIN_FUNCTION_PA14 6u // PA_14
#define PIN_FUNCTION_PA15 8u // PA_15
#define PIN_FUNCTION_PA18 9u // PA_18
#define PIN_FUNCTION_PA19 3u // PA_19
#define PIN_FUNCTION_PA22 4u // PA_22
#define PIN_FUNCTION_PA23 10u // PA_23
#define PIN_FUNCTION_PA29 1u // PA_29
#define PIN_FUNCTION_PA30 5u // PA_30
#define PIN_FUNCTION_RTS0 4u // PA_22
#define PIN_FUNCTION_RX0 9u // PA_18
#define PIN_FUNCTION_RX2 1u // PA_29
#define PIN_FUNCTION_SCK0 9u // PA_18
#define PIN_FUNCTION_SCK1 9u // PA_18
#define PIN_FUNCTION_TX0 10u // PA_23
#define PIN_FUNCTION_TX2 5u // PA_30

View File

@@ -3,7 +3,7 @@
"realtek-ambz2",
"realtek-ambz2-8720",
"realtek-ambz2-2mb-992k",
"pcb/ic-rtl8720cf",
"ic/rtl8720cf",
"pcb/bw15"
],
"build": {
@@ -17,6 +17,7 @@
"symbol": "BW15"
},
"doc": {
"fccid": "2AXVG-BW15",
"links": {
"Vendor datasheet": "https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf"
}

View File

@@ -8,7 +8,7 @@
- [Vendor datasheet](https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf)
Parameter | Value
-------------|--------------------------
-------------|------------------------------------------
Board code | `bw15`
MCU | RTL8720CF
Manufacturer | Realtek
@@ -20,6 +20,7 @@ Voltage | 3.0V - 3.6V
I/O | 13x GPIO, 8x PWM, 3x UART
Wi-Fi | 802.11 b/g/n
BLE | v4.2
FCC ID | [2AXVG-BW15](https://fccid.io/2AXVG-BW15)
## Usage
@@ -34,6 +35,15 @@ board = bw15
framework = arduino
```
In ESPHome YAML:
```yaml
libretuya:
board: bw15
framework:
version: dev
```
## Pinout
![Pinout](pinout_bw15.svg)

23
boards/cb2l.json Normal file
View File

@@ -0,0 +1,23 @@
{
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231n-tuya",
"ic/bk7231-qfn32",
"pcb/cb2l",
"pcb/cb2l-test"
],
"build": {
"mcu": "bk7231n",
"variant": "cb2l"
},
"name": "CB2L Wi-Fi Module",
"url": "https://developer.tuya.com/en/docs/iot/cb2l-module-datasheet?id=Kai2eku1m3pyl",
"vendor": "Tuya Inc.",
"doc": {
"fccid": "2ANDL-CB2L"
},
"pcb": {
"symbol": "CB2L"
}
}

83
boards/cb2l/README.md Normal file
View File

@@ -0,0 +1,83 @@
# CB2L Wi-Fi Module
*by Tuya Inc.*
[Product page](https://developer.tuya.com/en/docs/iot/cb2l-module-datasheet?id=Kai2eku1m3pyl)
- [General info](../../docs/platform/beken-72xx/README.md)
- [Flashing guide](../../docs/platform/beken-72xx/flashing.md)
- [BkWriter v1.6.0](https://images.tuyacn.com/smart/bk_writer1.60/bk_writer1.60.exe)
Parameter | Value
-------------|------------------------------------------
Board code | `cb2l`
MCU | BK7231N
Manufacturer | Beken
Series | BK72XX
Frequency | 120 MHz
Flash size | 2 MiB
RAM size | 256 KiB
Voltage | 3.0V - 3.6V
I/O | 9x GPIO, 5x PWM, 2x UART
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
FCC ID | [2ANDL-CB2L](https://fccid.io/2ANDL-CB2L)
## Usage
**Board code:** `cb2l`
In `platformio.ini`:
```ini
[env:cb2l]
platform = libretuya
board = cb2l
framework = arduino
```
In ESPHome YAML:
```yaml
libretuya:
board: cb2l
framework:
version: dev
```
## Pinout
![Pinout](pinout_cb2l.svg)
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other
----|-----|----------|----------|-----|------|------
D0 | P8 | | | | PWM2 |
D1 | P7 | | | | PWM1 |
D2 | P6 | | | | PWM0 |
D3 | P26 | | | | PWM5 |
D4 | P24 | | | | PWM4 |
D5 | P10 | UART1_RX | | | |
D6 | P0 | UART2_TX | I2C2_SCL | | |
D7 | P11 | UART1_TX | | | |
D8 | P21 | | I2C1_SDA | | | TMS
## Flash memory map
Flash size: 2 MiB / 2,097,152 B / 0x200000
Hex values are in bytes.
Name | Start | Length | End
----------------|----------|--------------------|---------
Bootloader | 0x000000 | 68 KiB / 0x11000 | 0x011000
App Image | 0x011000 | 1.1 MiB / 0x119000 | 0x12A000
OTA Image | 0x12A000 | 664 KiB / 0xA6000 | 0x1D0000
Calibration | 0x1D0000 | 4 KiB / 0x1000 | 0x1D1000
Network Data | 0x1D1000 | 4 KiB / 0x1000 | 0x1D2000
TLV Store | 0x1D2000 | 4 KiB / 0x1000 | 0x1D3000
Key-Value Store | 0x1D3000 | 32 KiB / 0x8000 | 0x1DB000
User Data | 0x1DB000 | 148 KiB / 0x25000 | 0x200000
Bootloader and app partitions contain CRC16 sums every 32 bytes. That results in the actual flash offsets/sizes not aligned to sector boundaries. To simplify calculations, the values shown in the table (extracted from bootloader's partition table) were aligned to 4096 bytes.

258
boards/cb2l/pinout_cb2l.svg Normal file
View File

@@ -0,0 +1,258 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" baseProfile="full" height="500" version="1.1" viewBox="0,0,93.0909090909091,45.45454545454545" width="1024">
<defs/>
<rect fill="white" height="45.45454545454545" stroke="black" stroke-width="0.1" width="93.0909090909091" x="0" y="0"/>
<linearGradient gradientUnits="objectBoundingBox" id="id1" x1="1.0" x2="0.0" y1="0.0" y2="1.0">
<stop offset="0%" stop-color="#f9f9f9"/>
<stop offset="100%" stop-color="#ededed"/>
</linearGradient>
<rect fill="url(#id1) none" height="17.2" stroke="#b5a739" stroke-width="0.1" width="14.9" x="17.622727272727275" y="9.677272727272728"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin1.trace" width="1.2" x="18.472727272727273" y="25.227272727272727"/>
<circle cx="19.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin1.cast" r="0.35"/>
<circle cx="19.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin1.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin2.trace" width="1.2" x="20.472727272727273" y="25.227272727272727"/>
<circle cx="21.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin2.cast" r="0.35"/>
<circle cx="21.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin2.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin3.trace" width="1.2" x="22.472727272727273" y="25.227272727272727"/>
<circle cx="23.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin3.cast" r="0.35"/>
<circle cx="23.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin3.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin4.trace" width="1.2" x="24.472727272727276" y="25.227272727272727"/>
<circle cx="25.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin4.cast" r="0.35"/>
<circle cx="25.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin4.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin5.trace" width="1.2" x="26.472727272727276" y="25.227272727272727"/>
<circle cx="27.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin5.cast" r="0.35"/>
<circle cx="27.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin5.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin6.trace" width="1.2" x="28.472727272727276" y="25.227272727272727"/>
<circle cx="29.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin6.cast" r="0.35"/>
<circle cx="29.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin6.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin7.trace" width="1.2" x="30.472727272727276" y="25.227272727272727"/>
<circle cx="31.072727272727274" cy="26.92727272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin7.cast" r="0.35"/>
<circle cx="31.072727272727274" cy="25.877272727272725" fill="#fff" id="pins_horz7_2mm_0.7mm.pin7.hole" r="0.35"/>
<rect fill="#4e4c4c" height="2.0" width="0.2" x="18.972727272727273" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="0.2" x="18.772727272727273" y="28.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label1.anchor" width="0.0" x="19.272727272727273" y="29.02727272727273"/>
<rect fill="#4e4c4c" height="4.0" width="0.2" x="20.972727272727273" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="2.2" x="18.772727272727273" y="30.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label2.anchor" width="0.0" x="19.272727272727273" y="31.027272727272724"/>
<rect fill="#4e4c4c" height="6.0" width="0.2" x="22.972727272727276" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="4.2" x="18.772727272727273" y="32.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label3.anchor" width="0.0" x="19.272727272727273" y="33.027272727272724"/>
<rect fill="#4e4c4c" height="8.0" width="0.2" x="24.972727272727276" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="6.2" x="18.772727272727273" y="34.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label4.anchor" width="0.0" x="19.272727272727273" y="35.027272727272724"/>
<rect fill="#4e4c4c" height="8.0" width="0.2" x="26.972727272727276" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="4.2" x="27.072727272727274" y="34.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label5.anchor" width="0.0" x="30.772727272727273" y="35.027272727272724"/>
<rect fill="#4e4c4c" height="6.0" width="0.2" x="28.972727272727276" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="2.2" x="29.072727272727274" y="32.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label6.anchor" width="0.0" x="30.772727272727273" y="33.027272727272724"/>
<rect fill="#4e4c4c" height="4.0" width="0.2" x="30.972727272727276" y="27.127272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="0.2" x="31.072727272727274" y="30.92727272727273"/>
<rect height="0.0" id="tuya2l.front.pins.label7.anchor" width="0.0" x="30.772727272727273" y="31.027272727272724"/>
<text fill="#000" font-family="Consolas" font-size="1.0" x="21.572727272727274" y="15.127272727272727">CB2L</text>
<rect fill="#e0e0e0" height="5.2" width="0.5" x="18.372727272727275" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="0.5" width="4.6" x="18.372727272727275" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="5.2" width="0.5" x="20.572727272727274" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="3.0" width="0.5" x="22.472727272727276" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="0.5" width="3.0" x="22.472727272727276" y="12.627272727272727"/>
<rect fill="#e0e0e0" height="3.0" width="0.5" x="24.972727272727276" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="0.5" width="2.7" x="24.972727272727276" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="3.0" width="0.5" x="27.172727272727272" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="0.5" width="3.0" x="27.172727272727272" y="12.627272727272727"/>
<rect fill="#e0e0e0" height="3.0" width="0.5" x="29.672727272727272" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="0.5" width="2.5" x="29.672727272727272" y="10.127272727272727"/>
<rect fill="#e0e0e0" height="4.4" width="0.5" x="31.672727272727272" y="10.127272727272727"/>
<linearGradient gradientUnits="objectBoundingBox" id="id2" x1="1.0" x2="0.0" y1="0.0" y2="1.0">
<stop offset="0%" stop-color="whitesmoke"/>
<stop offset="100%" stop-color="#999"/>
</linearGradient>
<rect fill="url(#id2) none" height="9.0" rx="0.5" ry="0.5" width="13.6" x="18.272727272727273" y="15.627272727272727"/>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="16.172727272727272" y="28.92727272727273"/>
<g transform="translate(12.387086626672172,28.22727272727273)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="13.872727272727273" y="29.02727272727273">P8</text>
<g transform="translate(9.387086626672172,28.22727272727273)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="10.572727272727274" y="29.02727272727273">D0</text>
<g transform="translate(5.787086626672172,28.22727272727273)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="7.272727272727274" y="29.02727272727273">PWM2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="16.172727272727272" y="30.927272727272722"/>
<g transform="translate(12.387086626672172,30.227272727272723)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="13.872727272727273" y="31.027272727272724">P7</text>
<g transform="translate(9.387086626672172,30.227272727272723)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="10.572727272727274" y="31.027272727272724">D1</text>
<g transform="translate(5.787086626672172,30.227272727272723)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="7.272727272727274" y="31.027272727272724">PWM1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="16.172727272727272" y="32.92727272727272"/>
<g transform="translate(12.387086626672172,32.22727272727273)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="13.872727272727273" y="33.027272727272724">P6</text>
<g transform="translate(9.387086626672172,32.22727272727273)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="10.572727272727274" y="33.027272727272724">D2</text>
<g transform="translate(5.787086626672172,32.22727272727273)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="7.272727272727274" y="33.027272727272724">PWM0</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="16.172727272727272" y="34.92727272727272"/>
<g transform="translate(12.387086626672172,34.22727272727273)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="13.872727272727273" y="35.027272727272724">P26</text>
<g transform="translate(9.387086626672172,34.22727272727273)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="10.572727272727274" y="35.027272727272724">D3</text>
<g transform="translate(5.787086626672172,34.22727272727273)">
<rect fill="#aeafc1" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="7.272727272727274" y="35.027272727272724">IRDA</text>
<g transform="translate(2.1870866266721727,34.22727272727273)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="3.6727272727272746" y="35.027272727272724">PWM5</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="31.272727272727273" y="34.92727272727272"/>
<g transform="translate(34.687086626672176,34.22727272727273)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="36.17272727272728" y="35.027272727272724">P24</text>
<g transform="translate(38.28708662667218,34.22727272727273)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="39.472727272727276" y="35.027272727272724">D4</text>
<g transform="translate(41.28708662667218,34.22727272727273)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="42.77272727272728" y="35.027272727272724">PWM4</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="31.272727272727273" y="32.92727272727272"/>
<g transform="translate(34.687086626672176,32.22727272727273)">
<rect fill="#000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="36.17272727272728" y="33.027272727272724">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="31.272727272727273" y="30.927272727272722"/>
<g transform="translate(34.687086626672176,30.227272727272723)">
<rect fill="#cd3c24" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="36.17272727272728" y="31.027272727272724">3V3</text>
<linearGradient gradientUnits="objectBoundingBox" id="id3" x1="1.0" x2="0.0" y1="0.0" y2="1.0">
<stop offset="0%" stop-color="#f9f9f9"/>
<stop offset="100%" stop-color="#ededed"/>
</linearGradient>
<rect fill="url(#id3) none" height="17.2" stroke="#b5a739" stroke-width="0.1" width="14.9" x="68.71818181818182" y="14.852272727272727"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin1.trace" width="1.2" x="69.56818181818183" y="30.402272727272727"/>
<circle cx="70.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin1.cast" r="0.35"/>
<circle cx="70.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin1.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin2.trace" width="1.2" x="71.56818181818183" y="30.402272727272727"/>
<circle cx="72.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin2.cast" r="0.35"/>
<circle cx="72.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin2.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin3.trace" width="1.2" x="73.56818181818183" y="30.402272727272727"/>
<circle cx="74.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin3.cast" r="0.35"/>
<circle cx="74.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin3.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin4.trace" width="1.2" x="75.56818181818183" y="30.402272727272727"/>
<circle cx="76.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin4.cast" r="0.35"/>
<circle cx="76.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin4.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin5.trace" width="1.2" x="77.56818181818183" y="30.402272727272727"/>
<circle cx="78.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin5.cast" r="0.35"/>
<circle cx="78.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin5.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin6.trace" width="1.2" x="79.56818181818183" y="30.402272727272727"/>
<circle cx="80.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin6.cast" r="0.35"/>
<circle cx="80.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin6.hole" r="0.35"/>
<rect fill="#e5b472" height="1.7" id="pins_horz7_2mm_0.7mm.pin7.trace" width="1.2" x="81.56818181818183" y="30.402272727272727"/>
<circle cx="82.16818181818182" cy="32.10227272727273" fill="#fff" id="pins_horz7_2mm_0.7mm.pin7.cast" r="0.35"/>
<circle cx="82.16818181818182" cy="31.052272727272726" fill="#fff" id="pins_horz7_2mm_0.7mm.pin7.hole" r="0.35"/>
<circle cx="70.16818181818182" cy="22.302272727272726" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="2.7" width="0.2" x="68.96818181818182" y="19.702272727272728"/>
<rect fill="#4e4c4c" height="0.2" width="0.4" x="69.06818181818183" y="22.202272727272728"/>
<rect height="0.0" id="label_line_2mm_down.anchor" width="0.0" x="68.96818181818182" y="22.302272727272726"/>
<rect height="0.0" id="cb2l.back.u1_rxd.anchor" width="0.0" x="69.56818181818183" y="19.802272727272726"/>
<circle cx="70.86818181818182" cy="20.702272727272728" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="2.0" width="0.2" x="70.76818181818182" y="17.702272727272724"/>
<rect fill="#4e4c4c" height="0.2" width="1.7" x="69.06818181818183" y="17.702272727272724"/>
<rect height="0.0" id="cb2l.back.u2_txd.anchor" width="0.0" x="69.56818181818183" y="17.802272727272726"/>
<circle cx="72.66818181818182" cy="20.702272727272728" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="4.0" width="0.2" x="72.56818181818183" y="15.702272727272724"/>
<rect fill="#4e4c4c" height="0.2" width="3.5" x="69.06818181818183" y="15.702272727272724"/>
<rect height="0.0" id="cb2l.back.u1_txd.anchor" width="0.0" x="69.56818181818183" y="15.802272727272726"/>
<circle cx="77.86818181818182" cy="20.702272727272728" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="6.0" width="0.2" x="77.76818181818182" y="13.702272727272724"/>
<rect fill="#4e4c4c" height="0.2" width="8.7" x="69.06818181818181" y="13.702272727272724"/>
<rect height="0.0" id="cb2l.back.gnd.anchor" width="0.0" x="69.56818181818181" y="13.802272727272726"/>
<circle cx="70.86818181818182" cy="25.502272727272725" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="1.0" width="0.2" x="70.76818181818182" y="23.502272727272725"/>
<rect fill="#4e4c4c" height="0.2" width="1.7" x="69.06818181818183" y="23.502272727272725"/>
<rect height="0.0" id="cb2l.back.rst.anchor" width="0.0" x="69.56818181818183" y="23.602272727272727"/>
<circle cx="70.86818181818182" cy="26.802272727272726" fill="#e5b472" r="0.5"/>
<rect fill="#4e4c4c" height="1.0" width="0.2" x="70.76818181818182" y="27.802272727272726"/>
<rect fill="#4e4c4c" height="0.2" width="1.7" x="69.06818181818183" y="28.602272727272727"/>
<rect height="0.0" id="cb2l.back.f_csn.anchor" width="0.0" x="69.56818181818183" y="28.702272727272728"/>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818182" y="13.702272727272726"/>
<g transform="translate(62.68254117212672,13.002272727272725)">
<rect fill="#000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="13.802272727272726">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818183" y="23.502272727272725"/>
<g transform="translate(62.68254117212672,22.802272727272726)">
<rect fill="#ed602e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="23.602272727272727">CEN</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818183" y="19.702272727272724"/>
<g transform="translate(62.68254117212672,19.002272727272725)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="19.802272727272726">P10</text>
<g transform="translate(59.682541172126726,19.002272727272725)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="60.868181818181824" y="19.802272727272726">D5</text>
<g transform="translate(56.082541172126724,19.002272727272725)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.56818181818183" y="19.802272727272726">RX1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818183" y="17.702272727272724"/>
<g transform="translate(62.68254117212672,17.002272727272725)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="17.802272727272726">P0</text>
<g transform="translate(59.682541172126726,17.002272727272725)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="60.868181818181824" y="17.802272727272726">D6</text>
<g transform="translate(56.082541172126724,17.002272727272725)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.56818181818183" y="17.802272727272726">TX2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818183" y="15.702272727272726"/>
<g transform="translate(62.68254117212672,15.002272727272725)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="15.802272727272726">P11</text>
<g transform="translate(59.682541172126726,15.002272727272725)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="60.868181818181824" y="15.802272727272726">D7</text>
<g transform="translate(56.082541172126724,15.002272727272725)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.56818181818183" y="15.802272727272726">TX1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="66.46818181818183" y="28.602272727272727"/>
<g transform="translate(62.68254117212672,27.902272727272727)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.16818181818182" y="28.702272727272728">P21</text>
<g transform="translate(59.682541172126726,27.902272727272727)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="60.868181818181824" y="28.702272727272728">D8</text>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1 @@
#include "variant.h"

30
boards/cb2l/variant.cpp Normal file
View File

@@ -0,0 +1,30 @@
/* This file was auto-generated from cb2l.json using boardgen */
#include <Arduino.h>
extern "C" {
// clang-format off
PinInfo pinTable[PINS_COUNT] = {
// D0: P8, PWM2
{GPIO8, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D1: P7, PWM1
{GPIO7, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D2: P6, PWM0
{GPIO6, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D3: P26, PWM5, IRDA
{GPIO26, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D4: P24, PWM4
{GPIO24, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D5: P10, UART1_RX
{GPIO10, PIN_GPIO | PIN_IRQ | PIN_UART, PIN_NONE, 0},
// D6: P0, UART2_TX, I2C2_SCL
{GPIO0, PIN_GPIO | PIN_IRQ | PIN_I2C | PIN_UART, PIN_NONE, 0},
// D7: P11, UART1_TX
{GPIO11, PIN_GPIO | PIN_IRQ | PIN_UART, PIN_NONE, 0},
// D8: P21, I2C1_SDA, TMS, MCLK, ^FCS
{GPIO21, PIN_GPIO | PIN_IRQ | PIN_I2C | PIN_I2S | PIN_JTAG, PIN_NONE, 0},
};
// clang-format on
} // extern "C"

49
boards/cb2l/variant.h Normal file
View File

@@ -0,0 +1,49 @@
/* This file was auto-generated from cb2l.json using boardgen */
#pragma once
#include <WVariant.h>
// clang-format off
// Pins
// ----
#define PINS_COUNT 9
#define NUM_DIGITAL_PINS 9
#define NUM_ANALOG_OUTPUTS 0
// SPI Interfaces
// --------------
#define SPI_INTERFACES_COUNT 0
// Wire Interfaces
// ---------------
#define WIRE_INTERFACES_COUNT 0
// Serial ports
// ------------
#define SERIAL_INTERFACES_COUNT 1
#define PIN_SERIAL1_RX 5u // GPIO10
#define PIN_SERIAL1_TX 7u // GPIO11
// Pin function macros
// -------------------
#define PIN_FUNCTION_P0 6u // GPIO0
#define PIN_FUNCTION_P6 2u // GPIO6
#define PIN_FUNCTION_P7 1u // GPIO7
#define PIN_FUNCTION_P8 0u // GPIO8
#define PIN_FUNCTION_P10 5u // GPIO10
#define PIN_FUNCTION_P11 7u // GPIO11
#define PIN_FUNCTION_P21 8u // GPIO21
#define PIN_FUNCTION_P24 4u // GPIO24
#define PIN_FUNCTION_P26 3u // GPIO26
#define PIN_FUNCTION_PWM0 2u // GPIO6
#define PIN_FUNCTION_PWM1 1u // GPIO7
#define PIN_FUNCTION_PWM2 0u // GPIO8
#define PIN_FUNCTION_PWM4 4u // GPIO24
#define PIN_FUNCTION_PWM5 3u // GPIO26
#define PIN_FUNCTION_RX1 5u // GPIO10
#define PIN_FUNCTION_SCL2 6u // GPIO0
#define PIN_FUNCTION_SDA1 8u // GPIO21
#define PIN_FUNCTION_TX1 7u // GPIO11
#define PIN_FUNCTION_TX2 6u // GPIO0

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231n",
"beken-7231n-tuya",
"pcb/ic-bk7231-qfn32",
"ic/bk7231-qfn32",
"pcb/cb2s",
"pcb/cb2s-test"
],
@@ -14,6 +14,9 @@
"name": "CB2S Wi-Fi Module",
"url": "https://developer.tuya.com/en/docs/iot/cb2s-module-datasheet?id=Kafgfsa2aaypq",
"vendor": "Tuya Inc.",
"doc": {
"fccid": "2ANDL-CB2S"
},
"pcb": {
"symbol": "CB2S"
}

View File

@@ -9,7 +9,7 @@
- [BkWriter v1.6.0](https://images.tuyacn.com/smart/bk_writer1.60/bk_writer1.60.exe)
Parameter | Value
-------------|----------------------------------
-------------|------------------------------------------
Board code | `cb2s`
MCU | BK7231N
Manufacturer | Beken
@@ -21,6 +21,7 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 5x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
FCC ID | [2ANDL-CB2S](https://fccid.io/2ANDL-CB2S)
## Usage
@@ -35,6 +36,15 @@ board = cb2s
framework = arduino
```
In ESPHome YAML:
```yaml
libretuya:
board: cb2s
framework:
version: dev
```
## Pinout
![Pinout](pinout_cb2s.svg)
@@ -67,8 +77,9 @@ Name | Start | Length | End
Bootloader | 0x000000 | 68 KiB / 0x11000 | 0x011000
App Image | 0x011000 | 1.1 MiB / 0x119000 | 0x12A000
OTA Image | 0x12A000 | 664 KiB / 0xA6000 | 0x1D0000
TLV Store | 0x1D0000 | 4 KiB / 0x1000 | 0x1D1000
Network Data | 0x1D1000 | 8 KiB / 0x2000 | 0x1D3000
Calibration | 0x1D0000 | 4 KiB / 0x1000 | 0x1D1000
Network Data | 0x1D1000 | 4 KiB / 0x1000 | 0x1D2000
TLV Store | 0x1D2000 | 4 KiB / 0x1000 | 0x1D3000
Key-Value Store | 0x1D3000 | 32 KiB / 0x8000 | 0x1DB000
User Data | 0x1DB000 | 148 KiB / 0x25000 | 0x200000

View File

@@ -35,3 +35,30 @@
#define PIN_SERIAL1_TX 5u // GPIO11
#define PIN_SERIAL2_RX 9u // GPIO1
#define PIN_SERIAL2_TX 8u // GPIO0
// Pin function macros
// -------------------
#define PIN_FUNCTION_ADC3 3u // GPIO23
#define PIN_FUNCTION_P0 8u // GPIO0
#define PIN_FUNCTION_P1 9u // GPIO1
#define PIN_FUNCTION_P6 0u // GPIO6
#define PIN_FUNCTION_P7 1u // GPIO7
#define PIN_FUNCTION_P8 2u // GPIO8
#define PIN_FUNCTION_P10 4u // GPIO10
#define PIN_FUNCTION_P11 5u // GPIO11
#define PIN_FUNCTION_P21 10u // GPIO21
#define PIN_FUNCTION_P23 3u // GPIO23
#define PIN_FUNCTION_P24 6u // GPIO24
#define PIN_FUNCTION_P26 7u // GPIO26
#define PIN_FUNCTION_PWM0 0u // GPIO6
#define PIN_FUNCTION_PWM1 1u // GPIO7
#define PIN_FUNCTION_PWM2 2u // GPIO8
#define PIN_FUNCTION_PWM4 6u // GPIO24
#define PIN_FUNCTION_PWM5 7u // GPIO26
#define PIN_FUNCTION_RX1 4u // GPIO10
#define PIN_FUNCTION_RX2 9u // GPIO1
#define PIN_FUNCTION_SCL2 8u // GPIO0
#define PIN_FUNCTION_SDA1 10u // GPIO21
#define PIN_FUNCTION_SDA2 9u // GPIO1
#define PIN_FUNCTION_TX1 5u // GPIO11
#define PIN_FUNCTION_TX2 8u // GPIO0

22
boards/cb3l.json Normal file
View File

@@ -0,0 +1,22 @@
{
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231n-tuya",
"ic/bk7231-qfn32",
"pcb/cb3l"
],
"build": {
"mcu": "bk7231n",
"variant": "cb3l"
},
"name": "CB3L Wi-Fi Module",
"url": "https://developer.tuya.com/en/docs/iot/cb3l-module-datasheet?id=Kai51ngmrh3qm",
"vendor": "Tuya Inc.",
"doc": {
"fccid": "2ANDL-CB3L"
},
"pcb": {
"symbol": "CB3L"
}
}

87
boards/cb3l/README.md Normal file
View File

@@ -0,0 +1,87 @@
# CB3L Wi-Fi Module
*by Tuya Inc.*
[Product page](https://developer.tuya.com/en/docs/iot/cb3l-module-datasheet?id=Kai51ngmrh3qm)
- [General info](../../docs/platform/beken-72xx/README.md)
- [Flashing guide](../../docs/platform/beken-72xx/flashing.md)
- [BkWriter v1.6.0](https://images.tuyacn.com/smart/bk_writer1.60/bk_writer1.60.exe)
Parameter | Value
-------------|------------------------------------------
Board code | `cb3l`
MCU | BK7231N
Manufacturer | Beken
Series | BK72XX
Frequency | 120 MHz
Flash size | 2 MiB
RAM size | 256 KiB
Voltage | 3.0V - 3.6V
I/O | 12x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
FCC ID | [2ANDL-CB3L](https://fccid.io/2ANDL-CB3L)
## Usage
**Board code:** `cb3l`
In `platformio.ini`:
```ini
[env:cb3l]
platform = libretuya
board = cb3l
framework = arduino
```
In ESPHome YAML:
```yaml
libretuya:
board: cb3l
framework:
version: dev
```
## Pinout
![Pinout](pinout_cb3l.svg)
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other
----|-----------|----------|----------|-----|------|------
D0 | P23 | | | | | TDO
D1 | P14 | | | SCK | |
D2 | P26 | | | | PWM5 |
D3 | P24 | | | | PWM4 |
D4 | P6 | | | | PWM0 |
D5 | P9 | | | | PWM3 |
D6 | P0 | UART2_TX | I2C2_SCL | | |
D7 | P21 | | I2C1_SDA | | | TMS
D8 | P8 | | | | PWM2 |
D9 | P7 | | | | PWM1 |
D10 | P10 | UART1_RX | | | |
D11 | P11 | UART1_TX | | | |
A0 | P23, ADC3 | | | | |
## Flash memory map
Flash size: 2 MiB / 2,097,152 B / 0x200000
Hex values are in bytes.
Name | Start | Length | End
----------------|----------|--------------------|---------
Bootloader | 0x000000 | 68 KiB / 0x11000 | 0x011000
App Image | 0x011000 | 1.1 MiB / 0x119000 | 0x12A000
OTA Image | 0x12A000 | 664 KiB / 0xA6000 | 0x1D0000
Calibration | 0x1D0000 | 4 KiB / 0x1000 | 0x1D1000
Network Data | 0x1D1000 | 4 KiB / 0x1000 | 0x1D2000
TLV Store | 0x1D2000 | 4 KiB / 0x1000 | 0x1D3000
Key-Value Store | 0x1D3000 | 32 KiB / 0x8000 | 0x1DB000
User Data | 0x1DB000 | 148 KiB / 0x25000 | 0x200000
Bootloader and app partitions contain CRC16 sums every 32 bytes. That results in the actual flash offsets/sizes not aligned to sector boundaries. To simplify calculations, the values shown in the table (extracted from bootloader's partition table) were aligned to 4096 bytes.

253
boards/cb3l/pinout_cb3l.svg Normal file
View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" baseProfile="full" height="500" version="1.1" viewBox="0,0,85.33333333333333,41.666666666666664" width="1024">
<defs/>
<rect fill="white" height="41.666666666666664" stroke="black" stroke-width="0.1" width="85.33333333333333" x="0" y="0"/>
<linearGradient gradientUnits="objectBoundingBox" id="id1" x1="1.0" x2="0.0" y1="0.0" y2="1.0">
<stop offset="0%" stop-color="#47a8cd"/>
<stop offset="100%" stop-color="#008fb5"/>
</linearGradient>
<rect fill="url(#id1) none" height="23.9" stroke="#b5a739" stroke-width="0.1" width="15.9" x="36.46666666666666" y="8.883333333333333"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin1.trace" width="1.7" x="36.416666666666664" y="16.583333333333332"/>
<circle cx="36.416666666666664" cy="17.18333333333333" fill="#fff" id="esp12s.front.left.pin1.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="17.18333333333333" fill="#fff" id="esp12s.front.left.pin1.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin2.trace" width="1.7" x="36.416666666666664" y="18.583333333333332"/>
<circle cx="36.416666666666664" cy="19.18333333333333" fill="#fff" id="esp12s.front.left.pin2.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="19.18333333333333" fill="#fff" id="esp12s.front.left.pin2.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin3.trace" width="1.7" x="36.416666666666664" y="20.583333333333332"/>
<circle cx="36.416666666666664" cy="21.18333333333333" fill="#fff" id="esp12s.front.left.pin3.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="21.18333333333333" fill="#fff" id="esp12s.front.left.pin3.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin4.trace" width="1.7" x="36.416666666666664" y="22.583333333333332"/>
<circle cx="36.416666666666664" cy="23.18333333333333" fill="#fff" id="esp12s.front.left.pin4.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="23.18333333333333" fill="#fff" id="esp12s.front.left.pin4.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin5.trace" width="1.7" x="36.416666666666664" y="24.583333333333332"/>
<circle cx="36.416666666666664" cy="25.183333333333334" fill="#fff" id="esp12s.front.left.pin5.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="25.183333333333334" fill="#fff" id="esp12s.front.left.pin5.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin6.trace" width="1.7" x="36.416666666666664" y="26.583333333333332"/>
<circle cx="36.416666666666664" cy="27.183333333333334" fill="#fff" id="esp12s.front.left.pin6.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="27.183333333333334" fill="#fff" id="esp12s.front.left.pin6.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin7.trace" width="1.7" x="36.416666666666664" y="28.583333333333332"/>
<circle cx="36.416666666666664" cy="29.183333333333334" fill="#fff" id="esp12s.front.left.pin7.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="29.183333333333334" fill="#fff" id="esp12s.front.left.pin7.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.left.pin8.trace" width="1.7" x="36.416666666666664" y="30.583333333333332"/>
<circle cx="36.416666666666664" cy="31.183333333333334" fill="#fff" id="esp12s.front.left.pin8.cast" r="0.35"/>
<circle cx="37.46666666666666" cy="31.183333333333334" fill="#fff" id="esp12s.front.left.pin8.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin1.trace" width="1.7" x="50.71666666666667" y="16.583333333333332"/>
<circle cx="52.416666666666664" cy="17.18333333333333" fill="#fff" id="esp12s.front.right.pin1.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="17.18333333333333" fill="#fff" id="esp12s.front.right.pin1.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin2.trace" width="1.7" x="50.71666666666667" y="18.583333333333332"/>
<circle cx="52.416666666666664" cy="19.18333333333333" fill="#fff" id="esp12s.front.right.pin2.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="19.18333333333333" fill="#fff" id="esp12s.front.right.pin2.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin3.trace" width="1.7" x="50.71666666666667" y="20.583333333333332"/>
<circle cx="52.416666666666664" cy="21.18333333333333" fill="#fff" id="esp12s.front.right.pin3.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="21.18333333333333" fill="#fff" id="esp12s.front.right.pin3.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin4.trace" width="1.7" x="50.71666666666667" y="22.583333333333332"/>
<circle cx="52.416666666666664" cy="23.18333333333333" fill="#fff" id="esp12s.front.right.pin4.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="23.18333333333333" fill="#fff" id="esp12s.front.right.pin4.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin5.trace" width="1.7" x="50.71666666666667" y="24.583333333333332"/>
<circle cx="52.416666666666664" cy="25.183333333333334" fill="#fff" id="esp12s.front.right.pin5.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="25.183333333333334" fill="#fff" id="esp12s.front.right.pin5.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin6.trace" width="1.7" x="50.71666666666667" y="26.583333333333332"/>
<circle cx="52.416666666666664" cy="27.183333333333334" fill="#fff" id="esp12s.front.right.pin6.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="27.183333333333334" fill="#fff" id="esp12s.front.right.pin6.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin7.trace" width="1.7" x="50.71666666666667" y="28.583333333333332"/>
<circle cx="52.416666666666664" cy="29.183333333333334" fill="#fff" id="esp12s.front.right.pin7.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="29.183333333333334" fill="#fff" id="esp12s.front.right.pin7.hole" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="esp12s.front.right.pin8.trace" width="1.7" x="50.71666666666667" y="30.583333333333332"/>
<circle cx="52.416666666666664" cy="31.183333333333334" fill="#fff" id="esp12s.front.right.pin8.cast" r="0.35"/>
<circle cx="51.36666666666666" cy="31.183333333333334" fill="#fff" id="esp12s.front.right.pin8.hole" r="0.35"/>
<linearGradient gradientUnits="objectBoundingBox" id="id2" x1="1.0" x2="0.0" y1="0.0" y2="1.0">
<stop offset="0%" stop-color="whitesmoke"/>
<stop offset="100%" stop-color="#999"/>
</linearGradient>
<rect fill="url(#id2) none" height="15.8" rx="0.5" ry="0.5" width="12.0" x="38.416666666666664" y="15.783333333333331"/>
<rect fill="#fff" height="0.15" width="15.0" x="36.916666666666664" y="15.033333333333331"/>
<text fill="#fff" font-family="Consolas" font-size="1.0" x="43.416666666666664" y="14.333333333333332">CB3L</text>
<rect fill="#58839b" height="5.2" width="0.5" x="37.516666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="0.5" width="4.6" x="37.516666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="5.2" width="0.5" x="39.71666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="41.61666666666667" y="9.633333333333333"/>
<rect fill="#58839b" height="0.5" width="3.0" x="41.61666666666667" y="12.133333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="44.11666666666667" y="9.633333333333333"/>
<rect fill="#58839b" height="0.5" width="2.7" x="44.11666666666667" y="9.633333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="46.31666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="0.5" width="3.0" x="46.31666666666666" y="12.133333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="48.81666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="0.5" width="2.5" x="48.81666666666666" y="9.633333333333333"/>
<rect fill="#58839b" height="4.4" width="0.5" x="50.81666666666666" y="9.633333333333333"/>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="17.08333333333333"/>
<g transform="translate(29.53102602061156,16.38333333333333)">
<rect fill="#ed602e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="17.18333333333333">CEN</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="19.08333333333333"/>
<g transform="translate(29.53102602061156,18.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="19.18333333333333">P23</text>
<g transform="translate(25.93102602061156,18.38333333333333)">
<rect fill="#8ad039" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="27.416666666666664" y="19.18333333333333">ADC3</text>
<g transform="translate(22.93102602061156,18.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="24.116666666666664" y="19.18333333333333">D0</text>
<g transform="translate(19.93102602061156,18.38333333333333)">
<rect fill="#16a352" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="21.116666666666664" y="19.18333333333333">A0</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="21.08333333333333"/>
<g transform="translate(29.53102602061156,20.38333333333333)">
<rect fill="#ed602e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="21.18333333333333">CEN</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="23.08333333333333"/>
<g transform="translate(29.53102602061156,22.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="23.18333333333333">P14</text>
<g transform="translate(26.531026020611563,22.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="27.716666666666665" y="23.18333333333333">D1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="25.083333333333332"/>
<g transform="translate(29.53102602061156,24.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="25.183333333333334">P26</text>
<g transform="translate(26.531026020611563,24.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="27.716666666666665" y="25.183333333333334">D2</text>
<g transform="translate(22.93102602061156,24.383333333333333)">
<rect fill="#aeafc1" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="24.416666666666664" y="25.183333333333334">IRDA</text>
<g transform="translate(19.331026020611564,24.383333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="20.816666666666666" y="25.183333333333334">PWM5</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="27.083333333333332"/>
<g transform="translate(29.53102602061156,26.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="27.183333333333334">P24</text>
<g transform="translate(26.531026020611563,26.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="27.716666666666665" y="27.183333333333334">D3</text>
<g transform="translate(22.93102602061156,26.383333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="24.416666666666664" y="27.183333333333334">PWM4</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="29.083333333333332"/>
<g transform="translate(29.53102602061156,28.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="29.183333333333334">P6</text>
<g transform="translate(26.531026020611563,28.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="27.716666666666665" y="29.183333333333334">D4</text>
<g transform="translate(22.93102602061156,28.383333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="24.416666666666664" y="29.183333333333334">PWM0</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="33.31666666666666" y="31.083333333333332"/>
<g transform="translate(29.53102602061156,30.383333333333333)">
<rect fill="#cd3c24" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="31.016666666666662" y="31.183333333333334">3V3</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="31.083333333333332"/>
<g transform="translate(56.33102602061157,30.383333333333333)">
<rect fill="#000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="31.183333333333334">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="29.083333333333332"/>
<g transform="translate(56.33102602061157,28.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="29.183333333333334">P9</text>
<g transform="translate(59.93102602061157,28.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="29.183333333333334">D5</text>
<g transform="translate(62.93102602061157,28.383333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="29.183333333333334">PWM3</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="27.083333333333332"/>
<g transform="translate(56.33102602061157,26.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="27.183333333333334">P0</text>
<g transform="translate(59.93102602061157,26.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="27.183333333333334">D6</text>
<g transform="translate(62.93102602061157,26.383333333333333)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="27.183333333333334">TX2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="25.083333333333332"/>
<g transform="translate(56.33102602061157,24.383333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="25.183333333333334">P21</text>
<g transform="translate(59.93102602061157,24.383333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="25.183333333333334">D7</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="23.08333333333333"/>
<g transform="translate(56.33102602061157,22.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="23.18333333333333">P8</text>
<g transform="translate(59.93102602061157,22.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="23.18333333333333">D8</text>
<g transform="translate(62.93102602061157,22.38333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="23.18333333333333">PWM2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="21.08333333333333"/>
<g transform="translate(56.33102602061157,20.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="21.18333333333333">P7</text>
<g transform="translate(59.93102602061157,20.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="21.18333333333333">D9</text>
<g transform="translate(62.93102602061157,20.38333333333333)">
<rect fill="#afa35e" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="21.18333333333333">PWM1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="19.08333333333333"/>
<g transform="translate(56.33102602061157,18.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="19.18333333333333">P10</text>
<g transform="translate(59.93102602061157,18.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="19.18333333333333">D10</text>
<g transform="translate(62.93102602061157,18.38333333333333)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="19.18333333333333">RX1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="52.916666666666664" y="17.08333333333333"/>
<g transform="translate(56.33102602061157,16.38333333333333)">
<rect fill="#800000" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="57.81666666666667" y="17.18333333333333">P11</text>
<g transform="translate(59.93102602061157,16.38333333333333)">
<rect fill="#99188d" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="2.8" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="white" font-family="Consolas" font-size="1.2" text-anchor="middle" x="61.11666666666667" y="17.18333333333333">D11</text>
<g transform="translate(62.93102602061157,16.38333333333333)">
<rect fill="#dcd4ee" height="1.6" rx="0.3" ry="0.3" transform="skewX(-15)" width="3.4" x="0" y="0"/>
</g>
<text dominant-baseline="central" fill="#423F42" font-family="Consolas" font-size="1.2" text-anchor="middle" x="64.41666666666667" y="17.18333333333333">TX1</text>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

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