Switched to Official SDK

This commit is contained in:
SSAnkur committed 2026-10-20 15:33:46 +06:00
1 parent 224c50821c
commit 5cb31298d6
8 files changed
+260 -142

No files matched your search

Binary file not shown.
+53 -14
View File
@@ -1,7 +1,9 @@
# LYWSD03MMC 2025 B1.5 (pvvx B1.1) BTHome v2 beacon + LCD + OTA
Port of the nRF52840/Zephyr SHT41 beacon to the Xiaomi LYWSD03MMC
(Telink TLSR8251). Same behaviour:
(Telink TLSR8251), built on the official Telink BLE SDK
([tc_ble_single_sdk](https://github.com/telink-semi/tc_ble_single_sdk)
V3.4.3.0). Same behaviour:
- every 60 s: measure temperature/humidity, read the battery right after the
sensor load, update the advertisement
@@ -12,8 +14,9 @@ Port of the nRF52840/Zephyr SHT41 beacon to the Xiaomi LYWSD03MMC
it in the scan response only, as pvvx does)
- a failed sensor read keeps advertising the last good T/RH; the voltage is
updated on every measurement
- Telink OTA service for over-the-air firmware updates, reachable only
during the first 2 minutes after the battery is inserted (see Flashing)
- Telink OTA service (the SDK's OTA server) for over-the-air firmware
updates, reachable only during the first 2 minutes after the battery is
inserted (see Flashing)
plus the display: temperature (0.1 °C, `°C`), humidity (`%`), battery icon
below 2.4 V, `-- --` until the first good reading. The LCD controller holds
@@ -60,19 +63,53 @@ pvvx/ATC_MiThermometer.
- The sensor is wired straight to the coin cell, so there is no power gating;
the SHTV3 is put to sleep explicitly after every measurement.
- Voltage is VDD (= CR2032), measured by driving the unbonded PB0 pad high
and sampling it with the ADC. Uses the typical 1175 mV reference, so expect
a few % absolute error; adjust `ADC_VREF_MV` against a meter if needed.
and sampling it with the ADC. The ADC reference is the chip's factory
calibration, which the SDK loads from the calibration sector or eFuse
(typical 1175 mV when there is none). To trim against a meter, set
`ADC_VREF_MV` to a fixed value in mV.
## Build
Builds on Linux x86-64 only (the SDK's post-build tool is a Linux x86-64
binary). Official Telink SDK, used unmodified, plus the TC32 toolchain:
```sh
git clone https://github.com/pvvx/ATC_MiThermometer ../ATC_MiThermometer
git clone -b release/V3.4.3.0 https://github.com/telink-semi/tc_ble_single_sdk ../tc_ble_single_sdk
# apply the official patches as tc_ble_single_sdk_v3.4.3.0_Patch/patch_*/Patch_Guide.txt
# says (copy each patch folder over tc_ble_single_sdk/, 0001 then 0002)
# TC32 toolchain (Linux): e.g. https://github.com/flyskywhy/tc32 branch "linux"
ln -s /path/to/tc32 ../ATC_MiThermometer/SDK/tools/linux/tc32
make
make TC32_PATH=/path/to/tc32/bin/
```
Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
`SDK_PATH` defaults to `../tc_ble_single_sdk/tc_ble_single_sdk`. The
makefile uses the same compiler flags, startup code
(`boot/B85/cstartup_825x.S`, `MCU_STARTUP_8251`: 32 KiB SRAM), linker
script (`project/tlsr_tc32/B85/boot.link`), libraries (`lt_825x`,
`lt_general_stack`) and post-build tool (`script/tl_check_fw/check_fw`)
as the SDK's own `825x_ble_sample` project. Because the SDK only
looks up `app_config.h` for its own demo projects, `src/app_config.h` is
force-included (`-include`) into every file, SDK sources included.
Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended) and prints
the memory use: about 54 KiB of image, 17.8 KiB of RAM code + retained data
(so 32 KiB deep-sleep retention, as before), 21 KiB of the 32 KiB SRAM.
### Changes from the pvvx-SDK build
- RF/clock bring-up, calibration loading
(`blc_app_loadCustomizedParameters_normal()` / `_deepRetn()`), retention
size selection and the OTA server API are the official ones.
- The power-on marker moved from `DEEP_ANA_REG0` to `DEEP_ANA_REG1`: the
official SDK reserves bit 1 of `DEEP_ANA_REG0` for its crystal start-up
failure flag.
- OTA: inactivity timeout 16 s (`OTA_PACKET_TIMEOUT_S`), whole upload at
most 1000 s (`OTA_PROCESS_TIMEOUT_S`; the SDK default would abort after
30 s); images up to 124 KiB (SDK limit). The OTA characteristic gained
NOTIFY and a CCC descriptor, as in the SDK sample, because the official
server answers the extended OTA protocol on it.
- Flash write protection is still cleared once per power-on (status
register := 0, as pvvx), now with the SDK's flash driver and only when a
protection bit is set.
## Flashing
@@ -81,7 +118,7 @@ Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
"Select Firmware" → custom file. Units running the previous build of this
firmware (no OTA) need one wired flash: USB-UART adapter on the SWS pad,
`TlsrComProg`/`ATCtelink.py` from the pvvx repo.
- **Updates** (this firmware → any Telink image ≤ 128 KiB, including back to
- **Updates** (this firmware → any Telink image ≤ 124 KiB, including back to
pvvx or stock), with https://pvvx.github.io/ATC_MiThermometer/TelinkOTA.html:
1. Take the battery out, wait ~5 s, put it back. The BLE symbol on the
LCD means the OTA window is open: for 2 minutes the device accepts
@@ -97,12 +134,14 @@ Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
The new image is written to the inactive flash bank and only activated
after its CRC checks out, so an interrupted upload leaves the current
firmware running. The transfer aborts after 16 s without data.
firmware running. The transfer aborts after 16 s without data or after
1000 s in total.
The window opens only on a real power cycle. Reboots without one
(low-battery sleep, OTA reboot, crash) keep it closed. After installing
this firmware by OTA from pvvx firmware, power-cycle once before the next
update.
(low-battery sleep, OTA reboot, crash) keep it closed. Exception: the
first boot after installing this build by OTA from other firmware (stock,
pvvx, or the earlier pvvx-SDK build of this firmware) opens the window
once, because the marker register was never set.
**OTA itself is unauthenticated**: anyone in Bluetooth range can upload
firmware while the window is open, so it is limited to 2 minutes after a
+67 -51
View File
@@ -1,87 +1,103 @@
# LYWSD03MMC B1.5 BTHome v2 beacon + LCD
# LYWSD03MMC B1.5 BTHome v2 beacon + LCD + OTA
#
# Builds against the Telink TLSR825x BLE SDK as shipped in
# pvvx/ATC_MiThermometer, reusing its startup code, linker script and
# post-build scripts:
# git clone https://github.com/pvvx/ATC_MiThermometer ../ATC_MiThermometer
# make ATC_PATH=../ATC_MiThermometer
# TC32 toolchain: $(TEL_PATH)/tools/linux/tc32/bin (or set TC32_PATH)
# Builds against the official Telink BLE SDK for TLSR825x (B85m):
# git clone -b release/V3.4.3.0 https://github.com/telink-semi/tc_ble_single_sdk ../tc_ble_single_sdk
# (apply the patches in tc_ble_single_sdk_v3.4.3.0_Patch/ as Patch_Guide.txt says)
# make TC32_PATH=/path/to/tc32/bin/
#
# Same compiler flags, startup code, linker script, libraries and post-build
# step as the SDK's own 825x_ble_sample project (project/tlsr_tc32/B85/.cproject).
# The SDK itself is used unmodified.
PROJECT_NAME ?= LYWSD03MMC-BTHome
ATC_PATH ?= ../ATC_MiThermometer
TEL_PATH ?= $(ATC_PATH)/SDK
TC32_PATH ?= $(TEL_PATH)/tools/linux/tc32/bin/
PYTHON ?= python3
SDK_PATH ?= ../tc_ble_single_sdk/tc_ble_single_sdk
TC32_PATH ?=
SRC_PATH := ./src
OUT_PATH := ./out
ATC_SRC := $(ATC_PATH)/src
ifeq ($(wildcard $(TEL_PATH)/components/drivers/8258/gpio_8258.c),)
$(error SDK not found: set ATC_PATH or TEL_PATH)
ifneq ($(MAKECMDGOALS),clean)
ifeq ($(wildcard $(SDK_PATH)/boot/B85/cstartup_825x.S),)
$(error Telink SDK not found: set SDK_PATH to the tc_ble_single_sdk/tc_ble_single_sdk directory)
endif
endif
CC := $(TC32_PATH)tc32-elf-gcc
LD := $(TC32_PATH)tc32-elf-ld
CC := $(TC32_PATH)tc32-elf-gcc
LD := $(TC32_PATH)tc32-elf-ld
OBJCOPY := $(TC32_PATH)tc32-elf-objcopy
NM := $(TC32_PATH)tc32-elf-nm
NM := $(TC32_PATH)tc32-elf-nm
CFLAGS := -DCHIP_TYPE=CHIP_TYPE_8258 -DMCU_STARTUP_825X \
# TLSR8251: 32 KiB SRAM (stack top 0x848000)
STARTUP := MCU_STARTUP_8251
INCLUDES := -I$(SDK_PATH) -I$(SDK_PATH)/vendor/common -I$(SDK_PATH)/common \
-I$(SDK_PATH)/drivers/B85 -I$(SRC_PATH)
# -include: the SDK selects app_config.h only for its own demo projects
# (vendor/common/user_config.h), so ours is forced into every file
CFLAGS := -DCHIP_TYPE=CHIP_TYPE_825x -D$(STARTUP)=1 \
-ffunction-sections -fdata-sections -Wall -O2 -fpack-struct \
-fshort-enums -finline-small-functions -std=gnu99 -funsigned-char \
-fshort-enums -finline-small-functions -std=gnu99 \
-fshort-wchar -fms-extensions \
-I$(TEL_PATH)/components -I$(SRC_PATH)
$(INCLUDES) -include $(SRC_PATH)/app_config.h
APP_SRCS := main.c ble.c i2c.c sensor.c battery.c lcd.c
DRV_SRCS := adc.c analog.c bsp.c clock.c flash.c gpio_8258.c i2c.c lpc.c rf_pa.c timer.c uart.c
COM_SRCS := string.c utility.c
ASFLAGS := -D$(STARTUP)=1 $(INCLUDES)
OBJS := \
$(OUT_PATH)/boot/cstartup_825x.o \
$(OUT_PATH)/boot/div_mod.o \
$(addprefix $(OUT_PATH)/app/,$(APP_SRCS:.c=.o)) \
$(addprefix $(OUT_PATH)/drv/,$(DRV_SRCS:.c=.o)) \
$(addprefix $(OUT_PATH)/common/,$(COM_SRCS:.c=.o)) \
$(OUT_PATH)/vendor/blt_common.o
APP_SRCS := $(wildcard $(SRC_PATH)/*.c)
# Everything the SDK's ble_sample compiles from the SDK (minus USB/audio
# code, which this device has no use for); --gc-sections keeps only what
# is referenced
SDK_DIRS := drivers/B85 drivers/B85/driver_ext drivers/B85/flash common vendor/common
SDK_SRCS := $(foreach d,$(SDK_DIRS),$(wildcard $(SDK_PATH)/$(d)/*.c))
SDK_ASMS := $(SDK_PATH)/boot/B85/cstartup_825x.S $(SDK_PATH)/common/div_mod.S
OBJS := $(patsubst $(SRC_PATH)/%.c,$(OUT_PATH)/app/%.o,$(APP_SRCS)) \
$(patsubst $(SDK_PATH)/%.c,$(OUT_PATH)/sdk/%.o,$(SDK_SRCS)) \
$(patsubst $(SDK_PATH)/%.S,$(OUT_PATH)/sdk/%.o,$(SDK_ASMS))
LDSCRIPT := $(SDK_PATH)/project/tlsr_tc32/B85/boot.link
LIBS := -L$(SDK_PATH)/proj_lib -llt_825x -llt_general_stack
ELF := $(OUT_PATH)/$(PROJECT_NAME).elf
BIN := $(PROJECT_NAME).bin
# Post-build: the SDK's check_fw (Linux x86-64 binary) pads the image, sets
# the size and marker in the header and appends the CRC the OTA server
# checks. Copied to out/ because the SDK checkout does not mark it executable.
CHECK_FW = cp $(SDK_PATH)/script/tl_check_fw/check_fw $(OUT_PATH)/check_fw && \
chmod +x $(OUT_PATH)/check_fw && $(OUT_PATH)/check_fw $(BIN)
all: $(BIN)
$(OUT_PATH)/app/%.o: $(SRC_PATH)/%.c $(wildcard $(SRC_PATH)/*.h)
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
$(OUT_PATH)/drv/%.o: $(TEL_PATH)/components/drivers/8258/%.c
$(OUT_PATH)/sdk/%.o: $(SDK_PATH)/%.c $(SRC_PATH)/app_config.h
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
@echo CC $<
@$(CC) $(CFLAGS) -c -o $@ $<
$(OUT_PATH)/common/%.o: $(TEL_PATH)/components/common/%.c
$(OUT_PATH)/sdk/%.o: $(SDK_PATH)/%.S
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
$(CC) $(ASFLAGS) -c -o $@ $<
$(OUT_PATH)/vendor/%.o: $(TEL_PATH)/components/vendor/common/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
$(OUT_PATH)/boot/%.o: $(ATC_SRC)/%.S
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
# Two-pass link: the second pass places code after the retention RAM area
$(ELF): $(OBJS)
$(LD) --gc-sections -L $(TEL_PATH)/components/proj_lib -T $(ATC_SRC)/boot.link \
-o $@ $(OBJS) -llt_8258
$(LD) --gc-sections -Ttext `$(PYTHON) $(ATC_SRC)/TlsrRetMemAddr.py -e $@ -t $(NM)` \
-L $(TEL_PATH)/components/proj_lib -T $(ATC_SRC)/boot.link \
-o $@ $(OBJS) -llt_8258
@$(PYTHON) $(ATC_SRC)/TlsrMemInfo.py -s 32768 -t $(NM) $@
$(LD) --gc-sections -T $(LDSCRIPT) -o $@ $(OBJS) $(LIBS)
@$(NM) $@ | awk ' \
function hex(s, i, n) { n = 0; for (i = 1; i <= length(s); i++) \
n = n * 16 + index("0123456789abcdef", substr(tolower(s), i, 1)) - 1; return n } \
$$3 == "_rstored_" { r = hex($$1) } \
$$3 == "_retention_size_" { t = hex($$1) } \
$$3 == "_bin_size_" { b = hex($$1) } \
$$3 == "_ram_use_end_" { e = hex($$1) - 8650752 } \
END { printf "RAM code %d B, retention (code + data) %d B -> %s deep retention, RAM used %d B of 32768, image %d B\n", \
r, t, (t < 16384 ? "16K" : "32K"), e, b }'
# Raw image + Telink CRC (required by the OTA bootloader)
$(BIN): $(ELF)
$(OBJCOPY) -O binary $< $@
$(PYTHON) $(ATC_PATH)/utils/tl_check_fw.py $@
$(CHECK_FW)
clean:
rm -rf $(OUT_PATH) $(BIN)
+37 -14
View File
@@ -11,16 +11,18 @@
* to measure VDD (= CR2032 voltage)
* PD7 LCD SPI-CLK
* PB7 LCD SPI-SDI
*
* Built against the official Telink tc_ble_single_sdk. The makefile
* force-includes this file into every translation unit (-include), SDK
* sources included, because the SDK's vendor/common/user_config.h only
* knows its own demo projects. That is how gpio_init() in the SDK sees the
* PB0 settings below.
*/
#pragma once
/* System clock. vendor/common/app_common.h derives SYS_CLK_TYPE and
* CLOCK_SYS_CLOCK_1S/1MS/1US from this; do not define them here. */
#define CLOCK_SYS_CLOCK_HZ 24000000
#define SYS_CLK_TYPE SYS_CLK_24M_Crystal
enum {
CLOCK_SYS_CLOCK_1S = CLOCK_SYS_CLOCK_HZ,
CLOCK_SYS_CLOCK_1MS = (CLOCK_SYS_CLOCK_1S / 1000),
CLOCK_SYS_CLOCK_1US = (CLOCK_SYS_CLOCK_1S / 1000000),
};
/* Low-power busy wait: CPU stalled, woken by timer0 */
#define pm_wait_us(t) cpu_stall_wakeup_by_timer0((t) * CLOCK_SYS_CLOCK_1US)
@@ -29,6 +31,19 @@ enum {
/* Variables that must survive deep-sleep retention */
#define RAM _attribute_data_retention_
/* ---- SDK feature switches ---- */
#define BLE_APP_PM_ENABLE 1
#define PM_DEEPSLEEP_RETENTION_ENABLE 1 /* also feeds the boot.link check */
#define BLE_APP_SECURITY_ENABLE 0 /* no pairing: OTA service only */
#define APP_FLASH_PROTECTION_ENABLE 0 /* see flash_clear_write_protection() */
#define UART_PRINT_DEBUG_ENABLE 0
#define MODULE_WATCHDOG_ENABLE 0
/* No Telink evaluation board: a value that matches none of the boards in
* vendor/common/boards, so default_config.h pulls in no board pin map */
#define BOARD_LYWSD03MMC_B15 0xB150
#define BOARD_SELECT BOARD_LYWSD03MMC_B15
/* ---- Application settings (equivalents of the Zephyr build) ---- */
#define DEV_NAME "LYWSD03MMC" /* CONFIG_BT_DEVICE_NAME */
#define ADV_INTERVAL 16000 /* 10000 ms / 0.625 ms */
@@ -52,12 +67,22 @@ enum {
#define OTA_WINDOW_S 120
#define OTA_WINDOW_ADV_INTERVAL 1600 /* 1 s while the window is open */
/* Power-on marker. DEEP_ANA_REG1 keeps its value through deep sleep and
* software reboots and is cleared only by a power cycle. (DEEP_ANA_REG0,
* used by the pvvx build, is reserved by the official SDK: its bit 1 is
* the crystal start-up failure flag.) */
#define POWER_ON_FLAG_REG DEEP_ANA_REG1
#define POWER_ON_FLAG 0x55
/* ---- Connection / OTA (values from pvvx ble.h / app.c) ---- */
#define CONN_INTERVAL 16 /* 16 * 1.25 ms = 20 ms */
#define CONN_LATENCY 49 /* (49 + 1) * 20 ms = 1 s between radio events */
#define CONN_TIMEOUT 400 /* x10 ms = 4 s */
#define OTA_IDLE_TIMEOUT_US (16 * 1000000) /* abort OTA after 16 s without data */
/* Official OTA server timeouts. The packet timeout is an inactivity
* timeout (1..20 s); the process timeout limits the whole upload
* (5..1000 s, SDK default only 30 s). */
#define OTA_PACKET_TIMEOUT_S 16 /* abort OTA after 16 s without data */
#define OTA_PROCESS_TIMEOUT_S 1000
/* ---- I2C ---- */
#define I2C_GROUP I2C_GPIO_GROUP_C2C3
@@ -69,7 +94,10 @@ enum {
/* ---- Battery ADC ---- */
#define GPIO_VBAT GPIO_PB0
#define ADC_VBAT_CHN B0P
#define ADC_VREF_MV 1175 /* typical internal 1.2 V ref, mV */
/* 0: use the chip's ADC reference calibration, loaded by the SDK from the
* calibration sector or eFuse (typical 1175 mV if there is none).
* Non-zero: fixed reference in mV, to trim against a meter. */
#define ADC_VREF_MV 0
/* PB0 (not bonded) is driven high permanently, as in pvvx: no toggling
* around each ADC read, and the pad never floats. Applied by gpio_init()
* on every wake-up, including from deep-sleep retention. */
@@ -89,9 +117,4 @@ enum {
#define GPIO_KEY GPIO_PA5
#define PULL_KEY PM_PIN_PULLUP_1M
#define MODULE_WATCHDOG_ENABLE 0
/* Stock Xiaomi flash layout: factory MAC address lives here */
#define CFG_ADR_MAC 0x76000
#include "vendor/common/default_config.h"
+17 -2
View File
@@ -3,7 +3,9 @@
* kept driven high (= VDD) permanently by gpio_init() (see app_config.h,
* same as pvvx) and sampled through the 1/8 prescaler against the
* 1.2 V reference. 8 samples are taken, sorted, and the middle 4 averaged.
* ADC sequence follows pvvx/ATC_MiThermometer battery.c.
* ADC sequence follows pvvx/ATC_MiThermometer battery.c; the conversion
* uses the per-chip reference calibration of the official SDK (as its
* adc_sample_and_get_result() does) unless ADC_VREF_MV fixes it.
*/
#include "tl_common.h"
#include "drivers.h"
@@ -12,6 +14,11 @@
#define ADC_SAMPLES 8
/* Set by user_calib_adc_vref() from blc_app_loadCustomizedParameters_normal()
* (drivers/B85/adc.c, retention RAM, default 1175 mV / 0 mV) */
extern unsigned short adc_gpio_calib_vref;
extern signed char adc_gpio_calib_vref_offset;
/* ADC analog config is not kept across deep-sleep retention: set it up each time */
static void vbat_adc_setup(void)
{
@@ -61,8 +68,16 @@ u16 battery_read_mv(void)
dfifo_disable_dfifo2();
adc_power_on_sar_adc(0);
/* mV = avg/8192 * Vref * 8 = sum4 * Vref / 4096 */
/* mV = avg/8192 * Vref * 8 + offset = sum4 * Vref / 4096 + offset */
u32 sum = s[2] + s[3] + s[4] + s[5];
if (!sum) {
return 0;
}
#if ADC_VREF_MV
return (u16)((sum * ADC_VREF_MV + 2048) >> 12);
#else
return (u16)((s32)((sum * adc_gpio_calib_vref + 2048) >> 12) +
adc_gpio_calib_vref_offset);
#endif
}
+51 -48
View File
@@ -7,23 +7,23 @@
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
*
* OTA itself (packet handling, CRC, flash writes, swapping the boot bank
* and rebooting) is done by the Telink BLE library: otaWrite()/otaRead().
* The new image is written to the bank that is not running (0x00000 or
* 0x20000, 128 KiB each), so a failed transfer leaves the old firmware
* bootable.
* and rebooting) is done by the OTA server of the official Telink BLE
* library (stack/ble/service/ota/ota_server.h, otaWrite()). It speaks the
* legacy Telink OTA protocol (CMD_OTA_START / data / CMD_OTA_END) used by
* TelinkOTA.html, plus the extended one. The new image is written to the
* bank that is not running (0x00000 or 0x20000, 128 KiB each; at most
* 124 KiB of firmware, the SDK default), so a failed transfer leaves the old
* firmware bootable.
*/
#include "tl_common.h"
#include "drivers.h"
#include "stack/ble/ble.h"
#include "stack/ble/service/ble_ll_ota.h"
#include "vendor/common/blt_common.h"
#include "app_config.h"
#include "ble.h"
#include "lcd.h"
void bls_set_advertise_prepare(void *p); /* in the library, no header */
/* Peripheral Preferred Connection Parameters (not in this SDK's headers) */
/* Peripheral Preferred Connection Parameters (not in the SDK headers;
* the SDK's ble_sample defines it locally too) */
typedef struct {
u16 intervalMin;
u16 intervalMax;
@@ -46,9 +46,6 @@ RAM static u32 window_start;
static void close_window(void);
RAM static u8 conn_param_accepted;
RAM static u8 mac_public[6];
static u8 mac_random_static[6];
/* Scan response: complete local name DEV_NAME */
#define NAME_LEN (sizeof(DEV_NAME) - 1)
RAM static u8 scan_rsp[2 + NAME_LEN];
@@ -74,6 +71,7 @@ enum {
OTA_PS_H, /* Telink OTA */
OTA_CD_H,
OTA_DP_H,
OTA_CCC_H,
OTA_DESC_H,
ATT_END_H,
@@ -120,27 +118,21 @@ static const u8 service_change_cd[5] = {
U16_LO(GATT_UUID_SERVICE_CHANGE), U16_HI(GATT_UUID_SERVICE_CHANGE)
};
/* Telink OTA */
static const u8 ota_service_uuid[16] = TELINK_OTA_UUID_SERVICE;
static const u8 ota_data_uuid[16] = TELINK_SPP_DATA_OTA;
static u8 ota_data;
/* Telink OTA, laid out as in the SDK's ble_sample: the official OTA
* server answers on the data characteristic itself (result and version
* responses of the extended protocol), hence NOTIFY and the CCC. */
static const u8 ota_service_uuid[16] = WRAPPING_BRACES(TELINK_OTA_UUID_SERVICE);
static const u8 ota_data_uuid[16] = WRAPPING_BRACES(TELINK_SPP_DATA_OTA);
RAM static u8 ota_data;
RAM static u8 ota_ccc[2];
static const u8 ota_name[] = { 'O', 'T', 'A' };
static const u8 ota_cd[19] = {
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP,
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP | CHAR_PROP_NOTIFY |
CHAR_PROP_WRITE,
U16_LO(OTA_DP_H), U16_HI(OTA_DP_H),
0x12, 0x2B, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
TELINK_SPP_DATA_OTA
};
/* Make the library's OTA timeout an inactivity timeout (pvvx otaWritePre):
* every OTA packet restarts it. */
static int ota_write_pre(void *p)
{
blt_ota_start_tick = clock_time() | 1;
return otaWrite(p);
}
/* In RAM, as pvvx: the stack walks this table from interrupt context,
* which must not depend on flash reads while OTA is erasing/writing it. */
RAM static attribute_t attributes[] = {
@@ -159,9 +151,10 @@ RAM static attribute_t attributes[] = {
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(service_change_val), (u8 *)&service_change_uuid, (u8 *)&service_change_val, 0, 0 },
{ 0, ATT_PERMISSIONS_RDWR, 2, sizeof(service_change_ccc), (u8 *)&ccc_uuid, (u8 *)&service_change_ccc, 0, 0 },
{ 4, ATT_PERMISSIONS_READ, 2, 16, (u8 *)&primary_service_uuid, (u8 *)ota_service_uuid, 0, 0 },
{ 5, ATT_PERMISSIONS_READ, 2, 16, (u8 *)&primary_service_uuid, (u8 *)ota_service_uuid, 0, 0 },
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_cd), (u8 *)&character_uuid, (u8 *)ota_cd, 0, 0 },
{ 0, ATT_PERMISSIONS_RDWR, 16, sizeof(ota_data), (u8 *)ota_data_uuid, &ota_data, &ota_write_pre, &otaRead },
{ 0, ATT_PERMISSIONS_RDWR, 16, sizeof(ota_data), (u8 *)ota_data_uuid, &ota_data, &otaWrite, 0 },
{ 0, ATT_PERMISSIONS_RDWR, 2, sizeof(ota_ccc), (u8 *)&ccc_uuid, ota_ccc, 0, 0 },
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_name), (u8 *)&user_desc_uuid, (u8 *)ota_name, 0, 0 },
};
@@ -174,7 +167,6 @@ static void ota_start_cb(void)
ota_active = 1;
conn_param_accepted = 0;
bls_pm_setManualLatency(0);
bls_ota_setTimeout(OTA_IDLE_TIMEOUT_US);
}
/* After a successful OTA the library reboots into the new image itself;
@@ -233,12 +225,17 @@ static void suspend_exit_cb(u8 e, u8 *p, int n)
void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
int open_window)
{
/* Factory public address (A4:C1:38:...) from the stock flash layout */
blc_initMacAddress(CFG_ADR_MAC, mac_public, mac_random_static);
u8 mac_public[6];
u8 mac_random_static[6];
/* Factory public address (A4:C1:38:...) from the stock flash layout:
* flash_sector_mac_address = 0x76000 on the 512 KiB flash */
blc_initMacAddress(flash_sector_mac_address, mac_public,
mac_random_static);
memcpy(&scan_rsp[2], DEV_NAME, NAME_LEN);
scan_rsp[0] = NAME_LEN + 1;
scan_rsp[1] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
scan_rsp[1] = DT_COMPLETE_LOCAL_NAME;
/* Controller */
blc_ll_initStandby_module(mac_public);
@@ -246,23 +243,26 @@ void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
blc_ll_initConnection_module();
blc_ll_initSlaveRole_module();
/* Host */
/* Host (GAP first, as the SDK requires) */
blc_gap_peripheral_init();
bls_att_setAttributeTable((u8 *)attributes);
blc_l2cap_register_handler(blc_l2cap_packet_receive);
bls_att_setAttributeTable((u8 *)attributes);
blc_smp_setSecurityLevel(No_Security);
/* OTA server. Its init also erases the inactive bank if it still
* holds an old image. */
blc_ota_initOtaServer_module();
blc_ota_setOtaProcessTimeout(OTA_PROCESS_TIMEOUT_S);
blc_ota_setOtaDataPacketTimeout(OTA_PACKET_TIMEOUT_S);
blc_ota_registerOtaStartCmdCb(ota_start_cb);
blc_ota_registerOtaResultIndicationCb(ota_result_cb);
rf_set_power_level_index(RF_TX_POWER);
bls_app_registerEventCallback(BLT_EV_FLAG_CONNECT, &connect_cb);
bls_app_registerEventCallback(BLT_EV_FLAG_TERMINATE, &disconnect_cb);
bls_app_registerEventCallback(BLT_EV_FLAG_SUSPEND_EXIT, &suspend_exit_cb);
blc_l2cap_registerConnUpdateRspCb(conn_param_rsp_cb);
/* OTA: erase the inactive bank if it still holds an old image */
bls_ota_clearNewFwDataArea();
bls_ota_registerStartCmdCb(ota_start_cb);
bls_ota_registerResultIndicateCb(ota_result_cb);
/* Advertising */
bls_ll_setAdvData((u8 *)adv, adv_len);
bls_ll_setScanRspData(scan_rsp, sizeof(scan_rsp));
@@ -281,19 +281,18 @@ void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
close_window();
}
/* Power management (pvvx init_ble) */
/* Power management. Thresholds as pvvx init_ble; early wake-up and
* retention size as the SDK's ble_sample: 16 KiB retention when code
* + retained data fit, else 32 KiB. */
blc_ll_initPowerManagement_module();
blc_app_setDeepsleepRetentionSramSize();
blc_pm_setDeepsleepRetentionThreshold(40, 18);
blc_pm_setDeepsleepRetentionEarlyWakeupTiming(240);
blc_pm_setDeepsleepRetentionType(DEEPSLEEP_MODE_RET_SRAM_LOW32K);
blc_pm_setDeepsleepRetentionEarlyWakeupTiming(270);
bls_pm_setSuspendMask(SUSPEND_ADV | DEEPSLEEP_RETENTION_ADV |
SUSPEND_CONN | DEEPSLEEP_RETENTION_CONN);
bls_pm_setManualLatency(0);
}
/* Called every main-loop pass. During OTA: answer every connection event
* (no slave latency) and never use deep-sleep retention, which would drop
* the transfer state. Otherwise allow the deepest sleep everywhere. */
/* Window over: non-connectable advertising at the normal interval. In
* this mode the radio does not listen after each packet. */
static void close_window(void)
@@ -306,6 +305,9 @@ static void close_window(void)
lcd_show_ble(0);
}
/* Called every main-loop pass. During OTA: answer every connection event
* (no slave latency) and never use deep-sleep retention, which would drop
* the transfer state. Otherwise allow the deepest sleep everywhere. */
_attribute_ram_code_
int ble_task(void)
{
@@ -336,5 +338,6 @@ void ble_init_deep_retention(void)
{
rf_set_power_level_index(RF_TX_POWER);
blc_ll_recoverDeepRetention();
bls_ota_registerStartCmdCb(ota_start_cb); /* as pvvx */
/* OTA server state and callbacks live in retention RAM in the
* official library: nothing to re-register */
}
-1
View File
@@ -25,7 +25,6 @@
*/
#include "tl_common.h"
#include "drivers.h"
#include "drivers/8258/gpio_8258.h"
#include "app_config.h"
#include "lcd.h"
+35 -12
View File
@@ -1,6 +1,7 @@
/*
* BTHome v2 temperature / humidity beacon with LCD and OTA - Xiaomi
* LYWSD03MMC B1.5 (Telink TLSR8251, Telink BLE SDK, no RTOS)
* LYWSD03MMC B1.5 (Telink TLSR8251, official Telink tc_ble_single_sdk,
* no RTOS)
*
* Every 60 s: measure T/RH (SHTV3) and VDD, update the segment LCD and the
* BTHome v2 advertisement sent every 10 s. For OTA_WINDOW_S after the
@@ -21,7 +22,6 @@
#include "tl_common.h"
#include "drivers.h"
#include "stack/ble/ble.h"
#include "vendor/common/blt_common.h"
#include "app_config.h"
#include "sensor.h"
#include "battery.h"
@@ -92,6 +92,22 @@ static void low_battery(u16 mv)
deep_sleep_s(LOW_VBAT_SLEEP_S);
}
/* The OTA server writes flash: clear block protection left by other
* firmware, once per power-on, as pvvx does. Same operation as the pvvx
* SDK's flash_unlock() (status register := 0), done with the official
* driver, but only when a protection bit is actually set. Works on any
* SPI flash, unlike the per-chip flash_prot.c, which only knows the
* flash types listed in the SDK. */
#define FLASH_SR_PROTECT_BITS 0xFC /* BP0..BP4, SRP0 */
static void flash_clear_write_protection(void)
{
if (flash_read_status(FLASH_READ_STATUS_CMD_LOWBYTE) &
FLASH_SR_PROTECT_BITS) {
flash_write_status(FLASH_TYPE_8BIT_STATUS, 0);
}
}
_attribute_ram_code_
static void store_result(s32 mc, s32 mrh)
{
@@ -173,6 +189,16 @@ static int adv_prepare_cb(void *p)
static void user_init_normal(void)
{
/* Must come before any BLE stack initialisation (official order) */
random_generator_init();
/* Flash size -> sector map (MAC 0x76000, calibration 0x77000 on the
* 512 KiB part), then the factory calibration from that sector:
* crystal trim, flash supply and ADC reference. Read-only, so it is
* safe before the battery check, which needs the ADC calibration. */
blc_readFlashSize_autoConfigCustomFlashSector();
blc_app_loadCustomizedParameters_normal();
blc_ll_initBasicMCU();
adc_power_on_sar_adc(0);
lpc_power_down();
@@ -194,10 +220,7 @@ static void user_init_normal(void)
low_battery(vbat_mv);
}
/* The library writes flash for OTA: clear write protection left by
* other firmware (as pvvx, once per power-on) */
flash_unlock();
random_generator_init();
flash_clear_write_protection();
#if ADV_NAME_IN_ADV
memcpy(&adv[18], DEV_NAME, NAME_LEN);
@@ -217,11 +240,10 @@ static void user_init_normal(void)
}
publish();
/* DEEP_ANA_REG0 survives deep sleep and software reboots and is
* cleared only by a power cycle (pvvx uses it the same way) */
int power_on = analog_read(DEEP_ANA_REG0) != 0x55;
/* Cleared only by a power cycle, see app_config.h */
int power_on = analog_read(POWER_ON_FLAG_REG) != POWER_ON_FLAG;
analog_write(DEEP_ANA_REG0, 0x55);
analog_write(POWER_ON_FLAG_REG, POWER_ON_FLAG);
bls_pm_registerAppWakeupLowPowerCb(measure_done_cb);
ble_init(adv, sizeof(adv), adv_prepare_cb, power_on);
@@ -231,6 +253,7 @@ static void user_init_normal(void)
_attribute_ram_code_
static void user_init_deepRetn(void)
{
blc_app_loadCustomizedParameters_deepRetn(); /* crystal trim, VDD_F */
blc_ll_initBasicMCU();
ble_init_deep_retention();
}
@@ -280,11 +303,11 @@ int main(void)
int deep_ret = pm_is_MCU_deepRetentionWakeup();
/* Same order as the SDK's ble_sample */
rf_drv_ble_init();
gpio_init(!deep_ret);
clock_init(SYS_CLK_TYPE);
rf_drv_init(RF_MODE_BLE_1M);
reg_clk_en0 = FLD_CLK0_SWIRE_EN; /* gate unused peripheral clocks */
blc_app_loadCustomizedParameters(); /* factory crystal trim */
if (deep_ret) {
user_init_deepRetn();