sidoh / esp8266_milight_hub

Replacement for a Milight/LimitlessLED hub hosted on an ESP8266
MIT License
935 stars 220 forks source link

Experiment from bulb-originated traffic from RGB+CCT bulbs #91

Open sidoh opened 7 years ago

sidoh commented 7 years ago

It's possible this could be used to discover roughly how many bulbs are paired with a given ID/group, and detect when bulbs are turned on.

More details here.

khmann commented 6 years ago

the implementation of "dance mode" makes clear... Mi.Light is a child toy, not a professional lighting product.

fut105-dance.txt.gz I captured these packets from a pair of FUT105 on 2473MHz, Syncword 147a258b. This is a pretty full "normal" exercise of the bulbs, no weird power cycle or attempts to disrupt operation. I realized later my capture screwed up and filter CRC, but I didn't want to revisit it.

The inconsistency between the ID in these packets and the output of decode_packet.py of the FUT089 log I posted yesterday (B8 issue) make me suspect your constant is still wrong

sidoh commented 6 years ago

the implementation of "dance mode" makes clear... Mi.Light is a child toy, not a professional lighting product.

I'd question the judgment of anyone who bought milight bulbs expecting to get a "professional lighting product." :)

EDIT - this sounded more brusque than I meant for it to. I totally agree that the modes are really silly. I initially didn't support modes because I didn't think people would use them, but eventually people asked for support.

The inconsistency between the ID in these packets and the output of decode_packet.py of the FUT089 log I posted yesterday (B8 issue) make me suspect your constant is still wrong

Right, I've noticed the same thing (see my notes in this comment from July).

There are other signals that something is off too. For example, there's a random +2 in the encoding for the checksum byte that isn't present for other bytes.

But at this point I don't think there's any utility in having the "right" ID. Packets are reliably decoded, and changing the parameters would be a breaking change without some really ugly code. In fact, it might be the case that two IDs aren't distinguishable as long as (id1 & 0x7F7F) == (id2 & 0x7F7F) holds because of the way the "encryption" key is generated.

EDIT - I should acknowledge that this is a good catch, and that I could be wrong about the having the right ID not mattering.

khmann commented 6 years ago

Ah, theres a problem with the high bits but that doesn't usually matter, and it's only some bytes. Maybe it's not even a problem with your code but an artifact of theirs. I'll hold off any further speculation until I'm able to wrap my head around the code.

Hey, if you read the marketing, Mi.Light bulbs have native WiFi support and capable of 16 million colors, and the dance modes are awesome and a pleasure to look at. ...the fact that they never considered saturation yet spent countless development on a dynamic RF synchronization protocol.

It really is bizarrely complex - change to a separate frequency, then bulbs seem to dynamically select a "master node" and each time there is a significant topology change, some sort of a counter is incremented... All to synchronize terrible patterns. Their higher-end DMX products claim to support user-definable patterns; maybe that will trickle down someday. My 6W RGBW bulbs support 16 modes (10-16 are the same as the first - rainbow color cycle). I say "support" because they save unique brightness settings to EEPROM and restore when you activate same as the supported modes.

sidoh commented 6 years ago

Haha, yeah! I was petty surprised to see as much depth as I did. Seems like a weird place to put this much effort into.

I had a potentially practical hack in mind for this: seems possible that you could exploit this to discover how many bulbs in a group are turned on.

Sounds like you've dug into the bulb traffic a lot more than I have. Does this sound plausible given what you know?

khmann commented 6 years ago

It's certainly possible to tell if "something" is there... RGBW bulbs default to speed "2" generally a 7 second cycle. Speed 1 was about 5 seconds, speed 0 3.5, but at speed 0 the bulbs only transmitted their sync packet every OTHER cycle. So... 5-7 seconds best case before you could get a response.

Wether it is possible to "count" the bulbs, I'm not sure if ALL the bulbs transmit or not - it certainly seemed like they were "electing" a leader and I was only getting packets from it, though I might not have been capturing all the transmissions or counting "redundant" packets- my RX code has gotten a lot better since then, I'll give it a try with 6 RGBW bulbs when they arrive (I think they'll beat my 6 RGB-CCTs on the slow boat...)

I would love to find a way to abuse this mode... maybe a "ping/site survey mode" to diagnose signal strength or choose the best channel? You could (statistically) optimize retries or something, sample the nRF "carrier detect" bit while you waited...

I've stopped frequency hopping; the bulbs hop plenty fast that it just doesn't make sense to stop transmitting and re-initialize the radio. Also in my country the high channels are slightly outside the regulated spectrum and so free from interference.

My experience with RGBW/CCT bulbs is that the sync packets are very low power. I wonder if this changes with the new lights advertised feature of "extend the range infinite!!!" ;)

EDIT: what we need is an SPI trance EDIT EDIT: I say that because (you might not know) "v4" RGBW strip controllers transmit in much the same way as dance mode - acting "repeaters" - any valid command to any address is rebroadcast as an RGB frequency and sync-word transmission.

sidoh commented 6 years ago

My thought was to use this to ensure that bulbs were off, but it'd also be cool to have some kind of discovery/topology mode.

I didn't really look into it too much, and I was only looking at the packets sent with the same radio configs as the FUT092 protocol. But it did seem like each bulb was sending a sync packet. When three bulbs were on, I'd get three packets each interval. When two were on, I'd get two.

Fixing the transmitter to a particular channel is a pretty interesting idea.

The logic analyzer I have is really crappy, and I don't have the right kind of probes to attach to the tiny SMD LT8900. Would definitely be interesting to see, though.

khmann commented 6 years ago

I highly recommend "TX_REUSE", though I've not implemented it on your codebase. I do this when I want to send a packet:

reschedule timer interrupt +4ms (much larger values seem fine) drop CE put packet in TX fifo set TX_REUSE set CE

do other stuff. when the interrupt fires, it just drops CE and stops the timer. Packets are transmitted continuously in hardware until then. So then my "retry configuration" becomes just a usleep() between packets put in the fifo, the radio does the rest at a full 1mbps ;)

Here's an idea, in a large "professional" space, use multiple NodeMCUs each tuned to a different (Lo/Mid/Hi) channel group (no collisions) and set the bridges to receive multicast packets. Extend the range infinite!!!

sidoh commented 6 years ago

I'm all for optimizing the radio, but I'm using a library for the nRF (RF24). It'd probably need to be a pretty drastic improvement to justify re-implementing that in this codebase. Maybe a good suggestion for them if they're not already doing it.

re: multiple MCUs, I think perhaps we're optimizing for different things. My goal is to create a relatively easy-to-setup device that has lots of integration points and performs well for what I perceive to be the most common use cases. If it becomes clear that people need a device that's optimized for packet throughput, I might reconsider, or just refer them to your work. =)

khmann commented 6 years ago

I've worked with that library on Raspberry Pi, the radio stuff is not good; it spends way too much time trying to accurately emulate a PL1167 (all the silly nibble shifting, over and over) instead of actually sending packets, so I would consider you would probably have 100% improvement (given your CPU, mine is slower and I saw like 300%). Consider more faster retransmits = better reliability for zero dollars. Or same reliability faster, your choice.

Are you based directly on henryk's github or some other? Henryk got it working but then lost interest, and I'm more eager to support an active project.

Thats the main reason I'm "here", yours seems to be the most active implementation out there. My problem is it's "too thin", I really want macros and basic state tracking in the milight gateway without needing some giant Domoticz install. And also "too fat", I don't need another box when I already have OpenWRTs everywhere.

I'd be interested to help get your code working on Raspberry Pi because that's a pretty easy entry point for a lot of people. I'm hoping to find a way to use you for MiLight gateway functions but leverage my own mess as a (distributed) radio interface.

sidoh commented 6 years ago

I'm referring to the nRF library that manages the raw SPI communication with the device:

https://github.com/nRF24/RF24

But yes, the layer on top of the NRF library is based on Henryk's code. I made changes to support different packet types and configs (syncwords/frequencies). I didn't really make an attempt to optimize it because I've not had a reason to. I've used this thing in my home setup for close to six months now, and I have literally not once noticed a bulb missing a command.

If it's this code you're talking about optimizing, I'm certainly open to that. But it's probably not something I'm going to drive unless it's a limitation for a lot of people.

I also have a radio impl for the LT8900 from a contributor which performs better. My understanding is that the LT8900 basically is the PL1167. This code for this is basically as you describe -- put data in a register and toggle some pins. No fiddling with checksums and whatnot.

The feature I have planned for v1.6 actually is state tracking. It's a work in progress, but it's coming along. Basically I need to add a mechanism to flush states to flash, and tie in the state tracking framework to the packet received hooks.

Anyway, let's maybe take discussion about these two things (radio optimization and state tracking) to a different ticket. I'll probably forget where this conversation happened otherwise :)