Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae2f65b9e | ||
|
|
d02197474e | ||
|
|
41b37e9c24 | ||
|
|
b03400fac2 | ||
|
|
3e808f7b6b | ||
|
|
1e3a8971fb | ||
|
|
d8c0105b97 | ||
|
|
b3689cbac8 | ||
|
|
1b5d6472f7 | ||
|
|
e9511c507a | ||
|
|
365f64ded5 | ||
|
|
3601fa63d8 | ||
|
|
5d00ddf516 | ||
|
|
0051453cad | ||
|
|
7920ea2dda | ||
|
|
9416e45a75 | ||
|
|
98a65c81af | ||
|
|
8e1f06e79b | ||
|
|
58a09f453d | ||
|
|
7dc69982b6 | ||
|
|
46e4041ed8 | ||
|
|
0e129130b1 | ||
|
|
33c9868f90 |
@@ -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 | ❌ | ❌
|
||||
|
||||
|
||||
53
SUMMARY.md
53
SUMMARY.md
@@ -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)
|
||||
|
||||
6
TODO.md
6
TODO.md
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ bool startMainTask() {
|
||||
&mainThread,
|
||||
THD_APPLICATION_PRIORITY,
|
||||
"main",
|
||||
(beken_thread_function_t)main_task,
|
||||
(beken_thread_function_t)mainTask,
|
||||
8192,
|
||||
NULL
|
||||
);
|
||||
|
||||
76
arduino/beken-72xx/cores/arduino/wiring_irq.c
Normal file
76
arduino/beken-72xx/cores/arduino/wiring_irq.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
/**
|
||||
|
||||
@@ -95,3 +95,7 @@
|
||||
#ifndef LT_DEBUG_SSL
|
||||
#define LT_DEBUG_SSL 0
|
||||
#endif
|
||||
|
||||
#ifndef LT_DEBUG_OTA
|
||||
#define LT_DEBUG_OTA 0
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
102
boards/_base/pcb/lsc-lma35.json
Normal file
102
boards/_base/pcb/lsc-lma35.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
29
boards/lsc-lma35.json
Normal 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."
|
||||
]
|
||||
}
|
||||
}
|
||||
89
boards/lsc-lma35/README.md
Normal file
89
boards/lsc-lma35/README.md
Normal 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
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
325
boards/lsc-lma35/pinout_lsc-lma35.svg
Normal file
325
boards/lsc-lma35/pinout_lsc-lma35.svg
Normal 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 |
1
boards/lsc-lma35/pins_arduino.h
Normal file
1
boards/lsc-lma35/pins_arduino.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "variant.h"
|
||||
42
boards/lsc-lma35/variant.cpp
Normal file
42
boards/lsc-lma35/variant.cpp
Normal 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"
|
||||
37
boards/lsc-lma35/variant.h
Normal file
37
boards/lsc-lma35/variant.h
Normal 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
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')}",
|
||||
|
||||
@@ -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
40
docs/cloudcutter.md
Normal 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.
|
||||
@@ -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.
|
||||
38
docs/getting-started/README.md
Normal file
38
docs/getting-started/README.md
Normal 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).
|
||||
6
docs/getting-started/uploading.md
Normal file
6
docs/getting-started/uploading.md
Normal 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)
|
||||
@@ -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))
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
96
docs/projects/esphome.md
Normal 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).
|
||||
@@ -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
|
||||
@@ -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"
|
||||
%}
|
||||
@@ -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"
|
||||
32
docs/status/supported_boards.md
Normal file
32
docs/status/supported_boards.md
Normal 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`
|
||||
@@ -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`
|
||||
@@ -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
1
examples/PinScan/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.pio
|
||||
116
examples/PinScan/README.md
Normal file
116
examples/PinScan/README.md
Normal 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.
|
||||
12
examples/PinScan/platformio.ini
Normal file
12
examples/PinScan/platformio.ini
Normal 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
|
||||
70
examples/PinScan/src/analog.cpp
Normal file
70
examples/PinScan/src/analog.cpp
Normal 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';
|
||||
}
|
||||
210
examples/PinScan/src/digital.cpp
Normal file
210
examples/PinScan/src/digital.cpp
Normal 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';
|
||||
}
|
||||
67
examples/PinScan/src/help.cpp
Normal file
67
examples/PinScan/src/help.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
185
examples/PinScan/src/pinscan.cpp
Normal file
185
examples/PinScan/src/pinscan.cpp
Normal 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';
|
||||
}
|
||||
39
examples/PinScan/src/pinscan.h
Normal file
39
examples/PinScan/src/pinscan.h
Normal 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();
|
||||
37
examples/PinScan/src/ui.cpp
Normal file
37
examples/PinScan/src/ui.cpp
Normal 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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
36
platform.py
36
platform.py
@@ -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"])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user