[lightning-ln882h] Fix duplicate MAC addresses on LN882H (fixes #338)#381
[lightning-ln882h] Fix duplicate MAC addresses on LN882H (fixes #338)#381Bl00d-B0b wants to merge 1 commit into
Conversation
502e397 to
c10548a
Compare
|
Real-world validation — migration from Tested on DS-101JL (BSEED) dual gang wall switches with LN882H (ln-cb3s-v1.0). Previously these devices used a manual After flashing firmware with this fix applied:
Removing the This means the fix handles all cases correctly:
In cases 2 and 3 the resolved MAC is persisted to sysparam KV at |
|
See my comments here: TL;DR: Generating a MAC address randomly is a workaround. A real solution would be to adjust the partition layout, or - as mentioned in the linked thread - create a separate board definition for Tuya devices. |
c10548a to
5dfd201
Compare
Flash dump analysis — MAC recovery across all use casesI analyzed 5 unique 2 MB flash dumps from Tuya LN882H devices (plus verified binary details of the OTA write path in LibreTiny itself) to confirm the approach and document why no partition changes are needed. Devices analyzed
BLK_V1.0 KV structure — confirmed across all devicesEvery device has the same
Important: BLK_V1.0 keys are not null-terminated. The format is: A parser must use known key lengths, not null scanning. The unique MAC is factory-assigned, not provisioning-assigned. D3 and D4 were never connected to a Tuya account (no Provisioned devices additionally accumulate Why the Tuya KV at 0x1C5000 is preserved — no partition changes neededThe Tuya KV occupies OTA (Kickstart / cloudcutter / LibreTiny OTA update)
uint32_t offset = part->offset + block->addr; // physical = OTA_base + UF2_addr
flash->ops.erase(offset, block->len); // only this sector is erasedThe current LibreTiny firmware for LN882H contains UF2 blocks with addresses UART full-flash ( The UART flash image is ~548 KB and covers physical addresses Conclusion: The Tuya KV is naturally preserved under both flash methods as long as the firmware stays below ~592 KB (~66 KB of headroom remaining). Shrinking the OTA partition to protect this region is not recommended — the trade-off (680 KB → 584 KB, losing 96 KB of firmware capacity) is not worth it for MAC address preservation alone when a software solution covers all realistic flash scenarios. Three use cases — MAC resolution behaviour
In all cases the MAC is persisted to sysparam KV at |
50448cf to
bfbfce3
Compare
c257f4f to
4fa56fd
Compare
|
It's a good thing that the MAC address is not erased during OTA updates - yet. If someone builds firmware that's larger than that 0x92000, then it definitely will get erased. Tuya designed such a partition layout where 0x1C5000 was used for KV. But they also use OTA.
That doesn't quite make sense. |
Design reflection: approach alignment and partition trade-offOn the Tuya KV reading approachThe current PR satisfies the stated preference from #338 and the PR comment above: step 2 reads the factory-assigned MAC directly from the Tuya BLK_V1.0 KV store. The TRNG (step 3) is a fallback only for the edge case where the KV region is erased, which happens exclusively with a full-chip erase UART flash. Under all normal flash paths (standard UART flash, Kickstart, cloudcutter OTA) the Tuya KV is intact and the factory MAC is restored without any random generation. It is also worth noting that users migrating from a On repartitioningAdjusting the partition layout to protect There is also a practical deployment problem: the partition table sits at Additionally, even with repartitioning there is no guarantee that future Tuya firmware releases will keep the MAC address at the same offset within the KV region. The BLK_V1.0 layout and the RecommendationGiven the above, I would suggest following the same KISS approach already used by OpenBeken for LN882H: sysparam fast path → TRNG. Their implementation in Summary
The static IP fix ( |
|
I'm currently short on time, so I can't really look deeper into this issue - perhaps I'll be able to in a few days from now. |
|
Hi @kuba2k2, I appreciate the feedback and want to share where I stand, because I don't fully agree with the direction the discussion has taken. For context: my original implementation used exactly the TRNG approach — check sysparam sentinel, generate via hardware TRNG, persist. I extended it to include Tuya BLK_V1.0 KV reading in response to the review feedback. After going through the full analysis I'm now coming back to the original approach, and here is why. On repartitioning — not a viable option. The partition table lives at On a separate Tuya board definition — this is the same hardware regardless of original firmware. A board split adds maintenance overhead without solving the root problem. TRNG is the right solution. OpenBeken uses exactly this approach and it works. The MAC is generated once on first boot, persisted to sysparam KV at The detailed BLK_V1.0 flash analysis already in this PR can live as documentation: we know exactly where the original factory MAC sits ( Wider context — full LN882H platform effort This PR is part of a set of changes that together make LN882H a fully functional LibreTiny platform:
Everything is tested and working from my side. I'm ready to follow the review process properly. What I need help with Code ownership on ESPHome — Tests and reviewers — ESPHome PR esphome/esphome#16691 has a On a broader note — BLE support is a genuinely significant addition to LibreTiny. LN882H has a full BLE 5.1 stack built into its SDK and it works well. Once this lands, the same approach could be extended to the BK7231N/T chipset which also has BLE hardware. Having passive BLE scanning and BTHome sensor support on these cheap widely-deployed chips opens up a lot of home automation use cases that weren't possible before. I think it's worth getting right. Thanks for the project and for your time. |
|
First, "repartitioning" in this case doesn't mean modifying the partition table at 0x6000. I don't know what that table defines, but LibreTiny doesn't use it - instead it has its own, defined in board JSON files. Second - separate Tuya board definitions are already going to be implemented for BK7238 and perhaps even BK7231N. I know it's "just" the firmware and hardware stays the same (though even that's not always the case). Truth is, big vendors like Tuya use custom partitioning, sometimes custom encryption keys (like on Beken), modified bootloaders (which require specific support), and have their own KV storage in the flash (which should be avoided by LT to keep its original contents). That's all defined by a specific board JSON - and since virtually all boards use base JSON files as well, there's minimal maintenance overhead anyway. And it makes the board list way less ambiguous. An lastly - what OpenBeken does is not considered the golden standard of what we should do. I've seen their code and odd solutions to simple problems way too many times, and many of them just aren't good enough to be used in LibreTiny. Take BK7238 support for example - instead of providing proper OTA support on Tuya bootloaders, they just overwrite it with a OEM bootloader, which leaves devices with mismatched bootloaders and calibration partition layouts, and people migrating from OBK to LT are having issues due to that. I know there are more PRs related to LN882H support. It's a lot to review, and I have limited free time. Also note that I write these comments and review code without using AI agents. As for the code owners, you should probably assign yourself - that's usually what people do. The one who creates code is the owner - seems simple. I definitely wouldn't be a good fit, since I haven't ever flashed a single LN882H device yet. |
f9bdfea to
a61f94f
Compare
3178939 to
e746402
Compare
e746402 to
3ec72c5
Compare
|
Hi @kuba2k2, This PR has been updated to implement the board JSON approach you suggested. Here is a summary of what was done. Board JSON (your requested architecture)
Non-Tuya boards (
The WiFi STA MAC resolution now uses
The factory sentinel BLE MAC (related work) The same pattern extends to BLE MAC recovery in ESPHome PR esphome/esphome#16691. Tuya LN882H devices store a factory-assigned BLE MAC in the same BLK_V1.0 KV region under key Confirmed from flash dumps: Thanks again for guiding the architecture — the board JSON approach is clearly the right design. |
Fixes #338
Problem
Every LN882H device ships with the same factory-default MAC address (
00:50:C2:5E:10:88) stored in KV flash.sysparam_integrity_check_all()(called duringlt_init_family()) writes this placeholder on first boot and never replaces it. Every device on the same network therefore shares one MAC, causing ARP conflicts and connection failures.Fix
Add
lt_init_unique_mac()called immediately aftersysparam_integrity_check_all()inlt_init.c. It resolves the MAC address in priority order — once, on first boot after flash. Every subsequent boot returns in step 1 immediately.Priority order
Step 1 — sysparam KV already unique
Read the stored STA MAC via
sysparam_sta_mac_get(). If it differs from the factory sentinel, return immediately. This is the fast path on every boot after the first, and fully covers the LibreTiny→LibreTiny OTA case: the sysparam KV lives at0x1E0000, which is outside the OTA partition and is never erased by OTA.Step 2 — Restore Tuya factory MAC from BLK_V1.0 KV
Tuya assigns every LN882H a unique MAC at the factory and writes it as a second
6_sta_macentry in a BLK_V1.0 append-log KV store at flash offset0x1C5000. Scan for the last valid non-sentinel entry and restore it to sysparam KV.This region lies within the OTA partition (
0x133000–0x1DD000), but no partition layout changes are needed: OTA writes are block-addressed and the current firmware (~530 KB) ends at ~0x1B4700, well short of0x1C5000. UART full-flash similarly only covers0x000000–0x088000. The Tuya KV is naturally preserved under both flash methods.Step 3 — TRNG fallback
If the Tuya KV is absent or erased (e.g. UART flash with full-chip erase option), generate a unique address via
ln_generate_random_mac()(hardware TRNG, already compiled in vialn882h_net). This mirrors the approach used in OpenBeken.In all cases the SoftAP MAC is derived by setting the locally-administered bit (
mac[0] |= 0x02) on the STA MAC.Analysis
This fix is based on binary examination of 5 unique Tuya LN882H flash dumps (4× DS-101JL wall switch + 1× GU10 bulb from issue #338), including devices in three states: never provisioned, provisioned via Tuya app, and provisioned and in use for weeks.
Key findings:
6_sta_macentry0x1C5000–0x1C9000, 4 blocks × 4 KB) has an identical layout across all examined devices: sentinel at0x1C51EB, unique MAC at0x1C5423it_magic(8) + crc16(2) + val_size_u16_le(2) + prev_ptr_u32_le(4) + key_raw(N) + value_rawuf2_write) usesoffset = part->offset + block->addr— it only erases sectors for UF2 blocks actually present in the image; no block targets0x1C5000Detailed analysis is posted as a comment on this PR.
Safety
lt_tuya_kv_read_sta_mac()is read-only; it never writes to flashln_generate_random_mac()is not thread-safe but is called before the RTOS scheduler startsmemcmpagainst{0x00,0x50,0xC2,0x5E,0x10,0x88}) ensures existing devices with a persisted unique MAC in sysparam KV are never affected