23 Commits

Author SHA1 Message Date
Kuba Szczodrzyński
aae2f65b9e [release] v0.9.0
Some checks failed
Lint check / Lint with clang-format (push) Has been cancelled
Lint check / Lint with black (push) Has been cancelled
PlatformIO Publish / publish (push) Has been cancelled
2022-08-06 13:42:37 +02:00
Kuba Szczodrzyński
d02197474e [docs] Update ESPHome docs, add cloudcutter guide 2022-08-06 13:37:47 +02:00
Kuba Szczodrzyński
41b37e9c24 [core] Update ltchiptool to v1.4.0 2022-08-06 13:06:44 +02:00
Kuba Szczodrzyński
b03400fac2 [beken-72xx] Implement OTA API, add Update debugging 2022-08-06 11:36:53 +02:00
Kuba Szczodrzyński
3e808f7b6b [beken-72xx] Write OTA package to UF2 image 2022-08-05 20:29:48 +02:00
Kuba Szczodrzyński
1e3a8971fb [core] Migrate tools to ltchiptool 2022-08-05 20:29:45 +02:00
Kuba Szczodrzyński
d8c0105b97 [examples] Fix PinScan compilation on realtek-ambz 2022-08-03 12:48:50 +02:00
Kuba Szczodrzyński
b3689cbac8 [beken-72xx] Specify keys for OTA packaging 2022-08-01 19:56:34 +02:00
Kuba Szczodrzyński
1b5d6472f7 [examples] Fix PinScan telnet functionality 2022-08-01 11:28:56 +02:00
Kuba Szczodrzyński
e9511c507a [core] Allow disabling LT logger programmatically 2022-08-01 11:28:16 +02:00
Kuba Szczodrzyński
365f64ded5 [beken-72xx] Make Serial.end() disable hardware UART 2022-08-01 11:27:57 +02:00
Kuba Szczodrzyński
3601fa63d8 [examples] Add PinScan example 2022-08-01 10:52:58 +02:00
Kuba Szczodrzyński
5d00ddf516 [beken-72xx] Fix detaching interrupt handler 2022-07-31 23:25:12 +02:00
Kuba Szczodrzyński
0051453cad [beken-72xx] Implement interrupts 2022-07-31 21:35:02 +02:00
Kuba Szczodrzyński
7920ea2dda [beken-72xx] Fix SerialClass character reading 2022-07-31 14:52:09 +02:00
Kuba Szczodrzyński
9416e45a75 [docs] Restructure documentation, clarify some parts 2022-07-31 13:05:24 +02:00
Kuba Szczodrzyński
98a65c81af [boards] Add usage info to board README 2022-07-31 12:47:24 +02:00
Kuba Szczodrzyński
8e1f06e79b [boards] Add LSC LMA35 board, update boardgen 2022-07-30 19:48:58 +02:00
Kuba Szczodrzyński
58a09f453d [realtek-ambz] Fix errors related to lwIP 2022-07-30 17:18:46 +02:00
Kuba Szczodrzyński
7dc69982b6 [beken-72xx] Fix putchar_p UART index 2022-07-30 15:42:30 +02:00
Kuba Szczodrzyński
46e4041ed8 [beken-72xx] Mark mDNS as broken 2022-07-28 22:18:04 +02:00
Kuba Szczodrzyński
0e129130b1 [core] Print errors about starting main task 2022-07-28 20:29:53 +02:00
Kuba Szczodrzyński
33c9868f90 [core] Remove duplicated WMath functions 2022-07-28 13:46:42 +02:00
128 changed files with 2260 additions and 4076 deletions

View File

@@ -35,7 +35,7 @@ LibreTuya also provides a common interface for all family implementations. The i
## Board List
See [Boards & CPU list](https://kuba2k2.github.io/libretuya/docs/supported/).
See [Boards & CPU list](https://kuba2k2.github.io/libretuya/docs/status/supported/).
## Arduino Core support status
@@ -44,7 +44,7 @@ Note: this list will probably change with each functionality update.
  | `realtek-ambz` | `beken-72xx`
--------------------|----------------|-------------
Core functions | ✔️ | ✔️
GPIO/PWM/IRQ | ✔️/✔️/✔️ | /✔️/
GPIO/PWM/IRQ | ✔️/✔️/✔️ | ✔️/✔️/✔️
Analog input (ADC) | ✔️ | ✔️
Serial | ✔️ | ✔️
Serial (extra) | 0, 1, 2 | 1, 2
@@ -65,8 +65,8 @@ NVS / Preferences | ❌ | ❌
SPIFFS | ❌ | ❌
BLE | - | ❌
NTP | ❌ | ❌
OTA | ✔️ |
MDNS | ✔️ | ✔️
OTA | ✔️ | ✔️
MDNS | ✔️ | BK7231T only
MQTT | ✅ | ❌
SD | ❌ | ❌

View File

@@ -1,10 +1,29 @@
* [Home](README.md)
* [Getting started](docs/getting-started.md)
* [💻 Boards & CPU list](docs/supported.md)
* [✔️ Implementation status](docs/implementation-status.md)
* [🔧 Configuration](docs/config.md)
* [📁 Project structure](docs/project-structure.md)
* 🔖 Code reference
* 😊 Getting started
* [Start here](docs/getting-started/README.md)
* [Uploading](docs/getting-started/uploading.md)
* [Options & config](docs/reference/config.md)
* Examples
* [PinScan](examples/PinScan/README.md)
* [ESPHome port](docs/projects/esphome.md)
* [Using tuya-cloudcutter](docs/cloudcutter.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)
* Realtek AmebaZ Series
* [General info](docs/platform/realtek/README.md)
* [Flashing (AmebaZ)](docs/platform/realtek-ambz/flashing.md)
* [Debugging](docs/platform/realtek/debugging.md)
* [Exception decoder](docs/platform/realtek/exception-decoder.md)
* C library
* [Built-in functions](docs/platform/realtek-ambz/stdlib.md)
* [Memory management](docs/platform/realtek-ambz/memory-management.md)
* [All supported boards](boards/)
* API & libraries
* [Options & config](docs/reference/config.md)
* [LibreTuya API](docs/reference/lt-api.md)
* [LT class reference](ltapi/class_libre_tuya.md)
* [Common methods](ltapi/_libre_tuya_a_p_i_8h.md)
@@ -38,22 +57,10 @@
* [Functions](ltapi/functions.md)
* [Macros](ltapi/macros.md)
* [File list](ltapi/files.md)
* [✈️ OTA format](docs/ota/README.md)
* [uf2ota.py tool](docs/ota/uf2ota.md)
* [uf2ota.h library](docs/ota/library.md)
* [uf2ota.h reference](ltapi/uf2ota_8h.md)
* Families
* Beken BK72xx
* [General info](docs/platform/beken-72xx/README.md)
* [Flashing](docs/platform/beken-72xx/flashing.md)
* Realtek AmebaZ Series
* [General info](docs/platform/realtek/README.md)
* [Flashing (AmebaZ)](docs/platform/realtek-ambz/flashing.md)
* [Debugging](docs/platform/realtek/debugging.md)
* [Exception decoder](docs/platform/realtek/exception-decoder.md)
* C library
* [Built-in functions](docs/platform/realtek-ambz/stdlib.md)
* [Memory management](docs/platform/realtek-ambz/memory-management.md)
* [All supported boards](boards/)
* [📁 Project structure](docs/reference/project-structure.md)
* [✈️ OTA format](docs/ota/README.md)
* [uf2ota.py tool](docs/ota/uf2ota.md)
* [uf2ota.h library](docs/ota/library.md)
* [uf2ota.h reference](ltapi/uf2ota_8h.md)
* [📓 TODO](TODO.md)
* [🔗 Resources](docs/resources.md)

View File

@@ -14,8 +14,7 @@
### Tools
- move all UF2 assembling/uploading/processing tools (as well as `uf2ota` C library) to a separate repository, possibly rewriting parts of it again. Make these tools CLI-usable
- write OpenOCD flashers, using uf2ota library + FAL for partitions (same repo as above)
- write OpenOCD flashers, using uf2ota library + FAL for partitions (in ltchiptool repository)
### Serial
@@ -31,10 +30,8 @@
## BK7231
- WiFi events
- implement OTA
- fix WiFi on BK7231N, test other functionality
- add generic board definition
- fix SSL (mbedTLS)
- I2C (Wire)
- SPI
@@ -42,6 +39,5 @@
## RTL8710B
- add generic board definition
- move to GNU++11 (and verify that it works) - take all stdio functions from stdio.h
- rewrite most of Wiring (it was copied from `ambd_arduino`, and is ugly)

View File

@@ -1,6 +1,7 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <LibreTuyaAPI.h>
#include <libraries/Flash/Flash.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
@@ -109,12 +110,14 @@ uint32_t LibreTuya::getMaxAllocHeap() {
/* OTA-related */
static int8_t otaImage2Valid = -1;
uint8_t LibreTuya::otaGetStoredIndex() {
return 1;
return otaHasImage2() ? 2 : 1;
}
bool LibreTuya::otaSupportsDual() {
return false;
return true;
}
bool LibreTuya::otaHasImage1() {
@@ -122,11 +125,28 @@ bool LibreTuya::otaHasImage1() {
}
bool LibreTuya::otaHasImage2() {
return false;
if (otaImage2Valid != -1)
return otaImage2Valid;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
Flash.readBlock(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
otaImage2Valid = magic == 0x004C4252; // "RBL\0", little-endian
return otaImage2Valid;
}
bool LibreTuya::otaSwitch(bool force) {
return true;
// no need to check otaGetStoredIndex() as it does the same as otaHasImage2()
// force checking validity again
otaImage2Valid = -1;
if (otaHasImage2() && force) {
// "rollback" - abort bootloader upgrade operation by wiping first sector
return Flash.eraseSector(FLASH_DOWNLOAD_OFFSET);
}
return otaHasImage2(); // false if second image is not valid
}
/* Global instance */

View File

@@ -26,7 +26,10 @@ SerialClass::SerialClass(uint8_t port) {
static void callback(int port, void *param) {
RingBuffer *buf = (RingBuffer *)param;
buf->store_char(uart_read_byte(port));
int ch;
while ((ch = uart_read_byte(port)) != -1) {
buf->store_char(ch);
}
}
void SerialClass::begin(unsigned long baudrate, uint16_t config) {
@@ -54,6 +57,14 @@ void SerialClass::begin(unsigned long baudrate, uint16_t config) {
void SerialClass::end() {
uart_rx_callback_set(port, NULL, NULL);
switch (port) {
case 1:
uart1_exit();
break;
case 2:
uart2_exit();
break;
}
delete this->buf;
}

View File

@@ -23,7 +23,7 @@ bool startMainTask() {
&mainThread,
THD_APPLICATION_PRIORITY,
"main",
(beken_thread_function_t)main_task,
(beken_thread_function_t)mainTask,
8192,
NULL
);

View File

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

View File

@@ -15,7 +15,7 @@ void putchar_(char c) {
}
void putchar_p(char c, unsigned long port) {
bk_send_byte(port & 0xFF, c);
bk_send_byte((port & 0xFF) - 1, c);
}
WRAP_PRINTF(bk_printf);

View File

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

View File

@@ -21,7 +21,7 @@ void initVariant() __attribute__((weak));
// Initialize C library
extern "C" void __libc_init_array(void);
void main_task(const void *arg) {
void mainTask(const void *arg) {
setup();
for (;;) {
@@ -46,7 +46,9 @@ int main(void) {
// provide root partition
fal_root_part = (fal_partition_t)fal_partition_find("root");
// start the main task and OS kernel
startMainTask();
if (!startMainTask()) {
LT_E("Couldn't start the main task");
}
while (1) {}
return 0;

View File

@@ -1,5 +1,7 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
#pragma once
#define CHIP_TYPE(family, chip_id) (((family >> 24) << 8) | chip_id)
#define CHIP_TYPE_ENUM(family, chip_id) (ChipType) CHIP_TYPE(family, chip_id)

View File

@@ -98,7 +98,8 @@ uint8_t LibreTuya::otaGetTarget() {
}
/**
* @brief Perform OTA rollback.
* @brief Perform OTA rollback: switch to the previous image, or abort current
* switched OTA update, if not rebooted yet.
*
* @return false if no second image to run, writing failed or dual-OTA not supported
*/

View File

@@ -114,7 +114,9 @@ class LibreTuya {
*/
uint8_t otaGetStoredIndex();
/**
* @brief Check if the chip supports dual-OTA.
* @brief Check if the chip supports dual-OTA (i.e. OTA is flashed to a different partition).
*
* TODO: make this work for actual dual-OTA chips; remove checking this in otaGetTarget() etc.
*/
bool otaSupportsDual();
/**

View File

@@ -95,3 +95,7 @@
#ifndef LT_DEBUG_SSL
#define LT_DEBUG_SSL 0
#endif
#ifndef LT_DEBUG_OTA
#define LT_DEBUG_OTA 0
#endif

View File

@@ -9,7 +9,9 @@ extern "C" {
#endif
/**
* @brief Run main_task & start OS kernel (family-defined)
* @brief Run mainTask & start OS kernel (family-defined).
* Return false if an error occured; else do not return and
* and keep the OS kernel running.
*/
extern bool startMainTask();
@@ -17,7 +19,7 @@ extern bool startMainTask();
* @brief Main setup() and loop() task.
* Not to be called directly.
*/
extern void main_task(const void *arg);
extern void mainTask(const void *arg);
#define PIN_NONE (1 << 0)
#define PIN_GPIO (1 << 1)

View File

@@ -50,6 +50,9 @@ void lt_log(const uint8_t level, const char *caller, const unsigned short line,
void lt_log(const uint8_t level, const char *format, ...) {
#endif
if (uart_port == 0xFF)
return;
#if LT_LOGGER_TIMESTAMP
float seconds = millis() / 1000.0f;
#if LT_PRINTF_BROKEN
@@ -136,3 +139,7 @@ void lt_log(const uint8_t level, const char *format, ...) {
void lt_log_set_port(uint8_t port) {
uart_port = port;
}
void lt_log_disable() {
uart_port = 0xFF;
}

View File

@@ -20,6 +20,11 @@ void lt_log(const uint8_t level, const char *format, ...);
*/
void lt_log_set_port(uint8_t port);
/**
* @brief Disable LT logger. Enable it back using lt_log_set_port(LT_UART_DEFAULT_LOGGER).
*/
void lt_log_disable();
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
@@ -180,3 +185,8 @@ void lt_log_set_port(uint8_t port);
#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

@@ -18,6 +18,8 @@ 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());
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
info = uf2_info_init();
@@ -70,6 +72,8 @@ 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());
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
// buffer empty and entire block is in data
if (!tryWriteData(data, UF2_BLOCK_SIZE)) {
@@ -82,7 +86,7 @@ size_t UpdateClass::write(uint8_t *data, size_t len) {
} */
// write until buffer space is available
uint16_t toWrite;
uint16_t toWrite; // 1..512
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError())
@@ -141,6 +145,8 @@ 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());
if (len == UF2_BLOCK_SIZE) {
// data has a complete block
block = (uf2_block_t *)data;
@@ -172,11 +178,14 @@ size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
// 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);
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);
}
} else {

View File

@@ -2,8 +2,7 @@
#include <Arduino.h>
#include <functional>
#include "uf2ota/uf2ota.h"
#include <uf2ota/uf2ota.h>
// No Error
#define UPDATE_ERROR_OK (0)
@@ -119,7 +118,7 @@ class UpdateClass {
}
void clearError() {
errorUf2(UF2_ERR_OK);
errorArd(UPDATE_ERROR_OK);
}
bool hasError() {

View File

@@ -52,6 +52,8 @@ void UpdateClass::cleanup() {
* @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);
if (err <= UF2_ERR_IGNORE)
return false;
cleanup();
@@ -67,6 +69,8 @@ bool UpdateClass::errorUf2(uf2_err_t err) {
* @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;
@@ -77,6 +81,7 @@ bool UpdateClass::errorArd(uint8_t err) {
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
LT_D_OTA("Aborting update");
errorArd(UPDATE_ERROR_ABORT);
}
@@ -105,7 +110,7 @@ void UpdateClass::printError(Print &out) {
* "ard=..,uf2=..". Returns "" if no error.
*/
const char *UpdateClass::errorString() {
if (!errArd)
if (!errArd && !errUf2)
return "";
sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2);
return errorStr;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -61,6 +61,7 @@ bool mDNS::begin(const char *hostname) {
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) {
@@ -100,7 +101,7 @@ bool mDNS::addService(char *service, char *proto, uint16_t port) {
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);
mdns_resp_add_service(netif, mdnsInstanceName.c_str(), _service, protocol, port, 255, NULL, NULL);
}
netif = netif->next;
}

View File

@@ -10,9 +10,9 @@ static void *gpio_irq_handler_args[PINS_COUNT] = {NULL};
extern bool pinInvalid(pin_size_t pinNumber);
extern void pinRemoveMode(pin_size_t pinNumber);
void gpioIrqHandler(uint32_t id, gpio_irq_event event) {
static void gpioIrqHandler(uint32_t id, gpio_irq_event event) {
if (gpio_irq_handler_list[id] != NULL) {
if (gpio_irq_handler_args[id] != NULL)
if (gpio_irq_handler_args[id] == NULL)
((voidFuncPtr)gpio_irq_handler_list[id])();
else
((voidFuncPtrParam)gpio_irq_handler_list[id])(gpio_irq_handler_args[id]);

View File

@@ -23,8 +23,8 @@ void initArduino() {
}
bool startMainTask() {
osThreadDef(main_task, osPriorityRealtime, 1, 4096 * 4);
main_tid = osThreadCreate(osThread(main_task), NULL);
osThreadDef(mainTask, osPriorityRealtime, 1, 4096 * 4);
main_tid = osThreadCreate(osThread(mainTask), NULL);
osKernelStart();
return true;
}

View File

@@ -21,4 +21,5 @@
- [WR2LE](../boards/wr2le/README.md)
- [WR3L](../boards/wr3l/README.md)
- [WR3LE](../boards/wr3le/README.md)
- [LSC LMA35](../boards/lsc-lma35/README.md)
- [Generic - Host-native](../boards/generic-native/README.md)

View File

@@ -1,7 +1,13 @@
{
"build": {
"f_cpu": "120000000L",
"prefix": "arm-none-eabi-"
"prefix": "arm-none-eabi-",
"bkota": {
"encryption": "aes256",
"compression": "gzip",
"key": "0123456789ABCDEF0123456789ABCDEF",
"iv": "0123456789ABCDEF"
}
},
"connectivity": [
"wifi",

View File

@@ -0,0 +1,102 @@
{
"pcb": {
"templates": [
"custom-20x24-22",
"rf-20mm-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,JTAG,FLASH,SD,SPI,SDA1",
"pinout": {
"1": {
"PWR": 3.3
},
"2": {
"IC": 15,
"ARD": "D0"
},
"3": {
"IC": 11,
"ARD": "D1"
},
"4": {
"IC": 12,
"ARD": "D2"
},
"5": {
"IC": 16,
"ARD": "D3"
},
"6": {
"GND": null
},
"7": {
"IC": 18,
"ARD": "D4"
},
"8": {
"IC": 29,
"ARD": "D5"
},
"9": {
"IC": 17,
"ARD": [
"D6",
"A0"
]
},
"10": {
"CTRL": "?"
},
"11": {
"GND": null
},
"12": {
"IC": 24,
"ARD": "D7"
},
"13": {
"IC": 25,
"ARD": "D8"
},
"14": {
"GND": null
},
"15": {
"IC": 19,
"ARD": "D9"
},
"16": {
"IC": 22,
"ARD": "D10"
},
"17": {
"IC": 23,
"ARD": "D11"
},
"18": {
"IC": 26,
"ARD": "D12"
},
"19": {
"GND": null
},
"20": {
"IC": 27,
"ARD": "D13"
},
"21": {
"CTRL": "?"
},
"22": {
"IC": 28,
"ARD": "D14"
}
}
}
}

View File

@@ -12,6 +12,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `bw12`
MCU | RTL8710BX
Manufacturer | Realtek
Series | AmebaZ
@@ -22,6 +23,19 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `bw12`
In `platformio.ini`:
```ini
[env:bw12]
platform = libretuya
board = bw12
framework = arduino
```
## Pinout
![Pinout](pinout_bw12.svg)

View File

@@ -9,6 +9,7 @@
Parameter | Value
-------------|--------------------------
Board code | `bw15`
MCU | RTL8720CF
Manufacturer | Realtek
Series | AmebaZ2
@@ -20,6 +21,19 @@ I/O | 13x GPIO, 8x PWM, 3x UART
Wi-Fi | 802.11 b/g/n
BLE | v4.2
## Usage
**Board code:** `bw15`
In `platformio.ini`:
```ini
[env:bw15]
platform = libretuya
board = bw15
framework = arduino
```
## Pinout
![Pinout](pinout_bw15.svg)

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `cb2s`
MCU | BK7231N
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 11x GPIO, 5x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
## Usage
**Board code:** `cb2s`
In `platformio.ini`:
```ini
[env:cb2s]
platform = libretuya
board = cb2s
framework = arduino
```
## Pinout
![Pinout](pinout_cb2s.svg)

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `generic-bk7231n-qfn32-tuya`
MCU | BK7231N
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 19x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
## Usage
**Board code:** `generic-bk7231n-qfn32-tuya`
In `platformio.ini`:
```ini
[env:generic-bk7231n-qfn32-tuya]
platform = libretuya
board = generic-bk7231n-qfn32-tuya
framework = arduino
```
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `generic-bk7231t-qfn32-tuya`
MCU | BK7231T
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 19x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v4.2
## Usage
**Board code:** `generic-bk7231t-qfn32-tuya`
In `platformio.ini`:
```ini
[env:generic-bk7231t-qfn32-tuya]
platform = libretuya
board = generic-bk7231t-qfn32-tuya
framework = arduino
```
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other

View File

@@ -5,7 +5,8 @@
[Product page](https://kuba2k2.github.io/libretuya/)
Parameter | Value
-------------|-------
-------------|-----------------
Board code | `generic-native`
MCU | NATIVE
Manufacturer | N/A
Series | N/A
@@ -14,6 +15,19 @@ Flash size | 4 MiB
RAM size | 4 MiB
Voltage | 5V
## Usage
**Board code:** `generic-native`
In `platformio.ini`:
```ini
[env:generic-native]
platform = libretuya
board = generic-native
framework = arduino
```
## Flash memory map
Flash size: 4 MiB / 4,194,304 B / 0x400000

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `generic-rtl8710bn-2mb-468k`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 17x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `generic-rtl8710bn-2mb-468k`
In `platformio.ini`:
```ini
[env:generic-rtl8710bn-2mb-468k]
platform = libretuya
board = generic-rtl8710bn-2mb-468k
framework = arduino
```
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `generic-rtl8710bn-2mb-788k`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 17x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `generic-rtl8710bn-2mb-788k`
In `platformio.ini`:
```ini
[env:generic-rtl8710bn-2mb-788k]
platform = libretuya
board = generic-rtl8710bn-2mb-788k
framework = arduino
```
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other

View File

@@ -7,7 +7,8 @@
- [General info](../../docs/platform/realtek/README.md)
Parameter | Value
-------------|--------------------------
-------------|-----------------------------
Board code | `generic-rtl8720cf-2mb-992k`
MCU | RTL8720CF
Manufacturer | Realtek
Series | AmebaZ2
@@ -19,6 +20,19 @@ I/O | 20x GPIO, 8x PWM, 3x UART
Wi-Fi | 802.11 b/g/n
BLE | v4.2
## Usage
**Board code:** `generic-rtl8720cf-2mb-992k`
In `platformio.ini`:
```ini
[env:generic-rtl8720cf-2mb-992k]
platform = libretuya
board = generic-rtl8720cf-2mb-992k
framework = arduino
```
## Flash memory map
Flash size: 2 MiB / 2,097,152 B / 0x200000

29
boards/lsc-lma35.json Normal file
View File

@@ -0,0 +1,29 @@
{
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231n-tuya",
"pcb/ic-bk7231-qfn32",
"pcb/lsc-lma35"
],
"build": {
"mcu": "bk7231n",
"variant": "lsc-lma35"
},
"name": "LSC LMA35",
"symbol": "LSC LMA35",
"url": "https://www.action.com/de-at/p/lsc-smart-connect-outdoor-led-streifen/",
"vendor": "Unknown",
"pcb": {
"symbol": "LMA35"
},
"doc": {
"extra": [
"## Information",
"This board has no marking on the front side, only something that looks like PCB manufacturing info on the back; thus it was named based on these symbols.",
"It can be found in 'LSC Smart Connect Outdoor LED Strip', and is likely custom-made for this product.",
"The pinout was established by writing to and probing consecutive GPIOs, using the generic board definition.",
"Pins marked with '?' are currently unknown, with a possibility of being CEN. Pin 22 (P1/D14) is also not confirmed."
]
}
}

View File

@@ -0,0 +1,89 @@
# LSC LMA35
*by Unknown*
[Product page](https://www.action.com/de-at/p/lsc-smart-connect-outdoor-led-streifen/)
- [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 | `lsc-lma35`
MCU | BK7231N
Manufacturer | Beken
Series | BK72XX
Frequency | 120 MHz
Flash size | 2 MiB
RAM size | 256 KiB
Voltage | 3.0V - 3.6V
I/O | 15x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v5.1
## Usage
**Board code:** `lsc-lma35`
In `platformio.ini`:
```ini
[env:lsc-lma35]
platform = libretuya
board = lsc-lma35
framework = arduino
```
## Pinout
![Pinout](pinout_lsc-lma35.svg)
## Arduino Core pin mapping
No. | Pin | UART | I²C | SPI | PWM | Other
----|-----------|----------|----------|------|------|------
D0 | P26 | | | | PWM5 |
D1 | P14 | | | SCK | |
D2 | P16 | | | MOSI | |
D3 | P24 | | | | PWM4 |
D4 | P22 | | | | | TDI
D5 | P0 | UART2_TX | I2C2_SCL | | |
D6 | P23 | | | | | TDO
D7 | P8 | | | | PWM2 |
D8 | P9 | | | | PWM3 |
D9 | P21 | | I2C1_SDA | | | TMS
D10 | P6 | | | | PWM0 |
D11 | P7 | | | | PWM1 |
D12 | P10 | UART1_RX | | | |
D13 | P11 | UART1_TX | | | |
D14 | P1 | UART2_RX | I2C2_SDA | | |
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
TLV Store | 0x1D0000 | 4 KiB / 0x1000 | 0x1D1000
Network Data | 0x1D1000 | 8 KiB / 0x2000 | 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.
## Information
This board has no marking on the front side, only something that looks like PCB manufacturing info on the back; thus it was named based on these symbols.
It can be found in 'LSC Smart Connect Outdoor LED Strip', and is likely custom-made for this product.
The pinout was established by writing to and probing consecutive GPIOs, using the generic board definition.
Pins marked with '?' are currently unknown, with a possibility of being CEN. Pin 22 (P1/D14) is also not confirmed.

View File

@@ -0,0 +1,325 @@
<?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="19.9" x="32.66666666666666" y="4.433333333333333"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin1.trace" width="0.7" x="32.61666666666666" y="12.133333333333333"/>
<circle cx="32.61666666666666" cy="12.733333333333333" fill="#fff" id="custom-20x24-22.front.left.pin1.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin2.trace" width="0.7" x="32.61666666666666" y="14.133333333333333"/>
<circle cx="32.61666666666666" cy="14.733333333333333" fill="#fff" id="custom-20x24-22.front.left.pin2.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin3.trace" width="0.7" x="32.61666666666666" y="16.133333333333333"/>
<circle cx="32.61666666666666" cy="16.733333333333334" fill="#fff" id="custom-20x24-22.front.left.pin3.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin4.trace" width="0.7" x="32.61666666666666" y="18.133333333333333"/>
<circle cx="32.61666666666666" cy="18.733333333333334" fill="#fff" id="custom-20x24-22.front.left.pin4.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin5.trace" width="0.7" x="32.61666666666666" y="20.133333333333333"/>
<circle cx="32.61666666666666" cy="20.733333333333334" fill="#fff" id="custom-20x24-22.front.left.pin5.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin6.trace" width="0.7" x="32.61666666666666" y="22.133333333333333"/>
<circle cx="32.61666666666666" cy="22.733333333333334" fill="#fff" id="custom-20x24-22.front.left.pin6.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.left.pin7.trace" width="0.7" x="32.61666666666666" y="24.133333333333333"/>
<circle cx="32.61666666666666" cy="24.733333333333334" fill="#fff" id="custom-20x24-22.front.left.pin7.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin1.trace" width="0.7" x="51.91666666666666" y="12.133333333333333"/>
<circle cx="52.61666666666666" cy="12.733333333333333" fill="#fff" id="custom-20x24-22.front.right.pin1.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin2.trace" width="0.7" x="51.91666666666666" y="14.133333333333333"/>
<circle cx="52.61666666666666" cy="14.733333333333333" fill="#fff" id="custom-20x24-22.front.right.pin2.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin3.trace" width="0.7" x="51.91666666666666" y="16.133333333333333"/>
<circle cx="52.61666666666666" cy="16.733333333333334" fill="#fff" id="custom-20x24-22.front.right.pin3.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin4.trace" width="0.7" x="51.91666666666666" y="18.133333333333333"/>
<circle cx="52.61666666666666" cy="18.733333333333334" fill="#fff" id="custom-20x24-22.front.right.pin4.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin5.trace" width="0.7" x="51.91666666666666" y="20.133333333333333"/>
<circle cx="52.61666666666666" cy="20.733333333333334" fill="#fff" id="custom-20x24-22.front.right.pin5.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin6.trace" width="0.7" x="51.91666666666666" y="22.133333333333333"/>
<circle cx="52.61666666666666" cy="22.733333333333334" fill="#fff" id="custom-20x24-22.front.right.pin6.cast" r="0.35"/>
<rect fill="#e5b472" height="1.2" id="custom-20x24-22.front.right.pin7.trace" width="0.7" x="51.91666666666666" y="24.133333333333333"/>
<circle cx="52.61666666666666" cy="24.733333333333334" fill="#fff" id="custom-20x24-22.front.right.pin7.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin1.trace" width="1.2" x="35.06666666666666" y="27.683333333333334"/>
<circle cx="35.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin1.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin2.trace" width="1.2" x="37.06666666666666" y="27.683333333333334"/>
<circle cx="37.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin2.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin3.trace" width="1.2" x="39.06666666666666" y="27.683333333333334"/>
<circle cx="39.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin3.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin4.trace" width="1.2" x="41.06666666666666" y="27.683333333333334"/>
<circle cx="41.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin4.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin5.trace" width="1.2" x="43.06666666666666" y="27.683333333333334"/>
<circle cx="43.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin5.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin6.trace" width="1.2" x="45.06666666666666" y="27.683333333333334"/>
<circle cx="45.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin6.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin7.trace" width="1.2" x="47.06666666666666" y="27.683333333333334"/>
<circle cx="47.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin7.cast" r="0.35"/>
<rect fill="#e5b472" height="0.7" id="pins_horz8_2mm_0.7mm.pin8.trace" width="1.2" x="49.06666666666666" y="27.683333333333334"/>
<circle cx="49.66666666666666" cy="28.383333333333333" fill="#fff" id="pins_horz8_2mm_0.7mm.pin8.cast" r="0.35"/>
<rect fill="#4e4c4c" height="2.0" width="0.2" x="35.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="0.2" x="35.36666666666666" y="30.383333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label1.anchor" width="0.0" x="35.86666666666666" y="30.483333333333334"/>
<rect fill="#4e4c4c" height="4.0" width="0.2" x="37.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="2.2" x="35.36666666666666" y="32.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label2.anchor" width="0.0" x="35.86666666666666" y="32.483333333333334"/>
<rect fill="#4e4c4c" height="6.0" width="0.2" x="39.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="4.2" x="35.36666666666666" y="34.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label3.anchor" width="0.0" x="35.86666666666666" y="34.483333333333334"/>
<rect fill="#4e4c4c" height="8.0" width="0.2" x="41.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="6.2" x="35.36666666666666" y="36.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label4.anchor" width="0.0" x="35.86666666666666" y="36.483333333333334"/>
<rect fill="#4e4c4c" height="8.0" width="0.2" x="43.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="6.2" x="43.66666666666666" y="36.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label5.anchor" width="0.0" x="49.36666666666666" y="36.483333333333334"/>
<rect fill="#4e4c4c" height="6.0" width="0.2" x="45.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="4.2" x="45.66666666666666" y="34.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label6.anchor" width="0.0" x="49.36666666666666" y="34.483333333333334"/>
<rect fill="#4e4c4c" height="4.0" width="0.2" x="47.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="2.2" x="47.66666666666666" y="32.38333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label7.anchor" width="0.0" x="49.36666666666666" y="32.483333333333334"/>
<rect fill="#4e4c4c" height="2.0" width="0.2" x="49.56666666666666" y="28.583333333333332"/>
<rect fill="#4e4c4c" height="0.2" width="0.2" x="49.66666666666666" y="30.383333333333333"/>
<rect height="0.0" id="custom-20x24-22.front.down.label8.anchor" width="0.0" x="49.36666666666666" y="30.483333333333334"/>
<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="17.6" x="33.81666666666666" y="11.333333333333332"/>
<rect fill="#58839b" height="5.2" width="0.5" x="35.71666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="0.5" width="4.6" x="35.71666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="5.2" width="0.5" x="37.91666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="39.81666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="0.5" width="3.0" x="39.81666666666666" y="7.683333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="42.31666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="0.5" width="2.7" x="42.31666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="44.51666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="0.5" width="3.0" x="44.51666666666666" y="7.683333333333333"/>
<rect fill="#58839b" height="3.0" width="0.5" x="47.01666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="0.5" width="2.5" x="47.01666666666666" y="5.183333333333333"/>
<rect fill="#58839b" height="4.4" width="0.5" x="49.01666666666666" y="5.183333333333333"/>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="12.633333333333333"/>
<g transform="translate(25.731026020611555,11.933333333333332)">
<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="27.216666666666658" y="12.733333333333333">3V3</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="14.633333333333333"/>
<g transform="translate(25.731026020611555,13.933333333333332)">
<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="27.216666666666658" y="14.733333333333333">P26</text>
<g transform="translate(22.73102602061156,13.933333333333332)">
<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="23.91666666666666" y="14.733333333333333">D0</text>
<g transform="translate(19.131026020611557,13.933333333333332)">
<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="20.61666666666666" y="14.733333333333333">IRDA</text>
<g transform="translate(15.531026020611561,13.933333333333332)">
<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="17.016666666666662" y="14.733333333333333">PWM5</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="16.633333333333333"/>
<g transform="translate(25.731026020611555,15.933333333333334)">
<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="27.216666666666658" y="16.733333333333334">P14</text>
<g transform="translate(22.73102602061156,15.933333333333334)">
<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="23.91666666666666" y="16.733333333333334">D1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="18.633333333333333"/>
<g transform="translate(25.731026020611555,17.933333333333334)">
<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="27.216666666666658" y="18.733333333333334">P16</text>
<g transform="translate(22.73102602061156,17.933333333333334)">
<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="23.91666666666666" y="18.733333333333334">D2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="20.633333333333333"/>
<g transform="translate(25.731026020611555,19.933333333333334)">
<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="27.216666666666658" y="20.733333333333334">P24</text>
<g transform="translate(22.73102602061156,19.933333333333334)">
<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="23.91666666666666" y="20.733333333333334">D3</text>
<g transform="translate(19.131026020611557,19.933333333333334)">
<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.61666666666666" y="20.733333333333334">PWM4</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="22.633333333333333"/>
<g transform="translate(25.731026020611555,21.933333333333334)">
<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="27.216666666666658" y="22.733333333333334">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="29.51666666666666" y="24.633333333333333"/>
<g transform="translate(25.731026020611555,23.933333333333334)">
<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="27.216666666666658" y="24.733333333333334">P22</text>
<g transform="translate(22.73102602061156,23.933333333333334)">
<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="23.91666666666666" y="24.733333333333334">D4</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="24.633333333333333"/>
<g transform="translate(56.53102602061156,23.933333333333334)">
<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="58.016666666666666" y="24.733333333333334">P0</text>
<g transform="translate(60.131026020611564,23.933333333333334)">
<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.31666666666666" y="24.733333333333334">D5</text>
<g transform="translate(63.131026020611564,23.933333333333334)">
<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.61666666666666" y="24.733333333333334">TX2</text>
<g transform="translate(66.73102602061155,23.933333333333334)">
<rect fill="#f95" 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="68.21666666666665" y="24.733333333333334">SCL2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="22.633333333333333"/>
<g transform="translate(56.53102602061156,21.933333333333334)">
<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="58.016666666666666" y="22.733333333333334">P23</text>
<g transform="translate(60.131026020611564,21.933333333333334)">
<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="61.61666666666667" y="22.733333333333334">ADC3</text>
<g transform="translate(63.731026020611566,21.933333333333334)">
<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="64.91666666666667" y="22.733333333333334">D6</text>
<g transform="translate(66.73102602061155,21.933333333333334)">
<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="67.91666666666666" y="22.733333333333334">A0</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="20.633333333333333"/>
<g transform="translate(56.53102602061156,19.933333333333334)">
<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="58.016666666666666" y="20.733333333333334">?</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="18.633333333333333"/>
<g transform="translate(56.53102602061156,17.933333333333334)">
<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="58.016666666666666" y="18.733333333333334">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="16.633333333333333"/>
<g transform="translate(56.53102602061156,15.933333333333334)">
<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="58.016666666666666" y="16.733333333333334">P8</text>
<g transform="translate(60.131026020611564,15.933333333333334)">
<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.31666666666666" y="16.733333333333334">D7</text>
<g transform="translate(63.131026020611564,15.933333333333334)">
<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.61666666666666" y="16.733333333333334">PWM2</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="14.633333333333333"/>
<g transform="translate(56.53102602061156,13.933333333333332)">
<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="58.016666666666666" y="14.733333333333333">P9</text>
<g transform="translate(60.131026020611564,13.933333333333332)">
<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.31666666666666" y="14.733333333333333">D8</text>
<g transform="translate(63.131026020611564,13.933333333333332)">
<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.61666666666666" y="14.733333333333333">PWM3</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="53.11666666666666" y="12.633333333333333"/>
<g transform="translate(56.53102602061156,11.933333333333332)">
<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="58.016666666666666" y="12.733333333333333">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="32.76666666666666" y="30.383333333333333"/>
<g transform="translate(28.981026020611555,29.683333333333334)">
<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="30.466666666666658" y="30.483333333333334">P21</text>
<g transform="translate(25.98102602061156,29.683333333333334)">
<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.16666666666666" y="30.483333333333334">D9</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="32.76666666666666" y="32.38333333333333"/>
<g transform="translate(28.981026020611555,31.683333333333334)">
<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="30.466666666666658" y="32.483333333333334">P6</text>
<g transform="translate(25.98102602061156,31.683333333333334)">
<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.16666666666666" y="32.483333333333334">D10</text>
<g transform="translate(22.381026020611557,31.683333333333334)">
<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="23.86666666666666" y="32.483333333333334">PWM0</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="32.76666666666666" y="34.38333333333333"/>
<g transform="translate(28.981026020611555,33.68333333333334)">
<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="30.466666666666658" y="34.483333333333334">P7</text>
<g transform="translate(25.98102602061156,33.68333333333334)">
<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.16666666666666" y="34.483333333333334">D11</text>
<g transform="translate(22.381026020611557,33.68333333333334)">
<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="23.86666666666666" y="34.483333333333334">PWM1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="32.76666666666666" y="36.38333333333333"/>
<g transform="translate(28.981026020611555,35.68333333333334)">
<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="30.466666666666658" y="36.483333333333334">P10</text>
<g transform="translate(25.98102602061156,35.68333333333334)">
<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.16666666666666" y="36.483333333333334">D12</text>
<g transform="translate(22.381026020611557,35.68333333333334)">
<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="23.86666666666666" y="36.483333333333334">RX1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="49.86666666666666" y="36.38333333333333"/>
<g transform="translate(53.28102602061156,35.68333333333334)">
<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="54.766666666666666" y="36.483333333333334">GND</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="49.86666666666666" y="34.38333333333333"/>
<g transform="translate(53.28102602061156,33.68333333333334)">
<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="54.766666666666666" y="34.483333333333334">P11</text>
<g transform="translate(56.881026020611564,33.68333333333334)">
<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="58.06666666666666" y="34.483333333333334">D13</text>
<g transform="translate(59.881026020611564,33.68333333333334)">
<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="61.36666666666667" y="34.483333333333334">TX1</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="49.86666666666666" y="32.38333333333333"/>
<g transform="translate(53.28102602061156,31.683333333333334)">
<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="54.766666666666666" y="32.483333333333334">?</text>
<rect fill="#4e4c4c" height="0.2" width="2.6" x="49.86666666666666" y="30.383333333333333"/>
<g transform="translate(53.28102602061156,29.683333333333334)">
<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="54.766666666666666" y="30.483333333333334">P1</text>
<g transform="translate(56.881026020611564,29.683333333333334)">
<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="58.06666666666666" y="30.483333333333334">D14</text>
<g transform="translate(59.881026020611564,29.683333333333334)">
<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="61.36666666666667" y="30.483333333333334">RX2</text>
<g transform="translate(63.481026020611566,29.683333333333334)">
<rect fill="#f95" 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.96666666666667" y="30.483333333333334">SDA2</text>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

View File

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

View File

@@ -0,0 +1,42 @@
/* This file was auto-generated from lsc-lma35.json using boardgen */
#include <Arduino.h>
extern "C" {
// clang-format off
PinInfo pinTable[PINS_COUNT] = {
// D0: P26, PWM5, IRDA
{GPIO26, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D1: P14, SD_CLK, SCK
{GPIO14, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0},
// D2: P16, SD_D0, MOSI
{GPIO16, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0},
// D3: P24, PWM4
{GPIO24, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D4: P22, TDI, FSI
{GPIO22, PIN_GPIO | PIN_IRQ | PIN_JTAG, PIN_NONE, 0},
// D5: P0, UART2_TX, I2C2_SCL
{GPIO0, PIN_GPIO | PIN_IRQ | PIN_I2C | PIN_UART, PIN_NONE, 0},
// D6: P23, ADC3, TDO, FSO
{GPIO23, PIN_GPIO | PIN_IRQ | PIN_ADC | PIN_JTAG, PIN_NONE, 0},
// D7: P8, PWM2
{GPIO8, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D8: P9, PWM3
{GPIO9, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D9: P21, I2C1_SDA, TMS, MCLK, ^FCS
{GPIO21, PIN_GPIO | PIN_IRQ | PIN_I2C | PIN_I2S | PIN_JTAG, PIN_NONE, 0},
// D10: P6, PWM0
{GPIO6, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D11: P7, PWM1
{GPIO7, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0},
// D12: P10, UART1_RX
{GPIO10, PIN_GPIO | PIN_IRQ | PIN_UART, PIN_NONE, 0},
// D13: P11, UART1_TX
{GPIO11, PIN_GPIO | PIN_IRQ | PIN_UART, PIN_NONE, 0},
// D14: P1, UART2_RX, I2C2_SDA
{GPIO1, PIN_GPIO | PIN_IRQ | PIN_I2C | PIN_UART, PIN_NONE, 0},
};
// clang-format on
} // extern "C"

View File

@@ -0,0 +1,37 @@
/* This file was auto-generated from lsc-lma35.json using boardgen */
#pragma once
#include <WVariant.h>
// clang-format off
// Pins
// ----
#define PINS_COUNT 15
#define NUM_DIGITAL_PINS 15
#define NUM_ANALOG_INPUTS 1
#define NUM_ANALOG_OUTPUTS 0
// Analog pins
// -----------
#define PIN_A0 6u // GPIO23
#define A0 PIN_A0
// SPI Interfaces
// --------------
#define SPI_INTERFACES_COUNT 0
// Wire Interfaces
// ---------------
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE2_SCL 5u // GPIO0
#define PIN_WIRE2_SDA 14u // GPIO1
// Serial ports
// ------------
#define SERIAL_INTERFACES_COUNT 2
#define PIN_SERIAL1_RX 12u // GPIO10
#define PIN_SERIAL1_TX 13u // GPIO11
#define PIN_SERIAL2_RX 14u // GPIO1
#define PIN_SERIAL2_TX 5u // GPIO0

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wb2l`
MCU | BK7231T
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 13x GPIO, 5x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v4.2
## Usage
**Board code:** `wb2l`
In `platformio.ini`:
```ini
[env:wb2l]
platform = libretuya
board = wb2l
framework = arduino
```
## Pinout
![Pinout](pinout_wb2l.svg)

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wb2s`
MCU | BK7231T
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 14x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v4.2
## Usage
**Board code:** `wb2s`
In `platformio.ini`:
```ini
[env:wb2s]
platform = libretuya
board = wb2s
framework = arduino
```
## Pinout
![Pinout](pinout_wb2s.svg)

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wb3l`
MCU | BK7231T
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 16x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v4.2
## Usage
**Board code:** `wb3l`
In `platformio.ini`:
```ini
[env:wb3l]
platform = libretuya
board = wb3l
framework = arduino
```
## Pinout
![Pinout](pinout_wb3l.svg)

View File

@@ -10,6 +10,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wb3s`
MCU | BK7231T
Manufacturer | Beken
Series | BK72XX
@@ -21,6 +22,19 @@ I/O | 15x GPIO, 6x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
Bluetooth | BLE v4.2
## Usage
**Board code:** `wb3s`
In `platformio.ini`:
```ini
[env:wb3s]
platform = libretuya
board = wb3s
framework = arduino
```
## Pinout
![Pinout](pinout_wb3s.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|---------------------------------
Board code | `wr2`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 7x GPIO, 5x PWM, 1x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr2`
In `platformio.ini`:
```ini
[env:wr2]
platform = libretuya
board = wr2
framework = arduino
```
## Pinout
![Pinout](pinout_wr2.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|---------------------------------
Board code | `wr2e`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 7x GPIO, 4x PWM, 1x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr2e`
In `platformio.ini`:
```ini
[env:wr2e]
platform = libretuya
board = wr2e
framework = arduino
```
## Pinout
![Pinout](pinout_wr2e.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|---------------------------------
Board code | `wr2l`
MCU | RTL8710BX
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 5x GPIO, 4x PWM, 1x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr2l`
In `platformio.ini`:
```ini
[env:wr2l]
platform = libretuya
board = wr2l
framework = arduino
```
## Pinout
![Pinout](pinout_wr2l.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|-------------------------
Board code | `wr2le`
MCU | RTL8710BX
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 5x GPIO, 5x PWM, 1x UART
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr2le`
In `platformio.ini`:
```ini
[env:wr2le]
platform = libretuya
board = wr2le
framework = arduino
```
## Pinout
![Pinout](pinout_wr2le.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wr3`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr3`
In `platformio.ini`:
```ini
[env:wr3]
platform = libretuya
board = wr3
framework = arduino
```
## Pinout
![Pinout](pinout_wr3.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wr3e`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr3e`
In `platformio.ini`:
```ini
[env:wr3e]
platform = libretuya
board = wr3e
framework = arduino
```
## Pinout
![Pinout](pinout_wr3e.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wr3l`
MCU | RTL8710BX
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr3l`
In `platformio.ini`:
```ini
[env:wr3l]
platform = libretuya
board = wr3l
framework = arduino
```
## Pinout
![Pinout](pinout_wr3l.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|----------------------------------
Board code | `wr3le`
MCU | RTL8710BX
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 11x GPIO, 6x PWM, 2x UART, 2x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr3le`
In `platformio.ini`:
```ini
[env:wr3le]
platform = libretuya
board = wr3le
framework = arduino
```
## Pinout
![Pinout](pinout_wr3le.svg)

View File

@@ -11,6 +11,7 @@
Parameter | Value
-------------|---------------------------------
Board code | `wr3n`
MCU | RTL8710BN
Manufacturer | Realtek
Series | AmebaZ
@@ -21,6 +22,19 @@ Voltage | 3.0V - 3.6V
I/O | 9x GPIO, 5x PWM, 2x UART, 1x ADC
Wi-Fi | 802.11 b/g/n
## Usage
**Board code:** `wr3n`
In `platformio.ini`:
```ini
[env:wr3n]
platform = libretuya
board = wr3n
framework = arduino
```
## Pinout
![Pinout](pinout_wr3n.svg)

View File

@@ -76,6 +76,19 @@ env.AddLibrary(
],
)
# Sources - uf2ota library
ltchiptool_dir = platform.get_package_dir(f"tool-ltchiptool")
env.AddLibrary(
name="uf2ota",
base_dir=ltchiptool_dir,
srcs=[
"+<uf2ota/*.c>",
],
includes=[
"+<.>",
],
)
# Sources - board variant
env.AddLibrary(
name="board_${VARIANT}",

View File

@@ -7,6 +7,12 @@ from SCons.Script import Builder, DefaultEnvironment
env = DefaultEnvironment()
board = env.BoardConfig()
# Install PyCryptodome for OTA packaging with AES
try:
import Cryptodome
except ImportError:
env.Execute("$PYTHONEXE -m pip install pycryptodomex")
ROOT_DIR = join("$SDK_DIR", "beken378")
APP_DIR = join(ROOT_DIR, "app")
DRIVER_DIR = join(ROOT_DIR, "driver")
@@ -350,9 +356,6 @@ env.AddLibrary(
"+<lwip-2.0.2/src/core/ipv6/*.c>",
"+<lwip-2.0.2/src/netif/ethernet.c>",
"+<dhcpd/*.c>",
# use ethernetif.c from AliOS since it enables netif IGMP flag
"-<lwip-2.0.2/port/ethernetif.c>",
"+<$SDK_DIR/beken378/alios/lwip-2.0.2/port/ethernetif.c>",
],
includes=[
"+<lwip-2.0.2/port>",
@@ -587,7 +590,7 @@ env.BuildLibraries()
# Main firmware outputs and actions
env.Replace(
# linker command (encryption + packaging)
LINK="${LINK2BIN} ${VARIANT} '' ''",
LINK="${LTCHIPTOOL} link2bin ${VARIANT} '' ''",
# UF2OTA input list
UF2OTA=[
# app binary image (enc+crc), OTA1 (uploader) only
@@ -604,5 +607,12 @@ env.Replace(
"", # not used for OTA2
"",
),
# OTA RBL package, OTA2 (uf2ota lib) only
(
"", # not used for OTA1
"",
"download",
"${BUILD_DIR}/${MCULC}_app.ota.rbl",
),
],
)

View File

@@ -294,7 +294,7 @@ env.BuildLibraries()
# Main firmware outputs and actions
env.Replace(
# linker command (dual .bin outputs)
LINK="${LINK2BIN} ${VARIANT} xip1 xip2",
LINK="${LTCHIPTOOL} link2bin ${VARIANT} xip1 xip2",
# default output .bin name
IMG_FW="image_${FLASH_OTA1_OFFSET}.ota1.bin",
# UF2OTA input list

View File

@@ -2,16 +2,15 @@
from os.path import join
from ltchiptool import Family
from SCons.Script import DefaultEnvironment
from tools.util.platform import get_family
env = DefaultEnvironment()
def env_add_defaults(env, platform, board):
# Get Family object for this board
family = get_family(short_name=board.get("build.family"))
family = Family.get(short_name=board.get("build.family"))
# Default environment variables
vars = dict(
SDK_DIR=platform.get_package_dir(family.framework),
@@ -36,10 +35,8 @@ def env_add_defaults(env, platform, board):
VARIANT=board.get("build.variant"),
LDSCRIPT_SDK=board.get("build.ldscript_sdk"),
LDSCRIPT_ARDUINO=board.get("build.ldscript_arduino"),
# Link2Bin tool
LINK2BIN='"${PYTHONEXE}" "${LT_DIR}/tools/link2bin.py"',
UF2OTA_PY='"${PYTHONEXE}" "${LT_DIR}/tools/uf2ota/uf2ota.py"',
UF2UPLOAD_PY='"${PYTHONEXE}" "${LT_DIR}/tools/upload/uf2upload.py"',
# ltchiptool variables
LTCHIPTOOL='"${PYTHONEXE}" -m ltchiptool',
# Fix for link2bin to get tmpfile name in argv
LINKCOM="${LINK} ${LINKARGS}",
LINKARGS="${TEMPFILE('-o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS', '$LINKCOMSTR')}",

View File

@@ -35,14 +35,13 @@ def env_uf2ota(env, *args, **kwargs):
env["UF2OUT_BASE"] = basename(output)
cmd = [
"@${UF2OTA_PY}",
"@${LTCHIPTOOL} uf2 write",
f'--output "{output}"',
"--family ${FAMILY}",
"--board ${VARIANT}",
f"--version {lt_version}",
f'--fw "{project_name}:{project_version}"',
f"--date {int(now.timestamp())}",
"write",
inputs,
]
@@ -76,7 +75,10 @@ def env_uf2upload(env, target):
return
# add main upload target
env.Replace(UPLOADER="${UF2UPLOAD_PY}", UPLOADCMD="${UPLOADER} ${UPLOADERFLAGS}")
env.Replace(
UPLOADER="${LTCHIPTOOL} uf2 upload",
UPLOADCMD="${UPLOADER} ${UPLOADERFLAGS}",
)
actions.append(env.VerboseAction("${UPLOADCMD}", "Uploading ${UF2OUT_BASE}"))
env.AddPlatformTarget("upload", target, actions, "Upload")

40
docs/cloudcutter.md Normal file
View File

@@ -0,0 +1,40 @@
# Flashing with tuya-cloudcutter
[`tuya-cloudcutter`](https://github.com/tuya-cloudcutter/tuya-cloudcutter) is a tool that disconnects IoT devices from the Tuya cloud, while also allowing **remote firmware flashing**. This means you can flash ESPHome without even connecting USB-UART to the device.
This is an unofficial guide, that worked for me during testing. The steps may not work for every device; you should also refer to the cloudcutter's manual pages.
**Currently, `tuya-cloudcutter` works for BK7231T and BK7231N only.**
If your device doesn't have a profile yet, it will probably not work. You can contribute by [taking a device dump](https://github.com/tuya-cloudcutter/tuya-cloudcutter#device-dumps) and posting it on cloudcutter's issues page.
## Instructions
### Preparation
1. Get a laptop (or a PC with Wi-Fi) with Linux and Docker installed. This was tested on Ubuntu 20.04, but you should be able to use another Debian-based distribution with NetworkManager.
- To install Docker, run `sudo apt-get install docker.io`. When it completes, run `sudo adduser <your username> docker` and reboot the machine.
- This was also successfully performed on a VirtualBox VM, with a USB Wi-Fi adapter redirected to the VM.
2. Install `git`, if you haven't already (`sudo apt-get install git`).
3. `git clone https://github.com/tuya-cloudcutter/tuya-cloudcutter`
### Firmware building
1. [Compile ESPHome](projects/esphome.md), or your custom firmware based on LibreTuya.
2. Get the firmware binary, named `bk7231x_app.ota.ug.bin` from the build directory (`.pio/build/<board>/` or `.esphome/build/<board>/.pioenvs/<board>/`).
3. Put the file to `custom-firmware` of tuya-cloudcutter.
### Pairing and flashing
1. Run `./run_flash.sh` from the cloudcutter directory.
2. Answer questions about the desired firmware file, the device vendor and profile.
3. Put the device to AP mode: (**)
- Bulbs (devices without buttons) usually need to be power-cycled a few times, until they start blinking slowly.
- Switches, plugs, relays (devices with buttons) usually enable AP after pressing the button for a few seconds.
- If the device (bulb or switch LED) is blinking quickly (~2 times per second), do the procedure again.
4. Cloudcutter will scan for APs, connect to the device and send a payload to it.
5. The device will most likely hang (not respond). Reboot it again to AP mode (just like in step 9).
6. Cloudcutter will scan for APs again, configure the device to talk to it, then begin the OTA update.
7. After around 30 seconds, the device will boot new firmware 👏
\*\* Use a smartphone with the Wi-Fi screen open and scanning, so that you can see if AP mode got enabled.

View File

@@ -1,59 +0,0 @@
# Getting started
Using LibreTuya is simple, just like every other PlatformIO development platform.
## For your own projects
If you're developing your own embedded software, and want it to run on LibreTuya-supported chips, the installation is simple:
1. [Install PlatformIO](https://platformio.org/platformio-ide)
2. `platformio platform install https://github.com/kuba2k2/libretuya`
3. Create a project, build it and upload to the chip
## Community projects
LibreTuya was developed with popular community projects in mind. Currently, unofficial ESPHome port is available (the PR will hopefully be merged into upstream at some point).
### ESPHome
Because ESPHome does not natively support running on non-ESP chips, you need to use a fork of the project.
Assuming you have PlatformIO, git and Python installed:
1. Open a terminal/cmd.exe, create `esphome` directory and `cd` into it.
2. `git clone https://github.com/kuba2k2/libretuya-esphome -b platform/libretuya` (you need the `platform/libretuya` branch)
3. Go to [Boards & CPU list](https://kuba2k2.github.io/libretuya/docs/supported/) and choose your board name.
4. Create a YAML config file for your device. You can either:
- use `python -m esphome wizard yourdevice.yml` - type answers to the six questions the wizard asks
- if your board isn't available in the wizard yet, use the manual YAML method below
- write a config file manually:
```yaml
esphome:
name: yourdevice
libretuya:
board: wr3
logger:
api:
password: ""
ota:
password: ""
wifi:
ssid: "YourWiFiSSID"
password: "SecretPa$$w0rd"
ap:
ssid: "Yourdevice Fallback Hotspot"
password: "Dv2hZMGZRUvy"
```
5. Install LibreTuya: `platformio platform install https://github.com/kuba2k2/libretuya`. This step is necessary, as otherwise ESPHome will try to use a version from the registry, which is often outdated.
6. Edit the config file to use your installed LT version:
```yaml
libretuya:
board: wr3
framework:
version: latest
```
7. `python -m esphome compile yourdevice.yml`
8. The binary file, ready for uploading, is now in `.esphome/build/yourdevice/.pioenvs/yourdevice/`. Refer to your board README to find appropriate flashing instructions.

View File

@@ -0,0 +1,38 @@
# Getting started
Using LibreTuya is simple, just like every other PlatformIO development platform.
1. [Install PlatformIO](https://platformio.org/platformio-ide)
2. `platformio platform install https://github.com/kuba2k2/libretuya`
## Board selection
- Go to [Boards & CPU list](../status/supported.md).
- Find the board your device has (usually, the model number is written on the silkscreen).
- If your board isn't available yet, use one of the "Generic" boards that matches the CPU you have.
- Click on the board name. From the documentation page, note the board code.
- Use this code to create a PlatformIO project.
## GPIO usage
!!! important
This can be confusing at first, so make sure to read this part carefully to understand it.
Input/output pin numbers in Arduino code (i.e. `digitalWrite()`) use Arduino pin numbers - for example `D1`, `D3`. This is the same as simply `1` or `3`, but it cannot be confused with CPU GPIO numbers.
On the board pinout page, the purple blocks represent Arduino pins, while the dark red blocks refer to GPIO numbers.
Projects like ESPHome also use Arduino pin numbering.
## Develop your own project
If you're developing your own embedded software, and want it to run on LibreTuya-supported chips, create a project.
- use PlatformIO IDE (PIO Home -> Open -> New Project)
- run `pio project init` in your desired project directory
Next, read [Uploading](uploading.md) guide to run your project!
## Run community projects
LibreTuya was developed with popular community projects in mind. Currently, unofficial [ESPHome port](../projects/esphome.md) is available ([the PR](https://github.com/esphome/esphome/pull/3509) will hopefully be merged into upstream at some point).

View File

@@ -0,0 +1,6 @@
# Uploading
The uploading/flashing procedure is different for every chip family:
- BK72xx / BK7231 - [click here](../platform/beken-72xx/flashing.md)
- Realtek AmebaZ (RTL8710BN/BX) - [click here](../platform/realtek-ambz/flashing.md)

View File

@@ -3,8 +3,6 @@
from os.path import join
from typing import List
from tools.util.fileio import writetext
class Markdown:
items: List[str]
@@ -15,7 +13,9 @@ class Markdown:
self.output = join(dir, f"{name}.md")
def write(self):
writetext(self.output, self.items)
with open(self.output, "w", encoding="utf-8") as f:
f.write("\n".join(self.items))
f.write("\n")
def pad(self, s: str, i: int) -> str:
return s + " " * (i - len(s))

View File

@@ -29,7 +29,7 @@ For easier understanding, these update types will be referred to in this documen
## Custom family IDs
{%
include-markdown "../supported_families.md"
include-markdown "../status/supported_families.md"
%}
## Extension tags

View File

@@ -10,6 +10,14 @@ Downloading is done using UART. For best experience, you should have two USB<->U
If you're not using auto-reset, you'll have to reset the chip manually when upload starts (you have 10 seconds to do that).
- UART1 is used for uploading the code. This adapter will be used by PlatformIO.
- UART2 allows for log output. You can have a terminal session continuously open on this adapter.
!!! hint
If you only have a single adapter, or just want to use the UART1 (upload) port only, you can change the logging port.
Refer to [Options & config](../../reference/config.md) (`Serial output` section). Set `LT_UART_DEFAULT_PORT` to `1`, which will use UART1 for all output.
## Automatically - using PlatformIO (recommended)
LibreTuya has built-in firmware uploaders. Pressing `Upload` in PlatformIO IDE does all the work for you.
@@ -30,7 +38,7 @@ upload_port = COM96
## Manually - using `uf2upload`
{%
include-markdown "../../flashing-uf2ota.md"
include-markdown "../../ota/flashing.md"
%}
## Manually - using raw binaries/BkWriter

View File

@@ -3,7 +3,10 @@
- [Flashing (Tuya manual)](https://developer.tuya.com/en/docs/iot/burn-and-authorize-wr-series-modules?id=Ka789pjc581u8)
- [ImageTool (AmebaZ/AmebaD)](https://images.tuyacn.com/smart/Image_Tool/Image_Tool.zip)
Downloading is done using UART. It is required to put the chip into download mode, prior to flashing. This can be done by resetting the chip, while pulling UART2_TX to GND. If successful, the chip should print few garbage characters every second.
Downloading is done using UART2 (sometimes called Log_UART). Refer to your board documentation to find the correct pins.
!!! important
It is required to put the chip into download mode, prior to flashing. This can be done by resetting the chip, while pulling UART2_TX to GND. If successful, the chip should print few garbage characters every second.
## Automatically - using PlatformIO (recommended)
@@ -20,7 +23,7 @@ upload_port = COM60
## Manually - using `uf2upload`
{%
include-markdown "../../flashing-uf2ota.md"
include-markdown "../../ota/flashing.md"
%}
## Manually - using raw binaries/BkWriter

96
docs/projects/esphome.md Normal file
View File

@@ -0,0 +1,96 @@
# ESPHome
!!! note
Read [Getting started](../getting-started/README.md) first.
## Install ESPHome
Because ESPHome does not natively support running on non-ESP chips, you need to use a fork of the project.
Assuming you have PlatformIO, git and Python installed:
1. Open a terminal/cmd.exe, create `esphome` directory and `cd` into it.
2. `git clone https://github.com/kuba2k2/libretuya-esphome -b platform/libretuya` (you need the `platform/libretuya` branch)
!!! note
For Linux users (or if `python -m esphome` doesn't work for you):
- unistall ESPHome first: `pip uninstall esphome`
- install the forked version: `pip install -e .`
## Create your device config
1. Go to [Boards & CPU list](https://kuba2k2.github.io/libretuya/docs/supported/), click on your board and remember your board code.
2. Create a YAML config file for your device. You can either:
- use `python -m esphome wizard yourdevice.yml` - type answers to the six questions the wizard asks, OR:
- if your board isn't available in the wizard yet, use the manual YAML method below
- write a config file manually:
```yaml
esphome:
name: yourdevice
libretuya:
board: wr3 # THIS IS YOUR BOARD CODE
framework:
version: latest
logger:
api:
password: ""
ota:
password: ""
wifi:
ssid: "YourWiFiSSID"
password: "SecretPa$$w0rd"
ap:
ssid: "Yourdevice Fallback Hotspot"
password: "Dv2hZMGZRUvy"
```
## Configuration options
All options from [Options & config](../reference/config.md) can be customized in the `libretuya:` block:
```yaml
libretuya:
framework:
version: latest
lt_config:
LT_LOG_HEAP: 1
LT_UART_DEFAULT_PORT: 2
LT_UART_SILENT_ALL: 0
```
(this is only an example)
Additionally, few options have their dedicated keys:
```yaml
libretuya:
framework:
version: latest
# verbose/trace/debug/info/warn/error/fatal
loglevel: warn
# suppress chip's SDK log messages
# (same as LT_UART_SILENT_ALL above)
sdk_silent: true
# disable SWD/JTAG so that all GPIOs can be used
# set to false if you want to attach a debugger
gpio_recover: true
```
(these values are defaults)
## Compile & upload
- `python -m esphome compile yourdevice.yml` - this will only compile the code
- `python -m esphome upload yourdevice.yml` - this will upload the previously compiled code
- `python -m esphome run yourdevice.yml` - this will compile and upload the code
!!! info
If you want to flash manually:
The binary file, ready for uploading, will be in `.esphome/build/yourdevice/.pioenvs/yourdevice/`.
Refer to your board README to find appropriate flashing instructions.
Or [flash with `tuya-cloudcutter`](../cloudcutter.md).

View File

@@ -15,7 +15,7 @@ custom_fw_version = 1.2.0
## LibreTuya options
!!! note
See [LibreTuyaConfig.h](../ltapi/_libre_tuya_config_8h_source.md) for most options and their defaults.
See [LibreTuyaConfig.h](../../ltapi/_libre_tuya_config_8h_source.md) for most options and their defaults.
All options are configurable via C++ defines in PlatformIO project file. For example:
```ini

View File

@@ -1,7 +1,7 @@
# Implementation status
{%
include-markdown "../README.md"
include-markdown "../../README.md"
start="\n## Arduino Core support status\n"
end="\n## License\n"
%}

View File

@@ -26,7 +26,7 @@ A list of chip families currently supported by this project.
!!! note
The term *family* was chosen over *platform*, in order to reduce possible confusion between LibreTuya supported "platforms" and PlatformIO's "platform", as an entire package. *Family* is also more compatible with the UF2 term.
The following list corresponds to UF2 OTA format family names, and is also [available as JSON](../families.json). The IDs are also present in [ChipType.h](../ltapi/_chip_type_8h_source.md).
The following list corresponds to UF2 OTA format family names, and is also [available as JSON](../../families.json). The IDs are also present in [ChipType.h](../../ltapi/_chip_type_8h_source.md).
{%
include-markdown "supported_families.md"

View File

@@ -0,0 +1,32 @@
<!-- This file is auto-generated -->
Name | MCU | Flash | RAM | Pins* | Wi-Fi | BLE | ZigBee | Family name
--------------------------------------------------------------------------|-----------|-------|---------|-------------|-------|-----|--------|----------------
**Generic** | | | | | | | |
[BK7231N (Tuya QFN32)](../../boards/generic-bk7231n-qfn32-tuya/README.md) | BK7231N | 2 MiB | 256 KiB | 19 (19 I/O) | ✔️ | ✔️ | ❌ | `beken-7231n`
[BK7231T (Tuya QFN32)](../../boards/generic-bk7231t-qfn32-tuya/README.md) | BK7231T | 2 MiB | 256 KiB | 19 (19 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[RTL8710BN (2M/468k)](../../boards/generic-rtl8710bn-2mb-468k/README.md) | RTL8710BN | 2 MiB | 256 KiB | 18 (18 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[RTL8710BN (2M/788k)](../../boards/generic-rtl8710bn-2mb-788k/README.md) | RTL8710BN | 2 MiB | 256 KiB | 18 (18 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[RTL8720CF (2M/992k)](../../boards/generic-rtl8720cf-2mb-992k/README.md) | RTL8720CF | 2 MiB | 256 KiB | 20 (20 I/O) | ✔️ | ✔️ | ❌ | `realtek-ambz2`
**Ai-Thinker Co., Ltd.** | | | | | | | |
[BW12](../../boards/bw12/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[BW15](../../boards/bw15/README.md) | RTL8720CF | 2 MiB | 256 KiB | 16 (13 I/O) | ✔️ | ✔️ | ❌ | `realtek-ambz2`
**Tuya Inc.** | | | | | | | |
[CB2S](../../boards/cb2s/README.md) | BK7231N | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | ❌ | `beken-7231n`
[WB2L](../../boards/wb2l/README.md) | BK7231T | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB2S](../../boards/wb2s/README.md) | BK7231T | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB3L](../../boards/wb3l/README.md) | BK7231T | 2 MiB | 256 KiB | 21 (17 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB3S](../../boards/wb3s/README.md) | BK7231T | 2 MiB | 256 KiB | 22 (16 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WR2](../../boards/wr2/README.md) | RTL8710BN | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2E](../../boards/wr2e/README.md) | RTL8710BN | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3](../../boards/wr3/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3E](../../boards/wr3e/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3N](../../boards/wr3n/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (10 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2L](../../boards/wr2l/README.md) | RTL8710BX | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2LE](../../boards/wr2le/README.md) | RTL8710BX | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3L](../../boards/wr3l/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3LE](../../boards/wr3le/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
**Unknown** | | | | | | | |
[LSC LMA35](../../boards/lsc-lma35/README.md) | BK7231N | 2 MiB | 256 KiB | 22 (15 I/O) | ✔️ | ✔️ | ❌ | `beken-7231n`
**N/A** | | | | | | | |
[Native](../../boards/generic-native/README.md) | NATIVE | 4 MiB | 4 MiB | - | ✔️ | ❌ | ❌ | `host-native`

View File

@@ -1,30 +0,0 @@
<!-- This file is auto-generated -->
Name | MCU | Flash | RAM | Pins* | Wi-Fi | BLE | ZigBee | Family name
-----------------------------------------------------------------------|-----------|-------|---------|-------------|-------|-----|--------|----------------
**Generic** | | | | | | | |
[BK7231N (Tuya QFN32)](../boards/generic-bk7231n-qfn32-tuya/README.md) | BK7231N | 2 MiB | 256 KiB | 19 (19 I/O) | ✔️ | ✔️ | ❌ | `beken-7231n`
[BK7231T (Tuya QFN32)](../boards/generic-bk7231t-qfn32-tuya/README.md) | BK7231T | 2 MiB | 256 KiB | 19 (19 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[RTL8710BN (2M/468k)](../boards/generic-rtl8710bn-2mb-468k/README.md) | RTL8710BN | 2 MiB | 256 KiB | 18 (18 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[RTL8710BN (2M/788k)](../boards/generic-rtl8710bn-2mb-788k/README.md) | RTL8710BN | 2 MiB | 256 KiB | 18 (18 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[RTL8720CF (2M/992k)](../boards/generic-rtl8720cf-2mb-992k/README.md) | RTL8720CF | 2 MiB | 256 KiB | 20 (20 I/O) | ✔️ | ✔️ | ❌ | `realtek-ambz2`
**Ai-Thinker Co., Ltd.** | | | | | | | |
[BW12](../boards/bw12/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[BW15](../boards/bw15/README.md) | RTL8720CF | 2 MiB | 256 KiB | 16 (13 I/O) | ✔️ | ✔️ | ❌ | `realtek-ambz2`
**Tuya Inc.** | | | | | | | |
[CB2S](../boards/cb2s/README.md) | BK7231N | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | ❌ | `beken-7231n`
[WB2L](../boards/wb2l/README.md) | BK7231T | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB2S](../boards/wb2s/README.md) | BK7231T | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB3L](../boards/wb3l/README.md) | BK7231T | 2 MiB | 256 KiB | 21 (17 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WB3S](../boards/wb3s/README.md) | BK7231T | 2 MiB | 256 KiB | 22 (16 I/O) | ✔️ | ✔️ | ❌ | `beken-7231t`
[WR2](../boards/wr2/README.md) | RTL8710BN | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2E](../boards/wr2e/README.md) | RTL8710BN | 2 MiB | 256 KiB | 11 (8 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3](../boards/wr3/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3E](../boards/wr3e/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3N](../boards/wr3n/README.md) | RTL8710BN | 2 MiB | 256 KiB | 16 (10 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2L](../boards/wr2l/README.md) | RTL8710BX | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR2LE](../boards/wr2le/README.md) | RTL8710BX | 2 MiB | 256 KiB | 7 (5 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3L](../boards/wr3l/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
[WR3LE](../boards/wr3le/README.md) | RTL8710BX | 2 MiB | 256 KiB | 16 (12 I/O) | ✔️ | ❌ | ❌ | `realtek-ambz`
**N/A** | | | | | | | |
[Native](../boards/generic-native/README.md) | NATIVE | 4 MiB | 4 MiB | - | ✔️ | ❌ | ❌ | `host-native`

View File

@@ -4,25 +4,17 @@ import sys
from os.path import dirname, join
sys.path.append(join(dirname(__file__), ".."))
import re
from typing import Dict, List, Set, Tuple
from typing import Dict, List, Set
import colorama
from colorama import Fore, Style
from ltchiptool import Board, Family
from ltchiptool.util import readjson, readtext, sizeof
from markdown import Markdown
from tools.util.fileio import readjson, readtext
from tools.util.markdown import Markdown
from tools.util.obj import get, sizeof
from tools.util.platform import (
get_board_list,
get_board_manifest,
get_families,
get_family,
)
def load_boards() -> Dict[str, dict]:
return {board: get_board_manifest(board) for board in get_board_list()}
OUTPUT = join(dirname(__file__), "status")
def load_chip_type_h() -> str:
@@ -41,24 +33,15 @@ def load_chip_type_h() -> str:
return code
def check_mcus(boards: List[Tuple[str, dict]]) -> bool:
for board_name, board in boards:
def check_mcus(boards: List[Board]) -> bool:
for board in boards:
# check if all boards' MCUs are defined in families.json
family_name: str = get(board, "build.family")
mcu_name: str = get(board, "build.mcu")
family = get_family(short_name=family_name)
if not family:
print(
Fore.RED
+ f"ERROR: Family '{family_name}' of board '{board_name}' does not exist"
+ Style.RESET_ALL
)
return False
mcus = [mcu.lower() for mcu in family.mcus]
mcu_name: str = board["build.mcu"]
mcus = [mcu.lower() for mcu in board.family.mcus]
if mcu_name not in mcus:
print(
Fore.RED
+ f"ERROR: MCU '{mcu_name}' of board '{board_name}' is not defined for family '{family_name}'"
+ f"ERROR: MCU '{mcu_name}' of board '{board.name}' is not defined for family '{board.family.name}'"
+ Style.RESET_ALL
)
return False
@@ -67,19 +50,19 @@ def check_mcus(boards: List[Tuple[str, dict]]) -> bool:
def get_family_mcus() -> Set[str]:
out = []
for family in get_families():
for family in Family.get_all():
out += family.mcus
return set(out)
def get_family_names() -> Set[str]:
return set(family.short_name for family in get_families())
return set(family.short_name for family in Family.get_all())
def get_board_mcus(boards: List[Tuple[str, dict]]) -> Set[str]:
def get_board_mcus(boards: List[Board]) -> Set[str]:
out = set()
for _, board in boards:
mcu_name: str = get(board, "build.mcu")
for board in boards:
mcu_name: str = board["build.mcu"]
out.add(mcu_name.upper())
return out
@@ -101,38 +84,37 @@ def get_enum_families(code: str) -> Set[str]:
return set(family[2:] for family in get_enum_keys(code, "ChipFamily"))
def board_sort(tpl):
generic = tpl[0].lower().startswith("generic")
vendor = get(tpl[1], "vendor")
def board_json_sort(tpl):
return tpl[1]["mcu"], tpl[0]
def board_obj_sort(board: Board):
generic = board.is_generic
vendor = board.vendor
if vendor == "N/A":
vendor = "\xff"
generic = False
return (
not generic, # reverse
vendor,
get(tpl[1], "build.mcu"),
get(tpl[1], "mcu"),
tpl[0],
board["build.mcu"],
board["mcu"],
board.name,
)
def get_board_symbol(board_name: str, board: dict) -> str:
symbol = get(board, "symbol")
if not symbol and board_name.startswith("generic-"):
symbol = board_name[8:]
else:
symbol = symbol or board_name.upper()
return symbol
def get_board_symbol(board: Board) -> str:
return board.symbol or board.generic_name or board.name.upper()
def write_chips(mcus: List[str]):
md = Markdown(dirname(__file__), "supported_chips")
md = Markdown(OUTPUT, "supported_chips")
md.add_list(*mcus)
md.write()
def write_boards(boards: List[Tuple[str, dict]]):
md = Markdown(dirname(__file__), "supported_boards")
def write_boards(boards: List[Board]):
md = Markdown(OUTPUT, "supported_boards")
header = [
"Name",
"MCU",
@@ -147,34 +129,33 @@ def write_boards(boards: List[Tuple[str, dict]]):
rows = []
vendor_prev = ""
for board_name, board in boards:
family = get_family(short_name=get(board, "build.family"))
for board in boards:
# add board vendor as a row
vendor = get(board, "vendor")
vendor = board["vendor"]
if vendor_prev != vendor:
rows.append([f"**{vendor}**"])
vendor_prev = vendor
# count total pin count & IO count
pins = "-"
pinout: Dict[str, dict] = get(board, "pcb.pinout")
pinout: Dict[str, dict] = board["pcb.pinout"]
if pinout:
pinout = [pin for name, pin in pinout.items() if name.isnumeric()]
pins_total = len(pinout)
pins_io = sum(1 for pin in pinout if "ARD" in pin)
pins = f"{pins_total} ({pins_io} I/O)"
# format row values
symbol = get_board_symbol(board_name, board)
board_url = f"[{symbol}](../boards/{board_name}/README.md)"
symbol = get_board_symbol(board)
board_url = f"[{symbol}](../../boards/{board.name}/README.md)"
row = [
board_url,
get(board, "build.mcu").upper(),
sizeof(get(board, "upload.flash_size")),
sizeof(get(board, "upload.maximum_ram_size")),
board["build.mcu"].upper(),
sizeof(board["upload.flash_size"]),
sizeof(board["upload.maximum_ram_size"]),
pins,
"✔️" if "wifi" in get(board, "connectivity") else "",
"✔️" if "ble" in get(board, "connectivity") else "",
"✔️" if "zigbee" in get(board, "connectivity") else "",
f"`{family.name}`",
"✔️" if "wifi" in board["connectivity"] else "",
"✔️" if "ble" in board["connectivity"] else "",
"✔️" if "zigbee" in board["connectivity"] else "",
f"`{board.family.name}`",
]
rows.append(row)
md.add_table(header, *rows)
@@ -186,7 +167,7 @@ def write_unsupported_boards(
name: str,
supported: List[str],
):
md = Markdown(dirname(__file__), name)
md = Markdown(OUTPUT, name)
header = [
"Name",
"MCU",
@@ -202,7 +183,7 @@ def write_unsupported_boards(
series_rows = []
series_rows.append([f"**{series_name.upper()} Series**"])
boards = series[series_name]
for board_name, board in sorted(boards.items(), key=board_sort):
for board_name, board in sorted(boards.items(), key=board_json_sort):
if board_name in supported:
continue
row = [
@@ -223,7 +204,7 @@ def write_unsupported_boards(
def write_families():
md = Markdown(dirname(__file__), "supported_families")
md = Markdown(OUTPUT, "supported_families")
header = [
"Title",
"Name (parent)",
@@ -234,7 +215,7 @@ def write_families():
]
rows = []
for family in get_families():
for family in Family.get_all():
row = [
# Title
"[{}]({})".format(
@@ -272,14 +253,14 @@ def write_families():
md.write()
def write_boards_list(boards: List[Tuple[str, dict]]):
def write_boards_list(boards: List[Board]):
md = Markdown(dirname(__file__), join("..", "boards", "SUMMARY"))
items = []
for board_name, board in boards:
symbol = get_board_symbol(board_name, board)
if board_name.startswith("generic-"):
symbol = get(board, "name")
items.append(f"[{symbol}](../boards/{board_name}/README.md)")
for board in boards:
symbol = get_board_symbol(board)
if board.is_generic:
symbol = board["name"]
items.append(f"[{symbol}](../boards/{board.name}/README.md)")
md.add_list(*items)
md.write()
@@ -287,18 +268,17 @@ def write_boards_list(boards: List[Tuple[str, dict]]):
if __name__ == "__main__":
colorama.init()
boards = load_boards()
boards = sorted(boards.items(), key=board_sort)
boards = map(Board, Board.get_list())
boards = sorted(boards, key=board_obj_sort)
code = load_chip_type_h()
errors = False
for name, board in boards:
variant = get(board, "build.variant")
if name != variant:
for board in boards:
if board.name != board["source"]:
print(
Fore.RED
+ f"ERROR: Invalid build.variant of '{name}': '{variant}'"
+ f"ERROR: Invalid build.variant of '{board['source']}': '{board.name}'"
+ Style.RESET_ALL
)
errors = True
@@ -345,5 +325,5 @@ if __name__ == "__main__":
write_unsupported_boards(
series=data,
name=f"unsupported_{name}",
supported=[tpl[0] for tpl in boards],
supported=[board.name for board in boards],
)

1
examples/PinScan/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.pio

116
examples/PinScan/README.md Normal file
View File

@@ -0,0 +1,116 @@
# PinScan
This example allows to quickly check all digital/analog pins of an IoT device.
By using a simple TUI (text user interface), you can check which I/O pins correspond to i.e. button presses, as well as write to one of the pins to see which LED lights up.
!!! warning
Messing with pins in a device can potentially damage some parts of it. Be careful when writing voltages to digital and PWM pins.
Uploading the example and opening up a terminal (e.g. PuTTY) presents this menu:
```
LibreTuya v0.8.0, PinScan v1.0
Board: cb2s
I/O count: 11
Digital I/O count: 11
Analog input count: 1
-------- UART2 --------
Commands:
d - Check digital pins
a - Check analog pins
s - Select UART port
t - Toggle ANSI control codes
r - Reboot (for uploading)
q - Go back to menu, at any time
? - Print help text, also for subcommands
```
The interface expects one-letter commands, without confirmation by `Enter`. The only part which expects an `Enter` keypress is inputting pin numbers and UART port numbers.
Pressing `q` at any time goes back to the main menu, terminating the current process.
!!! note
PinScan works best with a terminal supporting ANSI escape codes (PuTTY does), but this behavior can be disabled using `t`.
Switching to another UART port is possible (for example if the default port is on the pins you want to check) using `s` command. Do not change the port after using any I/O commands, as it won't work; reboot it using `r` before.
By setting `USE_WIFI` in `main.h` to 1, a Telnet server is enabled on port 23. This allows to test I/O pins without having physical, wired access to the device (i.e. using OTA). Make sure to specify correct WiFi credentials.
!!! hint
If your board isn't supported by LT yet, use one of the generic boards.
If your board doesn't even have a known pinout, use `d`/`s` commands of PinScan to ease the mapping of all board pins.
## Digital pins
```
Digital I/O
-------- UART2 --------
Commands:
r - Realtime readout of all pins
o - Read one pin continuously
s - Manual Scan - toggle each pin
h - Write HIGH to a pin
l - Write LOW to a pin
p - Output using pull up/down (default)
w - Output using write low/high (less safe)
```
### Realtime readout of all pins
```
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10
LO LO HI HI HI HI LO LO -- -- LO
```
Screen contents will update when voltage on one of the pins changes. Pins marked with `--` mean the currently used UART port (which can be changed using `s` command; after reboot).
!!! tldr
Try pressing a button to see which pin changes.
### Read one pin continuously
Enter the pin number, it will be probed until you press `q`.
### Manual Scan - toggle each pin
```
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10
HI LO LO LO LO LO LO LO -- -- LO
```
A pin will be toggled every 500ms, starting with D0. Type `n` to move to the next pin.
!!! tldr
Go through the pins to see which lights up an LED.
### Write HIGH/LOW to a pin
Self-explanatory.
### Output using pull/write
Outputs can be toggled by using internal pull-up/pull-down resistors, or by simply writing a voltage. Writing is more current-efficient, but is also less safe if something else supplies different voltage to the pin.
This affects scan and write high/low commands.
!!! tldr
Use `write` output mode (carefully) if there's an LED which doesn't light up with default pull mode.
## Analog pins
```
Analog inputs
-------- UART2 --------
Commands:
r - Realtime readout of all pins
o - Read one pin once
```
### Realtime readout of all pins
Read voltage (in millivolts) on all available analog pins, until `q` is pressed.
### Read one pin once
No need to explain.

View File

@@ -0,0 +1,12 @@
[env]
platform = libretuya
framework = arduino
[env:bk7231n]
board = generic-bk7231n-qfn32-tuya
[env:bk7231t]
board = generic-bk7231t-qfn32-tuya
[env:rtl8710b]
board = generic-rtl8710bn-2mb-788k

View File

@@ -0,0 +1,70 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include "pinscan.h"
static unsigned long last = 0;
static void readAnalog(uint8_t number, pin_size_t pin) {
stream->write("A");
stream->write('0' + number);
stream->print(": ");
stream->print(analogReadVoltage(pin));
stream->print(" mV\t");
}
void runAnalog() {
if (mode[1] == '\0')
return;
int pin = 0;
switch (mode[1]) {
case '?':
printHelp('a');
break;
case 'r':
if (millis() - last < DELAY_READ)
return;
last = millis();
printAnsi(ANSI_LINE_START);
#ifdef PIN_A0
readAnalog(0, PIN_A0);
#endif
#ifdef PIN_A1
readAnalog(1, PIN_A1);
#endif
#ifdef PIN_A2
readAnalog(2, PIN_A2);
#endif
if (!ansi)
stream->println();
return;
case 'o':
pin = inputPin();
// clang-format off
#ifdef PIN_A0
if (pin == 0) {
readAnalog(0, PIN_A0);
} else
#endif
#ifdef PIN_A1
if (pin == 1) {
readAnalog(0, PIN_A0);
} else
#endif
#ifdef PIN_A2
if (pin == 2) {
readAnalog(2, PIN_A2);
} else
#endif
// clang-format on
{
stream->print("Pin unavailable");
}
stream->println();
mode[1] = '?';
ansiSkipErase = true;
return;
}
mode[1] = '\0';
}

View File

@@ -0,0 +1,210 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include "pinscan.h"
#define PULL 0
#define WRITE 1
static bool pins[NUM_DIGITAL_PINS] = {};
static bool used[NUM_DIGITAL_PINS] = {};
static bool active = false;
static int pin = -1;
static unsigned long last = 0;
static bool outputMode = PULL;
static void printPin(bool state) {
if (ansi) {
stream->print(state ? (ANSI_RED "HI" ANSI_RESET) : (ANSI_BLUE "LO" ANSI_RESET));
} else {
stream->print(state ? "HI" : "LO");
}
}
static void printState() {
printAnsi(ANSI_TWO_LINES);
printAnsi(ANSI_ERASE_LINE);
for (pin_size_t i = 0; i < NUM_DIGITAL_PINS; i++) {
if (i < 10)
stream->write(' ');
stream->write('D');
stream->print(i);
stream->write(' ');
}
stream->println();
printAnsi(ANSI_ERASE_LINE);
for (pin_size_t i = 0; i < NUM_DIGITAL_PINS; i++) {
stream->write(' ');
if (used[i]) {
printPin(pins[i]);
} else {
stream->print("--");
}
stream->write(' ');
}
stream->println();
}
static void irqHandler(void *ptr) {
bool *pin = (bool *)ptr;
pin_size_t i = pin - pins;
pins[i] = digitalRead(i);
// change interrupt level according to current state
PinStatus status = pins[i] ? FALLING : RISING;
attachInterruptParam(i, irqHandler, status, ptr);
printState();
}
static void digitalAllIrq() {
#if USE_WIFI
LT_I("Disabling logger");
lt_log_disable();
Serial.end();
#endif
for (pin_size_t i = 0; i < NUM_DIGITAL_PINS; i++) {
if (pinSkip[0] == i || pinSkip[1] == i) {
used[i] = false;
continue;
}
pinMode(i, INPUT);
pins[i] = digitalRead(i);
used[i] = true;
// choose interrupt level according to current state
PinStatus status = pins[i] ? FALLING : RISING;
attachInterruptParam(i, irqHandler, status, pins + i);
}
active = true;
}
static void digitalOut(pin_size_t pin, PinStatus state) {
if (outputMode == PULL) {
pinMode(pin, state ? INPUT_PULLUP : INPUT_PULLDOWN);
} else {
pinMode(pin, OUTPUT);
digitalWrite(pin, state);
}
pins[pin] = state;
}
static int digitalAllLow() {
int first = -1;
for (pin_size_t i = 0; i < NUM_DIGITAL_PINS; i++) {
if (pinSkip[0] == i || pinSkip[1] == i) {
used[i] = false;
continue;
}
if (first == -1)
first = i;
digitalOut(i, LOW);
used[i] = true;
}
active = true;
return first;
}
void digitalDetach() {
outputMode = PULL;
if (!active)
return;
for (pin_size_t i = 0; i < NUM_DIGITAL_PINS; i++) {
if (pinSkip[0] == i || pinSkip[1] == i) {
continue;
}
if (used[i])
detachInterrupt(i);
used[i] = false;
pinMode(i, INPUT_PULLDOWN);
}
active = false;
pin = -1;
#if USE_WIFI
lt_log_set_port(LT_UART_DEFAULT_LOGGER);
Serial.begin(115200);
LT_I("Logger enabled");
#endif
}
void runDigital() {
if (mode[1] == '\0')
return;
switch (mode[1]) {
case '?':
printHelp('d');
pin = -1;
break;
case 'p':
outputMode = PULL;
stream->println("Will pull outputs UP/DOWN");
mode[1] = '?';
ansiSkipErase = true;
return;
case 'w':
outputMode = WRITE;
stream->println("Will write LOW/HIGH to outputs");
mode[1] = '?';
ansiSkipErase = true;
return;
case 'r':
if (!active) {
digitalAllIrq();
if (ansi)
printHelp('d');
printAnsi("\n\n"); // reserve two lines for readouts
printState();
}
return;
case 'o':
if (pin == -1) {
pin = inputPin();
pinMode(pin, INPUT);
}
printAnsi(ANSI_LINE_START);
stream->print("Pin D");
stream->print(pin);
stream->print(" state: ");
printPin(digitalRead(pin));
if (!ansi)
stream->println();
return;
case 's':
if (pin == -1) {
// choose the first pin
pin = digitalAllLow();
if (ansi)
printHelp('d');
printAnsi("\n\n"); // reserve two lines for readouts
printState();
}
if (mode[2] == 'n') {
// go to next pin; leave the current as LOW
digitalOut(pin, LOW);
do {
if (++pin >= NUM_DIGITAL_PINS)
pin = 0;
} while (used[pin] == false);
printState();
}
mode[2] = '\0';
// toggle the pin every 500ms
if (millis() - last < 500)
return;
last = millis();
digitalOut(pin, (PinStatus)!pins[pin]);
printState();
return;
case 'h':
case 'l':
pin = inputPin();
digitalOut(pin, mode[1] == 'h' ? HIGH : LOW);
stream->println("OK");
mode[1] = '?';
ansiSkipErase = true;
return;
}
mode[1] = '\0';
}

View File

@@ -0,0 +1,67 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include "pinscan.h"
void printHelp(uint8_t mode) {
printAnsiErase();
switch (mode) {
case '\0':
stream->setTimeout(10000);
stream->println("LibreTuya v" LT_VERSION_STR ", PinScan v" EXAMPLE_VER);
stream->println("Board: " LT_BOARD_STR);
stream->print("I/O count: ");
stream->println(PINS_COUNT);
stream->print("Digital I/O count: ");
stream->println(NUM_DIGITAL_PINS);
stream->print("Analog input count: ");
stream->println(NUM_ANALOG_INPUTS);
break;
case 'd':
stream->println("Digital I/O");
break;
case 'a':
stream->println("Analog inputs");
break;
}
line();
stream->println("Commands:");
switch (mode) {
case '\0':
// clang-format off
stream->println(
TAB "d - Check digital pins" EOL
TAB "a - Check analog pins" EOL
// TAB "p - Check PWM outputs" EOL
#if !USE_WIFI
TAB "s - Select UART port" EOL
#endif
TAB "t - Toggle ANSI control codes" EOL
TAB "r - Reboot (for uploading)" EOL
TAB "q - Go back to menu, at any time" EOL
TAB "? - Print help text, also for subcommands" EOL
);
// clang-format on
break;
case 'd':
// clang-format off
stream->println(
TAB "r - Realtime readout of all pins" EOL
TAB "o - Read one pin continuously" EOL
TAB "s - Manual Scan - toggle each pin" EOL
TAB "h - Write HIGH to a pin" EOL
TAB "l - Write LOW to a pin" EOL
TAB "p - Output using pull up/down (default)" EOL
TAB "w - Output using write low/high (less safe)" EOL
);
// clang-format on
break;
case 'a':
// clang-format off
stream->println(
TAB "r - Realtime readout of all pins" EOL
TAB "o - Read one pin once" EOL
);
// clang-format on
break;
}
}

View File

@@ -0,0 +1,185 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include "pinscan.h"
#if USE_WIFI
#include <WiFi.h>
#include <WiFiMulti.h>
static WiFiMulti wm;
static WiFiServer server(23);
static WiFiClient client;
#endif
Stream *stream = NULL;
uint8_t mode[] = {'?', '\0', '\0', '\0'};
pin_size_t pinSkip[2] = {255, 255};
int output = -1;
static void parseMode();
void setup() {
Serial.begin(115200);
#if USE_WIFI
wm.addAP(WIFI_SSID, WIFI_PASS);
while (wm.run() != WL_CONNECTED) {
Serial.println("WiFi connection failed, retrying in 5s");
delay(5000);
}
server.begin();
server.setNoDelay(true);
#else
stream = &Serial;
#if LT_UART_DEFAULT_SERIAL == 0
output = 0;
pinSkip[0] = PIN_SERIAL0_TX;
pinSkip[1] = PIN_SERIAL0_RX;
#elif LT_UART_DEFAULT_SERIAL == 1
output = 1;
pinSkip[0] = PIN_SERIAL1_TX;
pinSkip[1] = PIN_SERIAL1_RX;
#elif LT_UART_DEFAULT_SERIAL == 2
output = 2;
pinSkip[0] = PIN_SERIAL2_TX;
pinSkip[1] = PIN_SERIAL2_RX;
#endif
#endif
}
void loop() {
#if USE_WIFI
while (wm.run() != WL_CONNECTED) {
Serial.println("WiFi connection dropped, retrying in 5s");
delay(5000);
}
if (!stream) {
client = server.accept();
if (client) {
stream = &client;
printHelp();
}
} else if (!client.connected()) {
stream = NULL;
client.stop();
client = WiFiClient();
}
#endif
if (stream && stream->available()) {
// put the char in first free mode item
bool modeSet = false;
for (uint8_t i = 0; i < sizeof(mode) / sizeof(uint8_t); i++) {
if (mode[i] == '\0') {
mode[i] = stream->read();
// make the next mode show help screen
if (i < sizeof(mode) - 1) {
mode[++i] = '?';
}
// clear the following items, if any
while (i < sizeof(mode) - 1) {
mode[++i] = '\0';
}
modeSet = true;
break;
}
}
if (!modeSet) {
// no free mode items, reset everything
memset(mode, '\0', sizeof(mode));
while (stream->available()) {
stream->read();
}
}
// LT_I("New mode: %s", mode);
}
// check for any 'q' keypresses
for (uint8_t i = 0; i < sizeof(mode) / sizeof(uint8_t); i++) {
if (mode[i] == 'q') {
if (mode[0] == 'd')
digitalDetach();
memset(mode, '\0', sizeof(mode));
mode[0] = '?';
break;
}
}
if (stream)
parseMode();
}
static void parseMode() {
if (mode[0] == '\0')
return;
int serial = 0;
switch (mode[0]) {
case '?':
printHelp();
break;
case 'd':
runDigital();
return;
case 'a':
runAnalog();
return;
case 'r':
LT.restart();
while (1) {}
return;
#if !USE_WIFI
case 's':
stream->print("Choose output UART: ");
serial = stream->parseInt();
stream->println();
// clang-format off
#ifdef PIN_SERIAL0_TX
if (serial == 0) {
stream = &Serial0;
pinSkip[0] = PIN_SERIAL0_TX;
pinSkip[1] = PIN_SERIAL0_RX;
} else
#endif
#ifdef PIN_SERIAL1_TX
if (serial == 1) {
stream = &Serial1;
pinSkip[0] = PIN_SERIAL1_TX;
pinSkip[1] = PIN_SERIAL1_RX;
} else
#endif
#ifdef PIN_SERIAL2_TX
if (serial == 2) {
stream = &Serial2;
pinSkip[0] = PIN_SERIAL2_TX;
pinSkip[1] = PIN_SERIAL2_RX;
} else
#endif
// clang-format on
{
stream->print("Port unavailable");
break;
}
output = serial;
((SerialClass *)stream)->begin(115200);
stream->println();
mode[0] = '?';
return;
#endif
case 't':
ansi = !ansi;
stream->print("ANSI control codes ");
if (ansi) {
stream->println("enabled");
mode[0] = '?';
ansiSkipErase = true;
return;
} else {
stream->println("disabled");
}
break;
default:
break;
}
mode[0] = '\0';
}

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include <Arduino.h>
#define USE_WIFI 0 // set up a WiFi telnet server
#define DELAY_READ 100 // interval for realtime readouts
#define WIFI_SSID "MySSID"
#define WIFI_PASS "Secr3tPa$$w0rd"
#define EXAMPLE_VER "1.0"
#define EOL "\r\n"
#define TAB "\t"
#define ANSI_ERASE "\x1B[2J"
#define ANSI_HOME "\x1B[H"
#define ANSI_LINE_START "\x1B[0G"
#define ANSI_TWO_LINES "\x1B[2F"
#define ANSI_ERASE_LINE "\x1B[0K"
#define ANSI_BLUE "\x1B[0;34m"
#define ANSI_RED "\x1B[0;31m"
#define ANSI_RESET "\x1B[0m"
extern Stream *stream;
extern uint8_t mode[];
extern pin_size_t pinSkip[2];
extern int output;
extern bool ansi;
extern bool ansiSkipErase;
extern void printHelp(uint8_t mode = '\0');
extern void printAnsi(const char *str);
extern void printAnsiErase();
extern void line();
extern int inputPin();
extern void runAnalog();
extern void runDigital();
extern void digitalDetach();

View File

@@ -0,0 +1,37 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include "pinscan.h"
bool ansi = true;
bool ansiSkipErase = false;
void printAnsi(const char *str) {
if (ansi)
stream->print(str);
}
void printAnsiErase() {
if (!ansiSkipErase)
printAnsi(ANSI_ERASE ANSI_HOME);
ansiSkipErase = false;
}
void line() {
stream->print("--------");
if (output == -1) {
stream->print("TELNET");
} else {
stream->print(" UART");
stream->print(output);
stream->write(' ');
}
stream->print("--------");
stream->println();
}
int inputPin() {
stream->print("Enter pin number: ");
int pin = stream->parseInt();
stream->println();
return pin;
}

View File

@@ -6,7 +6,7 @@
"type": "git",
"url": "https://github.com/kuba2k2/platformio-libretuya"
},
"version": "0.8.0",
"version": "0.9.0",
"frameworks": {
"arduino": {
"title": "Generic Arduino framework",
@@ -120,6 +120,11 @@
"~1.100301.0"
]
},
"tool-ltchiptool": {
"type": "uploader",
"version": "https://github.com/libretuya/ltchiptool#v1.4.0",
"note": "This is used only for C/C++ code from ltchiptool."
},
"tool-openocd": {
"type": "uploader",
"optional": true,

View File

@@ -1,26 +1,48 @@
# Copyright (c) Kuba Szczodrzyński 2022-04-20.
import importlib
import json
import sys
from os import system
from os.path import dirname, join
from typing import Dict
from platformio import util
from platformio.debug.config.base import DebugConfigBase
from platformio.debug.exception import DebugInvalidOptionsError
from platformio.managers.platform import PlatformBase
from platformio.package.exception import MissingPackageManifestError
from platformio.package.manager.base import BasePackageManager
from platformio.package.meta import PackageItem, PackageSpec
from platformio.platform.base import PlatformBase
from platformio.platform.board import PlatformBoardConfig
from semantic_version import Version
# Make tools available
sys.path.insert(0, dirname(__file__))
from tools.util.platform import get_board_manifest
# Install & import tools
def check_ltchiptool():
global ltchiptool
import ltchiptool
importlib.reload(ltchiptool)
if Version(ltchiptool.get_version()) < Version("1.4.0"):
raise ImportError("Version too old")
try:
check_ltchiptool()
except (ImportError, AttributeError):
print("Installing/updating ltchiptool")
system(" ".join([sys.executable, "-m", "pip install -U ltchiptool"]))
try:
check_ltchiptool()
except (ImportError, AttributeError) as e:
print(
f"!!! Installing ltchiptool failed, or version outdated. Cannot continue: {e}"
)
raise e
# Remove current dir so it doesn't conflict with PIO
sys.path.remove(dirname(__file__))
if dirname(__file__) in sys.path:
sys.path.remove(dirname(__file__))
libretuya_packages = None
manifest_default = {"version": "0.0.0", "description": "", "keywords": []}
@@ -189,7 +211,7 @@ class LibretuyaPlatform(PlatformBase):
def update_board(self, board: PlatformBoardConfig):
if "_base" in board:
board._manifest = get_board_manifest(board._manifest)
board._manifest = ltchiptool.Board.get_data(board._manifest)
# add "arduino" framework
has_arduino = any("arduino" in fw for fw in board.manifest["frameworks"])

View File

@@ -129,4 +129,6 @@
#define THD_UMP3_PRIORITY 4
#define THD_WPAS_PRIORITY 5
#define THDD_KEY_SCAN_PRIORITY 7
#define UART1_USE_FIFO_REC 0
#define UART2_USE_FIFO_REC 0
#define WIFI_DEFAULT_BLE_REQUEST 1

View File

@@ -113,4 +113,6 @@
#define THD_UMP3_PRIORITY 4
#define THD_WPAS_PRIORITY 5
#define THDD_KEY_SCAN_PRIORITY 7
#define UART1_USE_FIFO_REC 0
#define UART2_USE_FIFO_REC 0
#define WIFI_DEFAULT_BLE_REQUEST 1

View File

@@ -7,6 +7,4 @@
// this is included only by wifi_simple_config.c
// which uses lwip_ntohl without parentheses
// so the #define from lwip/def.h doesn't work
#ifndef lwip_ntohl
#define lwip_ntohl lwip_htonl
#endif

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