The Mijia MHO-C201 (also sold as Xiaomi Miaomiaoce MMC-C201) is thermometer and hygrometer display with a segmented e-ink screen.
A multimeter in continuity mode was used to trace the connections between the stock MCU (a Holtek HT66F0182) and the display controller (a Holtek HT16E07). A schematic was created using Kicad.
To determine the purpose of each connection, a logic analyzer was used to capture the signals (see ./captures
) between the MCU and display controller. The captures were compared to the HT16E07 datasheet which confirmed the display controller behaves like a HT16E07.
The MCU was then removed (using flush cutters but would have been nicer to desolder) and thin, 30-awg wires were soldered to the now-empty MCU pads. The other end of the wires connected to a NodeMCU ESP8266.
A basic Micropython library was written and tested using the ESP8266.
A Micropython library to control the display is in: ./micropython.
-
Display at P1: Segmented e-paper display controller connected to PCB with 10-pin FPC.
-
Probably a HT16E07. The commands captured between the MCU and Display match the HT16E07 datasheet
- Except the MCU here seems to send a 14-byte DTM command whereas the datasheet says 17 bytes are required otherwise "the result of the data comparison between the new_data register and the previous_data register will be incorrect."
-
Pins (from "top")
-
VDL
- From HT16E07 datasheet:
-
Driver low supply voltage – bypass to GND with 1μF capacitor
- Connected to capacitor at C3.
- Measured voltage: 3.4V.
-
VDH
-
From HT16E07 datasheet:
-
Driver high supply voltage – bypass to GND with 1μF capacitor
-
-
Connected to capacitor at C2.
-
Measured voltage 11.6V
-
-
GND
-
VDD
Datasheet: 2.4 to 3.6V
-
SHD_N
-
From datasheet:
-
Charge pump enable pin – low shutdown
-
- RST_N
- SDA (data)
- SCL (clock)
- CSB (latch) (Low during data clock pulses, pulses high after 9 clocks pulses)
-
-
BUSY_N
-
From datasheet:
- > Busy flag output pin > BUSY_N="0" – driver is busy, driver is refreshing the display > BUSY_N="1" – driver is idle, host can send command/data to driver
-
Based on observation, BUSY_N is pulled down by the display when busy, and floating when idle so the MCU pin connected to BUSY_N should have a pull-up resistor.
-
-
MCU at U2: HT66F0182 datasheet
-
Pins 1. GND 2. Display P1-7 3. Not connected 4. Display P1-6 5. Not connected 6. Display P1-5 7. Display P1-9 8. Display P1-8 9. Display P1-10 10. S1 11. ??? 12. Sensor U1-4 I2C SCL 13. Sensor U1-3 Alert 14. Sensor U1-1 I2C SDA 15. Sensor U1-6 Reset 16. +BATT. Connected to nearby capacitor at C4.
-
Sensor at U1: SHT30-DIS [datasheet](datasheets/SHT3X-DIS Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf)
- Pins
- SDA
- ADDR. Seems not connected but docs for SHT30-DIS say "do not leave floating". MCU addresses Sensor using the default address 0x44 so assume this is "connected to logic low".
- ALERT
- SCL
- VDD
- Datasheet: 2.15 - 5.5V. 3.3V typical
- Connected to nearby to capacitor at C1
- nRESET
- R
- Datasheet: No electrical function; to be connected to VSS
- VSS
- Pins
-
Switch at S1: Toggles between C and F units.
- Connects MCU pin 10 to GND when pressed
-
Capacitors:
- C1 decoupling capacitor for sensor
- C2 capacitor connected to pin 1 of P1
- C3 capacitor connected to pin 2 of P1
- C4 decoupling capacitor for MCU and/or display controller
-
R1/R2 are footprints for pull-up resistors for the Sensor SDA/SCL lines but they're are unpopulated probably because the MCU pins (14, 12) are using internal pull-ups.
-
[MHO-C201 English User Manual](datasheets/MHO-C201 English User Manual.pdf)
-
[SHT3X-DIS Humidity and Temperature Sensor Datasheet](datasheets/SHT3X-DIS Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf)
The MCU sends updates the display 3 times on startup:
- MCU powers on
- After 500 ms, sets Display SHD_N high
- Update #1: Full clear. This update turns on then off all segments using special LUT values (see below for Update Display Sequence)
- BUSY_N is low for 3,000 ms between after sending DRF
- Wait 100ms
- During this 100ms the MCU reads temp from Sensor
- Immediately after DRF, MCU writes "Single Shot Data Acquisition Mode" command to sensor: 0x2416 (no clock stretching, low repeatability)
- 86 ms later (maybe triggered by Sensor ALERT line, I didn't have logic analyzer hooked to this line), read 6 bytes from Sensor, 2-byte temperature, 1-byte checksum, 2 byte humidity, 1-byte checksum.
- No other messages between MCU and Sensor observed in the first 10s after power on.
- Update #2: Set off segments. After Update #1 all segments are off so this update doesn't seem necessary.
- This time BUSY_N is low for 500 ms after DRF
- Update #3: Set on segments. This turns on some segments (to show temp/humidity)
- This time BUSY_N is low for 1,00 ms after DRF
- Sends final (third) CPOF
- SHD_N low
- RST_N pulse low for 1.5ms
After startup the MCU will periodically read the Sensor and sets on/off state of the segments. Each Update Display Sequence can only turn on or off segments but can't both turn off some and turn on some segments. So the Update Display Sequence needs to run twice: (1) turn off segments that shouldn't be on (2) turn on segments that should be on.
-
Send low pulse on RST_N 0.5ms
-
Wait 0.5ms
-
Send: Panel Setting (PSR)
-
Send: Power Setting (PWR)
-
Send: Charge Pump ON (CPON)
-
Wait for BUSY_N high
-
Send: Frame Rate Control (FRC)
-
Send: Frame Rate Control (FRC) (seems this second FRC i sonly sent on initial all-on refresh)
-
Send: LUTV, LUT_KK, LUT_KW
- The LUT define the timing and voltages for turning on/off the segments
- There are 3 sets of LUT values
- LUT values to fully clear the display by turning on and then off all the segments
- LUT values to turn off segments
- LUT values to turn on segments
-
Send: Data Start Transmission (DTM)
-
Datasheet:
Note that users must send the full 17-byte command at once. If less than a 17-byte command is sent, the contents of the previous_data register will be incorrect. In this case the outcome will be that the result of the data comparison between the new_data register and the previous_data register will be incorrect.
-
Logic analyzer capture shows DTM command is consistently 14 bytes.
-
Testing with custom code, DTM command can include any number of bytes (tested by sending 0 0x00 and 100 0x00). Any missing bytes seem to be assume is 0x00.
-
Send: Data Stop (DSP)
-
Send: Display Refresh (DRF)
-
BUSY_N goes low which means display is refreshing
-
Datasheet
After executing a DTM command and a DSP command, BUSY_N will be set to “0”.
-
-
Wait for BUSY_N high
-
Datasheet:
After finishing a display refresh, BUSY_N will be set to “1” and the data_flag wil be set to “0”.
-
-
Send: Charge Pump OFF (CPOF)
Segment 94 turns on the parts of the display that are always on during normal usage:
- Degree symbol and common parts of C and F
- Decimal
- Side of faces
- Percent sign
Segment 96 is the "background"