Usando um termômetro sem fio externo Buro H999 com dispositivos caseiros

Todo mundo é bom na estação meteorológica Buro H146G com um termômetro externo sem fio H999. Mas apenas para ver as leituras em seu LCD desbotado requer uma boa iluminação. E seria melhor para mim se a temperatura e a umidade do lado de fora da janela fossem exibidas em indicadores suficientemente brilhantes (por exemplo, combinando a temperatura e a umidade com o relógio nos indicadores de descarga de gás IN-12). Não é difícil criar uma embarcação assim, mas você precisa conhecer o protocolo de troca com um termômetro sem fio. Já existem artigos sobre o uso de um termômetro de estação meteorológica sem fio para obter temperatura e umidade no ar. Mas para as estações de Buro, o protocolo de troca ainda não foi descrito. Portanto, precisamos corrigi-lo: talvez alguém possa ser útil.

Na Internet, não encontrei uma descrição do protocolo para troca de estações BURO. E isso significa que você precisa abrir o protocolo de troca deste sensor sem fio.

Meu termômetro externo é assim:



Ao conectar um receptor super-regenerativo chinês de 433,92 MHz ao osciloscópio e pressionar o botão TEST no termômetro, ficou claramente visível como os pulsos de transmissão estavam funcionando. Bem, como a frequência é pequena, a saída do receptor foi conectada à entrada da placa de som através de um divisor resistivo. Depois de processar o arquivo de som gravado, o comparador exibiu a seguinte imagem:



Como em outras estações meteorológicas, a modulação é realizada alterando o ciclo de serviço. A transmissão começa no bloco do relógio, depois há outro sinal de relógio e depois há dados, após os quais o sinal de relógio final passa. Aparentemente, dois zeros após o sinal do relógio são o identificador do início dos dados - em qualquer caso, nunca notei a mudança deles. Os dados com um relógio inicial e final são duplicados seis vezes. A troca de dados é realizada por petiscos.

Para decodificar, decidi começar a receber no primeiro relógio e dois zeros e terminar no último relógio.

Para decodificar esse sinal, basta calcular a duração entre as quedas de sinal.

Para isso, escrevi um programa de teste simples para o controlador Atmega8:

Programa para Atmega8
//---------------------------------------------------------------------------------------------------- // //---------------------------------------------------------------------------------------------------- #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);//  } 


A saída do receptor está conectada ao pino 13 (AIN1). O Atmega se conecta via max232 à porta COM do computador (ou ao adaptador USB-COM). Velocidade da porta 9600 baud.

Após a decodificação, obtemos o seguinte fluxo de dados (eu expulso os dois zeros à esquerda):

// sem botão, canal 1
1100 1100 0000 1110 1000 0110 1100 0001 0000 1001 Umidade: 28% Temperatura: 25,4
// nenhum botão, canal 2
1100 1100 0001 1110 1000 0110 1101 0001 0000 0110 Umidade: 29% Temperatura: 25,4

O pacote total fica assim:



I0-I7 - identificador do termômetro. Cada vez que o termômetro é ligado, o identificador muda.

C0-C1 - canal (existem 3 possíveis no total). Os canais são numerados do zero.

H0-H7 - umidade. A umidade como porcentagem é lida como está, mas a temperatura (T0-T11) é, por algum motivo, definida em um formato incomum para estações meteorológicas. A julgar pelas descrições dos protocolos de troca de várias estações meteorológicas que encontrei, seria de esperar uma temperatura em décimos de grau e com uma mudança no limite inferior do termômetro. Então não. As experiências mostraram que o código de temperatura desta estação meteorológica se traduz em graus Celsius como (T-1220) / 18. Como esses números mágicos conhecem apenas os chineses que criaram esse protocolo de troca.

Como o lobisomem sugeriu nos comentários, a estação transfere a temperatura em décimos de grau Fahrenheit; portanto, uma tradução significativa em graus Celsius será 0,1 * (T-320) * 5 / 9-500 = 0,1 * (T-1220) / 1,8.

O bit K corresponde a pressionar o botão TEST.

Não foi possível estabelecer a atribuição dos campos restantes, mas o valor da chave Fahrenheit / Celsius no termômetro não entra no protocolo de troca. Presumivelmente, o último petisco (ou talvez parte do penúltimo) também é CRC, mas não consegui calcular o algoritmo (há uma suspeita de que linhas e colunas de petiscos estejam envolvidas no cálculo). Se alguém puder resolver esse enigma, informe-nos o algoritmo de cálculo.
Para aqueles que querem quebrar o cérebro, mas não têm esse termômetro, dou uma tabela de dados aceitos.

Quadro
 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 

Source: https://habr.com/ru/post/pt432704/


All Articles