Now that a communication with the STM32 has been established, it’s time to start checking out the data exchange a bit more. From Jaro’s notes (see references in part 1), many of the most important IDs are known already. However, I suspect that there’s more to that. For example, it appears that the app can display the filtered air volume rate, which means that either the value itself or some kind of motor power or RPM value should be available. Looking at the ID scheme also brings to mind that there are a lot of spaces that can be filled between the known commands.
I assume that the first ID kind of clusters alike parameters, e.g. “3” for sensor values or “2” system operation. The second one specifies the sub-parameter within each set.
So, let’s poke in the dark for a bit. I have modified my ESP program to be able to probe values from 1 to 20 for both IDs automatically and check the responses. If a parameter is unavailable, the STM gracefully responds with -4001, which makes identification of valid returns easy. Otherwise, it returns a somewhat sensible value. It appears that both IDs must be greater than zero. These are the ID sets, which were responded to:
- 1x not used
- 2x (Operation)
- 2:1 = 0 (?)
- 2:2 = false (Device on/off)
- 2:4 = 1 (Power level)
- 2:5 = 0 (Regulation mode)
- 3x (Sensors)
- 3:6 = Air quality uG/m3 (int)
- 3:7 = Humidity % (int)
- 3:8 = Temperature °C (float)
- 4x (Filter)
- 4:3 = 97 Filter life in percent (int)
- 4:5 = 79 (???)
- 5x (Beeper)
- 5:1 = false (Beeper on/off)
- 6x (Display?)
- 6:1 = 0 (Brightness 0-2)
- 6:6 = true
- 7x (Lock)
- 7:1 = false (Parental control)
- 8x not used
- 9x
- 9:1 = 3500
- 9:2 = 79
- 10x (Fan control)
- 10:1 = 2150
- 10:2 = 1900
- 10:3 = 1600
- 10:4 = 1300
- 10:5 = 770
- 10:6 = 390
- 10:7 = 2150
- 10:8 = 0
- 10:9 = 0
- 10:10 = 14 (Power used in manual speed?)
- 11x not used
- 12x
- 12:1 = 0 284700
- 13x (Maybe system status?)
- 13:1 = 2860, 2878 also seen
- 13:2 = 3
- 13:3 = 949
- 13:4 = “35,75,115,150,250”
- 13:5 = “waiting”
- 13:6 = 75
- 13:7 = 2
- 13:8 = 0
- 13:9 = 0 (Written to by “magic heartbeat”, seems to be a counter?)
- 14x (Networking)
- 14:1 = “xx:xx:xx:xx:xx:xx:x” (MAC)
- 14:2 = “xx:xx:xx:xx”
- 14:3 = “xx:xx:xx:xx”
- 14:4 = “xx:xx:xx:xx”
- 14:5 = “xx:xx:xx:xx”
- 15x
- 15:1 = 0
- 15:2 = 44
- 15:3 = 0
- 15:4 = “0000000000000000”
- 15:5 = false
- 15:6 = 0
- 15:8 = 0
- 15:9 = 2
- 15:10 = 3 (suspicion by Jaro: Power used in speed modes?)
- 15:11 = 44
- 16x…20x not used
Now, let’s solve some of these unknowns, in particular concerning the values marked in red. The green values indicate known registers.
First mystery: There appears to be a table of numbers in block 10 which are about the right range for RPM values. In addition, Jaro has designated 10:10 as a power value for manual speed setting. Turns out, that the fan is not controlled by PWM (which means pulse duration controls speed) but by frequency (PFM, pulse repetition frequency controls speed). The high/low ratio in each cycle is always 50%. This makes some sense: The motor is made by Nidec, who produce mostly synchronizable motors for precision applications like polygon mirror drives. Frequencies I have measured at this pin are 192 Hz (Speed 1), 400 Hz (Speed 2) and 475 Hz (Speed 3), 92 Hz (Night) and 537 Hz (Manual). These values directly correspond to register values divided by 4, which probably has technical reasons either in programming the STM’s internal waveform generator, or for measurement and comparison of the actual RPM frequency returned by the original fan. The registers correspond like so:
- 10:5 = Speed 1
- 10:3 = Speed 2
- 10:2 = Speed 3
- 10:6 = Night
- 10:1 and 10:7 have something to do with manual and the maximum speed.
My guess is that the table provides the targets for RPM regulation, and that the user-selectable values are fixed ones among these. Unfortunately the signal being an RPM clock also means that a PC fan will turn on when attached, but will always remain at half speed due to the symmetric cycle time of the square-wave. By the way, the original fan is not shut down by setting the PFM clock to zero, but by toggling the BRK (brake) signal to the motor. A valid clock signal is present at all times.
Second mystery: The heartbeat, written to 13:9 as a value of “60” every 60 seconds. This is actually a counter that can be read – it’s zero in the above capture because it has not yet been written. I also tried to read it sequentially after setting it to 60, and indeed – it decrements by 5 every 5 seconds on its own accord until it reaches zero. My best guess is that this is a watchdog for the ESP firmware, which may result in a “reboot” command issued by the STM if left untouched. I did not test this. Note that this is a pretty soft watchdog, because it implies that the ESP is still capable of processing a serial reboot command in a faulty state. Well…
Third mystery: Value 15:10, which is assumed to be related to the selected power level. This is correct, monitoring this value reveals that it reflects the speed value 1,2,3 in the corresponding manual settings and remains at 3 for all other selectable speed options.
Fourth mystery: 13:5, which says “waiting” here. This value switches to “ready” about 60s after boot. I’d guess that it responds to presence of the heartbeat. A test with heartbeats (13:9) disabled confirms this; the value remains “waiting”.
Fifth mystery, which may be related to the first one: Value 13:4 looks an awful lot like a table again. The unit, to my knowledge, measures PM2.5 between 0 and 250 ug/m3 and displays accordingly. I’d bet that this is the threshold table, indicating activation of fan levels 1,2,3,4,5. My guess is that the fan idles in night level (10:6) and the thresholds activate the five levels (10:5 to 10:1) above. I don’t have a way to test this right now, but this one goes on the “prove later”-list.
Sixth mystery: Where is the actual RPM value? It’s hidden in 10:8, which is 0 above. If a fan is connected (a SUNON KDE1212PMV1 120mm fan in this case, overvolted to 15V) this value jumps to e.g. 48548 and moves with fan speed. The fan makes about 3000 rpm at 12V, so it is safe to assume it runs faster, say 3400 rpm or so. That’s about 57 rps, relating to a one-per-rev pulse period of 18ms. Since most PC fans output at least two pulses per revolution, I’d go for 9-10ms. And indeed, I can measure a pulse period of 5ms high/5ms low (10.1ms period).

Unfortunately, the low period is not a true low level, but rather a very fast square-wave at about 27kHz. This is quite unusual behavior and indicates that something is wrong with the fan signal. It’s safe to assume that the purifier’s measurement counts these pulse cycles, too, and is thus far off the mark. I’ll have to try a different fan.
Next, I want to take a look at:
- Data transferred to/from the PlanTower PMS9003M laser dust sensor and whether other values except the PM2.5 are stored somewhere they can be accessed (betting on 13: or 15: blocks),
- Whether there is a value indicating displayed system errors,
- If there is information about the filter RFID (password or something like that),
- How the filter door switch is announced,
- How to actually read the rpms when the fan is ok, and
- If it is possible to fumble the programmed rpm levels.
My first goal is to create an as-complete-as-possible register map. Originally, I was also interested in turning down the annoying beeper, but since I already chose to simply desolder it, that’s a thing of the past :-D