Using an external wireless thermometer Buro H999 in conjunction with homemade devices

Published on December 10, 2018

Using an external wireless thermometer Buro H999 in conjunction with homemade devices

    All good weather station Buro H146G with an external wireless thermometer H999. But here only to see the readings on her black LCD display, good lighting is required. And I would be better if the output of temperature and humidity outside the window was displayed on fairly bright indicators (for example, by combining the display of temperature and humidity with the clock on the gas-discharge indicators IN-12). It is easy to make such an article, but you need to know the protocol of exchange with a wireless thermometer. There have already been articles on the use of a wireless thermometer weather stations to obtain temperature and humidity over the air. But for Buro stations, the exchange protocol has not yet been described. So, it is necessary to fix it: perhaps someone can come in handy.

    I did not find descriptions of the exchange protocol for BURO stations on the Internet. And this means that you have to open the protocol of the exchange of the wireless sensor.

    My external thermometer looks like this:



    By connecting the Chinese 433.92 MHz superregenerative receiver to the oscilloscope and pressing the TEST button on the thermometer, it was clearly visible how the transmission pulses were running. Well, since the frequency there is small, the output of the receiver was connected to the input of the sound card through a resistive divider. After processing the recorded sound file with a comparator, the following image was obtained:



    Like other weather stations, modulation is performed by changing the duty cycle. The transmission from the sync signal unit begins, then another sync signal goes, and then the data goes, followed by the final sync signal. Two zeros after the sync signal, apparently, are the identifier of the beginning of the data - in any case, I have never noticed their changes. Data with a start and end clock is duplicated six times. Data exchange is conducted by nibbles.

    For decoding, I decided to start receiving on the first clock signal and two zeros, and finish on the last clock signal.

    To decode such a signal, it is sufficient to calculate the duration between signal drops.

    For this I wrote a simple test program for the Atmega8 controller:

    Atmega8 software
    //----------------------------------------------------------------------------------------------------
    //библиотеки
    //----------------------------------------------------------------------------------------------------
    #include <avr/io.h>
    #include <util/delay.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h>
    #include <string.h>
    #include <stdbool.h>
    #include <stdint.h>
    //----------------------------------------------------------------------------------------------------
    //частота контроллера
    //----------------------------------------------------------------------------------------------------
    #define F_CPU 8000000UL
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //макроопределения
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //скорость передачи данных UART, бит/с
    #define UART_SPEED 9600UL
    //----------------------------------------------------------------------------------------------------
    //перечисления
    //----------------------------------------------------------------------------------------------------
    //тип блока
    enum BLOCK_TYPE
    {
     BLOCK_TYPE_UNKNOW,//неизвестный блок
     BLOCK_TYPE_DIVIDER,//разделитель
     BLOCK_TYPE_SYNCHRO,//синхросигнал
     BLOCK_TYPE_ONE,//единица
     BLOCK_TYPE_ZERO//ноль
    };
    //режим декодирования
    enum MODE
    {
     MODE_WAIT_SYNCHRO,//ожидание синхросигнала
     MODE_WAIT_ZERO_FIRST,//ожидание первого нуля
     MODE_WAIT_ZERO_SECOND,//ожидание второго нуля
     MODE_RECEIVE_DATA//приём данных
    }; 
    //----------------------------------------------------------------------------------------------------
    //глобальные переменные
    //----------------------------------------------------------------------------------------------------
    static const uint16_t MAX_TIMER_INTERVAL_VALUE=0xFFFF;//максимальное значение интервала таймера
    static volatile bool TimerOverflow=false;//было ли переполнение таймера
    static uint8_t Buffer[20];//буфер сборки полубайта
    static uint8_t BitSize=0;//количество принятых бит
    static uint8_t Byte=0;//собираемый байт
    //----------------------------------------------------------------------------------------------------
    //прототипы функций
    //----------------------------------------------------------------------------------------------------
    void InitAVR(void);//инициализация контроллера
    void UART_Write(unsigned char byte);//передача символа в COM-порт
    void SendText(const char *text);//отправить текст в COM-порт
    void RF_Init(void);//инициализация
    void RF_SetTimerOverflow(void);//установить флаг переполнения таймера
    void RF_ResetTimerOverflow(void);//сбросить флаг переполнения таймера
    bool RF_IsTimerOverflow(void);//получить, есть ли переполнение таймера
    uint16_t RF_GetTimerValue(void);//получить значение таймера
    void RF_ResetTimerValue(void);//сбросить значение таймера 
    BLOCK_TYPE RF_GetBlockType(uint32_t counter,bool value);//получить тип блока
    void RF_AddBit(bool state);//добавить бит данных
    void RF_ResetData(void);//начать сборку данных заново
    void RF_AnalizeCounter(uint32_t counter,bool value,MODE &mode);//анализ блока
    //----------------------------------------------------------------------------------------------------
    //основная функция программы
    //----------------------------------------------------------------------------------------------------
    int main(void)
    {
     InitAVR();  
     _delay_ms(200); 
     SendText("Thermo unit\r\n");
     _delay_ms(200);  
     sei();
     while(1);
     cli();  
    }
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //общие функции
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //----------------------------------------------------------------------------------------------------
    //инициализация контроллера
    //----------------------------------------------------------------------------------------------------
    void InitAVR(void)
    {
     //настраиваем порты
     DDRB=0;
     DDRD=0;
     DDRC=0; 
     //задаём состояние портов
     PORTB=0;
     PORTD=0;
     PORTC=0;
     //устанавливаем режим передачи данных UART
     UCSRB=(1<<RXEN)|(1<<TXEN)|(0<<RXCIE);  
     //RXCIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по завершению приёма по UART разрешено
     //TXCIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по завершению передачи по UART разрешено
     //UDRIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по опустошению регистра данных UART разрешено
     //RXEN=1 : активация приёмника, вывод D0 становится входом UART.
     //TXEN=1 : активация передатчика, вывод D1 становится выходом UART.
     //CHR9=1 : длина передаваемой посылки с становится равной 11 бит (9 бит данных + старт-стоповый бит + стоп-бит).
     //RXB8-расширенный стоп-бит
     //TXB8-расширенный стоп-бит
     //вычисляем значение регистра скорости передачи данных
     unsigned long speed=F_CPU/(16UL);
     speed=(speed/UART_SPEED)-1UL;
     UBRRH=(speed>>8)&0xff;
     UBRRL=speed&0xFF;
     RF_Init();  
    }
    //----------------------------------------------------------------------------------------------------
    //передача символа в COM-порт
    //----------------------------------------------------------------------------------------------------
    void UART_Write(unsigned char byte)
    { 
     while(!(UCSRA&(1<<UDRE)));
     UDR=byte;
    }
    //----------------------------------------------------------------------------------------------------
    //отправить текст в COM-порт
    //----------------------------------------------------------------------------------------------------
    void SendText(const char *text)
    {
     while((*text))
     {
      UART_Write(*text);
      text++;
     }
    }
    //----------------------------------------------------------------------------------------------------
    //инициализация
    //----------------------------------------------------------------------------------------------------
    void RF_Init(void)
    {
     //настраиваем аналоговый компаратор
     ACSR=(0<<ACD)|(1<<ACBG)|(0<<ACO)|(0<<ACI)|(1<<ACIE)|(0<<ACIC)|(0<<ACIS1)|(0<<ACIS0);
     //ACD - включение компаратора (0 - ВКЛЮЧЁН!)
     //ACBG - подключение к неинвертрирующему входу компаратора внутрреннего ИОН'а
     //ACO - результат сравнения (выход компаратора)
     //ACI - флаг прерывания от компаратора
     //ACIE - разрешение прерываний от компаратора
     //ACIC - подключение компаратора к схеме захвата таймера T1
     //ACIS1,ACID0 - условие генерации прерывания от компаратора
     //настраиваем таймер T1 на частоту 31250 Гц
     TCCR1A=(0<<WGM11)|(0<<WGM10)|(0<<COM1A1)|(0<<COM1A0)|(0<<COM1B1)|(0<<COM1B0);
     //COM1A1-COM1A0 - состояние вывода OC1A
     //COM1B1-COM1B0 - состояние вывода OC1B 
     //WGM11-WGM10 - режим работы таймера
     TCCR1B=(0<<WGM13)|(0<<WGM12)|(1<<CS12)|(0<<CS11)|(0<<CS10)|(0<<ICES1)|(0<<ICNC1);
     //WGM13-WGM12 - режим работы таймера
     //CS12-CS10 - управление тактовым сигналом (выбран режим деления тактовых импульсов на 256 (частота таймера 31250 Гц))
     //ICNC1 - управление схемой подавления помех блока захвата
     //ICES1 - выбор активного фронта сигнала захвата
     TCNT1=0;//начальное значение таймера
     TIMSK|=(1<<TOIE1);//прерывание по переполнению таймера (таймер T1 шестнадцатибитный) 
    } 
    //----------------------------------------------------------------------------------------------------
    //установить флаг переполнения таймера
    //----------------------------------------------------------------------------------------------------
    void RF_SetTimerOverflow(void)
    {
     cli();
     TimerOverflow=true;
     sei();
    }
    //----------------------------------------------------------------------------------------------------
    //сбросить флаг переполнения таймера
    //----------------------------------------------------------------------------------------------------
    void RF_ResetTimerOverflow(void)
    {
     cli();
     TimerOverflow=false;
     sei();
    }
    //----------------------------------------------------------------------------------------------------
    //получить, есть ли переполнение таймера
    //----------------------------------------------------------------------------------------------------
    bool RF_IsTimerOverflow(void)
    {
     cli();
     bool ret=TimerOverflow;
     sei();
     return(ret);
    }
    //----------------------------------------------------------------------------------------------------
    //получить значение таймера 
    //----------------------------------------------------------------------------------------------------
    uint16_t RF_GetTimerValue(void)
    {
     cli();
     uint16_t ret=TCNT1;
     sei(); 
     return(ret);
    } 
    //----------------------------------------------------------------------------------------------------
    //сбросить значение таймера 
    //----------------------------------------------------------------------------------------------------
    void RF_ResetTimerValue(void)
    {
     cli();
     TCNT1=0;
     sei();
     RF_ResetTimerOverflow();
    } 
    //----------------------------------------------------------------------------------------------------
    //получить тип блока
    //----------------------------------------------------------------------------------------------------
    BLOCK_TYPE RF_GetBlockType(uint32_t counter,bool value)
    { 
     static const uint32_t DIVIDER_MIN=(31250UL*12)/44100UL;
     static const uint32_t DIVIDER_MAX=(31250UL*25)/44100UL;
     static const uint32_t ZERO_MIN=(31250UL*80)/44100UL;
     static const uint32_t ZERO_MAX=(31250UL*100)/44100UL;
     static const uint32_t ONE_MIN=(31250UL*160)/44100UL;
     static const uint32_t ONE_MAX=(31250UL*200)/44100UL;
     static const uint32_t SYNCHRO_MIN=(31250UL*320)/44100UL;
     static const uint32_t SYNCHRO_MAX=(31250UL*400)/44100UL;
     if (counter>DIVIDER_MIN && counter<DIVIDER_MAX) return(BLOCK_TYPE_DIVIDER);//разделитель
     if (counter>ZERO_MIN && counter<ZERO_MAX) return(BLOCK_TYPE_ZERO);//ноль
     if (counter>ONE_MIN && counter<ONE_MAX) return(BLOCK_TYPE_ONE);//один
     if (counter>SYNCHRO_MIN && counter<SYNCHRO_MAX) return(BLOCK_TYPE_SYNCHRO);//синхросигнал
     return(BLOCK_TYPE_UNKNOW);//неизвестный блок
    }
    //----------------------------------------------------------------------------------------------------
    //добавить бит данных
    //----------------------------------------------------------------------------------------------------
    void RF_AddBit(bool state)
    {
     if ((BitSize>>2)>=19) return;//буфер заполнен
     Byte<<=1;
     if (state==true) Byte|=1;
     BitSize++; 
     if ((BitSize&0x03)==0)
     {
      Buffer[(BitSize>>2)-1]=Byte;
      Byte=0;
     }
    }
    //----------------------------------------------------------------------------------------------------
    //начать сборку данных заново
    //----------------------------------------------------------------------------------------------------
    void RF_ResetData(void)
    {
     BitSize=0;
     Byte=0;
    }
    //----------------------------------------------------------------------------------------------------
    //анализ блока
    //----------------------------------------------------------------------------------------------------
    void RF_AnalizeCounter(uint32_t counter,bool value,MODE &mode)
    {
     //узнаем тип блока
     BLOCK_TYPE type=RF_GetBlockType(counter,value);
     if (type==BLOCK_TYPE_UNKNOW)
     {
      mode=MODE_WAIT_SYNCHRO;
      RF_ResetData();
      return;
     } 
     if (type==BLOCK_TYPE_DIVIDER) return;//разделитель бесполезен для анализа 
     //посылка должна начинаться и завершаться синхросигналом
     if (mode==MODE_WAIT_SYNCHRO)//ждём синхросигнала
     {
      if (type==BLOCK_TYPE_SYNCHRO)
      {
       mode=MODE_WAIT_ZERO_FIRST;
       return;
      }
      mode=MODE_WAIT_SYNCHRO;
      RF_ResetData();
      return;
     }
     if (mode==MODE_WAIT_ZERO_FIRST || mode==MODE_WAIT_ZERO_SECOND)//ждём два нуля
     {
      if (type==BLOCK_TYPE_SYNCHRO && mode==MODE_WAIT_ZERO_FIRST) return;//продолжается синхросигнал
      if (type==BLOCK_TYPE_ZERO && mode==MODE_WAIT_ZERO_FIRST)
      {
       mode=MODE_WAIT_ZERO_SECOND;
       return;
      }
      if (type==BLOCK_TYPE_ZERO && mode==MODE_WAIT_ZERO_SECOND)
      {
       mode=MODE_RECEIVE_DATA;
       return;
      }
      mode=MODE_WAIT_SYNCHRO;
      RF_ResetData();
      return;
     }
     //принимаем данные
     if (type==BLOCK_TYPE_SYNCHRO)//приём окончен
     {
      uint8_t size=(BitSize>>2);
      char str[30];
      if  (size!=10)
      {
       mode=MODE_WAIT_SYNCHRO;
       RF_ResetData();
       return; 
      }
      //выдаём блок
      for(uint8_t n=0;n<size;n++)
      {
       uint8_t b=Buffer[n];  
       uint8_t mask=(1<<3);
       for(uint8_t m=0;m<4;m++,mask>>=1)
       {
        if (b&mask) SendText("1");
    	       else SendText("0");
       }
       SendText(" "); 
      }
      uint8_t channel=Buffer[2]&0x03;
      uint8_t key=(Buffer[8]>>3)&0x01;
      uint8_t h=(Buffer[7]<<4)|(Buffer[6]);//влажность
      int16_t temp=(Buffer[5]<<8)|(Buffer[4]<<4)|(Buffer[3]);//температура 
      int16_t k=18;
      int16_t t=(10*(temp-1220))/k;
      sprintf(str,"%i",key);
      SendText("Key:");
      SendText(str);  
      sprintf(str,"%i",channel+1);  
      SendText(" Ch:");
      SendText(str);  
      sprintf(str,"%i",h);  
      SendText(" H:");
      SendText(str);
      SendText("%, T:");
      if (t<0)
      {
       t=-t;
       sprintf(str,"-%i.%i",(int)(t/10),(int)(t%10));
      }
      else
      {
       sprintf(str,"%i.%i",(int)(t/10),(int)(t%10));
      }  
      SendText(str);
      SendText(" C\r\n");
      mode=MODE_WAIT_SYNCHRO;
      RF_ResetData();
      return;
     }
     //приём данных
     if (type==BLOCK_TYPE_ONE)
     {
      RF_AddBit(true);
      return;
     }
     if (type==BLOCK_TYPE_ZERO)
     {
      RF_AddBit(false);
      return;
     }
     mode=MODE_WAIT_SYNCHRO;
     RF_ResetData();
    }
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //обработчики векторов прерываний
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //----------------------------------------------------------------------------------------------------
    //обработчик вектора прерывания таймера T1 (16-ми разрядный таймер) по переполнению
    //----------------------------------------------------------------------------------------------------
    ISR(TIMER1_OVF_vect)
    {   
     RF_SetTimerOverflow();
    } 
    //----------------------------------------------------------------------------------------------------
    //обработчик вектора прерывания от компаратора
    //----------------------------------------------------------------------------------------------------
    ISR(ANA_COMP_vect)
    {
     ACSR&=0xFF^(1<<ACIE);//запрещаем прерывания
     ACSR|=(1<<ACI);//сбрасываем флаг прерывания компаратора
     static MODE mode=MODE_WAIT_SYNCHRO;
     //узнаём длительность интервала
     uint16_t length=RF_GetTimerValue();
     if (RF_IsTimerOverflow()==true) length=MAX_TIMER_INTERVAL_VALUE;//было переполнение, считаем интервал максимальным
     RF_ResetTimerValue();
     //отправляем на анализ
     bool value=true;
     if (ACSR&(1<<ACO)) value=false;
     RF_AnalizeCounter(length,value,mode);
     ACSR|=(1<<ACIE);//разрешаем прерывания
    }
    


    The output of the receiver is connected to pin 13 (AIN1). Atmega via max232 connects to a computer’s COM port (or to a USB-COM adapter). The speed of the port is 9600 baud.

    After decoding, the following data stream will be obtained (I emit two initial zeros):

    // without a button, channel 1
    1100 1100 0000 1110 1000 0110 1100 0001 0000 1001 Humidity: 28% Temperature: 25.4
    // without a button, channel 2
    1100 1100 0001 1110 1000 0110 1101 0001 0000 0110 Humidity: 29% Temperature: 25.4

    Total, the package looks like this:



    I0-I7 is a thermometer identifier. Each time the thermometer is turned on, the identifier changes.

    C0-C1 channel (there are 3 possible in total). Channels are numbered from scratch.

    H0-H7 - humidity. Humidity in percent is read as it is, but for some reason the temperature (T0-T11) is set in an unusual format for meteorological stations. Judging by the descriptions of the exchange protocols of various meteorological stations found by me, one would expect a temperature in tenths of a degree and with a shift in the lower limit of measurement of the thermometer. So, no. Experiments have shown that the temperature code of this weather station is converted to degrees Celsius as (T-1220) / 18. From where these magic numbers are known only by the Chinese, who invented this exchange protocol.

    As the wolowizard suggested in the comments, the station transmits the temperature in tenths of degrees Fahrenheit, so a meaningful translation to Celsius will be 0.1 * (T-320) * 5 / 9-500 = 0.1 * (T-1220) /1.8.

    Bit K corresponds to pressing the TEST button.

    The assignment of the remaining fields could not be established, but it turned out that the value of the Fahrenheit / Celsius switch on the thermometer does not fall into the exchange protocol. Presumably, the last nibble (and maybe the last part of the last one) is a CRC, but I haven’t managed to calculate the algorithm yet (there is a suspicion that rows and nibbls are involved in the calculation). If anyone can solve this riddle, please inform the calculation algorithm.
    For those who want to break their heads, but do not have such a thermometer, I provide a table of received data.

    Table
    1001 0110 0101 1011 1000 0110 1000 0010 0001 1111 Key:0 Ch:2 H:40%, T:25.2 C
    1001 1001 0000 1101 1010 0100 0101 0101 0000 0110 Key:0 Ch:1 H:85%, T:-1.2 C
    1001 0110 0101 1100 1000 0110 1010 0010 0001 0100 Key:0 Ch:2 H:42%, T:25.3 C
    1001 0110 1001 0110 0111 0110 1101 0001 0010 1111 Key:0 Ch:2 H:29%, T:24.1 C
    1001 0110 1001 0000 0111 0110 1101 0001 0010 1000 Key:0 Ch:2 H:29%, T:23.7 C
    1001 0110 1001 0010 0101 0110 1110 0001 0010 1111 Key:0 Ch:2 H:30%, T:22.1 C
    1001 0110 1001 1001 0011 0110 1110 0001 0010 1100 Key:0 Ch:2 H:30%, T:20.7 C
    1001 0110 1001 1111 0001 0110 1111 0001 0010 1010 Key:0 Ch:2 H:31%, T:19.2 C
    1001 0110 0101 1001 0000 0110 0001 0010 0010 1000 Key:0 Ch:2 H:33%, T:18.0 C
    1001 0110 0101 0010 1111 0101 0010 0010 0010 0111 Key:0 Ch:2 H:34%, T:16.7 C
    1001 0110 0101 0100 1110 0101 0010 0010 0010 0010 Key:0 Ch:2 H:34%, T:16.0 C
    1001 0110 0101 0100 1101 0101 0011 0010 0010 0001 Key:0 Ch:2 H:35%, T:15.1 C
    1001 0110 0101 1100 1100 0101 0100 0010 0010 1110 Key:0 Ch:2 H:36%, T:14.6 C
    1001 0110 0101 1111 1011 0101 0101 0010 0010 1111 Key:0 Ch:2 H:37%, T:13.9 C
    1001 0110 0101 0011 1011 0101 0101 0010 0010 0001 Key:0 Ch:2 H:37%, T:13.2 C
    1001 0110 0101 1001 1010 0101 0110 0010 0010 0101 Key:0 Ch:2 H:38%, T:12.7 C
    1001 0110 0101 0100 1010 0101 0111 0010 0010 1000 Key:0 Ch:2 H:39%, T:12.4 C
    1001 0110 0101 1011 1001 0101 0111 0010 0010 1010 Key:0 Ch:2 H:39%, T:11.9 C
    1001 0110 0101 0011 1001 0101 1000 0010 0010 1001 Key:0 Ch:2 H:40%, T:11.5 C
    1001 0110 0101 1011 1000 0101 1000 0010 0010 1110 Key:0 Ch:2 H:40%, T:11.0 C
    1001 0110 0101 0111 1000 0101 1001 0010 0010 0101 Key:0 Ch:2 H:41%, T:10.8 C
    1001 0110 0101 1111 0111 0101 1001 0010 0010 1101 Key:0 Ch:2 H:41%, T:10.3 C
    1001 0110 0101 0111 0111 0101 1010 0010 0010 0111 Key:0 Ch:2 H:42%, T:9.9 C
    1001 0110 0101 0001 0111 0101 1011 0010 0010 0101 Key:0 Ch:2 H:43%, T:9.6 C
    1001 0110 0101 1011 0110 0101 1100 0010 0010 0110 Key:0 Ch:2 H:44%, T:9.2 C
    1001 0110 0101 1000 0110 0101 1100 0010 0010 1100 Key:0 Ch:2 H:44%, T:9.1 C
    1001 0110 0101 0011 0110 0101 1101 0010 0010 0110 Key:0 Ch:2 H:45%, T:8.8 C
    1001 0110 0101 1001 0101 0101 1110 0010 0010 0110 Key:0 Ch:2 H:46%, T:8.2 C
    1001 0110 0101 0101 0101 0101 1111 0010 0010 1101 Key:0 Ch:2 H:47%, T:8.0 C
    1001 0110 0101 0010 0101 0101 1111 0010 0010 1100 Key:0 Ch:2 H:47%, T:7.8 C
    1001 0110 0101 1110 0100 0101 1111 0010 0010 0000 Key:0 Ch:2 H:47%, T:7.6 C
    1001 0110 0101 1100 0100 0101 1111 0010 0010 1100 Key:0 Ch:2 H:47%, T:7.5 C