Чтение и эмуляция датчиков Oregon Scientific (433Mhz)

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

Да, к стати, из этого поста пробовал, не знаю, почему не оставил, просто так может, чтобы другой попробовать ), хотя вроде он реже слышал посылки от далеко расположенного датчика, а может мне это показалось, тогда было время экспериментов. Надо вернуться, повырезать из него все ненужные декодеры, и, думаю, он сильно легче будет.

ЗЫ просто я не программер, так, индусский код.

ЗЗЫ А насчёт того, что он влажности не видел (именно G не поддерживал), это наверное, в другом сообщении код был.

snickser
Offline
Зарегистрирован: 02.07.2016

udavst,

Ну собственно да, я уже ранее это писал, код в первом посте плохо "слышит", а тот что в 113  получше будет.

 

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

У меня от  код от Porosenok, первый, который он тут публиковал - ловит прекрасно, но осталось 300 байт памяти, она переполняется, как только полностью заполняется массив передачи, и передача отваливается, из своего мне экономить нечего, отключаю дисплей, выигрываю немного памяти, всё работает долго, но нафигаж я дисплей прикрусивал, отключаю давление - тоже, но не хочется и так.

Из #113 поста код у меня молчит как партизал, за полчаса ниразу в serial ничего не плюнул, код из #224 выводит такое (у меня THGN122N на 3 канале)

[ookDecoder]
59.911 KSX 98A604FC0FEC3E
116.348 KSX 98BA0AFC0FFAFE
141.641 HEZ 3E000000003C01
161.462 HEZ 030310F2814901
173.348 KSX 98AA01FC0FE0FF
173.461 KSX 98AA01FC0FE03F
173.573 KSX 98AA01FC0FE03F

а код из #230 опять молчит.
 
Ан нет, поймал, но один раз за 10 минут ( (орегон на улице с противоположной стороны панельного дома), и больше не поймал ниразу.
109.853 OSV2 1A2D40B6201468053F55 3 b6 -14.2 56% 0

От Porosenok ловит каждую посылку. 

Вот такая незадача, либо памяти нет, либо приёма ((

 

Sprite
Offline
Зарегистрирован: 30.10.2016

Попробуйте код из этого поста.
http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-s...

Дописав туда строчки из этого поста:
http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-s...

У меня всё работает.

snickser
Offline
Зарегистрирован: 02.07.2016

Sprite,

Это тот же самый код, с парой мелких правок не связанных с приёмником.

udavst,

Да, проблема... Ну, есть способы её решить, положить рядом вторую ардуину (я вот к примеру Orange Pi купил, буду на нём пробовать), подключив их по Serial для обмена как вариант. NodeMCU можно попробовать, там вроде памяти сильно больше, придет мне скоро.

А антенна у вас нормальная, не кусок провода?...  )) Можно попробовать сменить приёмник на более чувствительный (даже гетеродинный).

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

да в том и дело, что, из-за 500 нехватающих байт городить вторую дуину в корпус как-то нелепо ), просто эта штука у меня больше выполнять ничего не будет, её дело просто принять с орегона, добавить у себя показания температуры-влажности-давления внутри дома, вывести на индикатор и отправить по радиоканалу (nrf24l01) на основной блок. Вообщем вчера сидел долго, в коде расшифровки ничего не понимаю, уменьшить не могу, пришлось урезать массив отправки до int8_t, выиграл 10 байт где-то, пока работает, но для любой хотелки места нет, и передаются теперь, конечно, только целые числа (

Приёмник вот такой, антенна - да кусок 16.5см провода (белый на фото, загнутый полукругом), но ведь код Porosenok ловит как-то, дело значит не в чувствительности, раз программно устраняется.

Porosenok: Можно убрать массивы (температура, влажность...) с каналами (если у меня 1 датчик, зачем мне все значения для всех каналов)?, ну и ещё как-то уменьшить использование памяти, убрать ненужные счётчики и переменные (ведь требуется только температура, влажность, канал, батарея, все эти сырые данные и тайминги обработки - зачем?

сейчас я навырезал, всё что понял, но это для меня предел (

/*Скетч использует 20562 байт (66%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 1737 байт (84%) динамической памяти, оставляя 311 байт для локальных переменных. Максимум: 2048 байт.*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(9,10);
const uint32_t pipe = 000000042; // адрес
#include "DHT.h"
DHT dht(3, DHT22);
#include "Adafruit_BMP085.h"
int BMP085_ADDRESS = 0x77; //BMP085 (SPI addr)
Adafruit_BMP085 bmp;
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 display = Adafruit_PCD8544(8, 7, 6, 5, 4);
#define THGN132 0x1D20      //Типы известных датчиков
//#define THN132  0xEC40
#define MHZ 2               //Приёмник подключается к D2
#define FIND_PACKET   1     
#define ANALIZE_PACKETS 3
#define PER_LENGTH 976      // Период в мкс
#define THR_LENGTH 615     //Порог периода 0 или 1
#define LENGTH_TOLERANCE 20 //Допуск на период
#define READ_BITS 90        //Размер битовой посылки
#define READ_BITS2 180      //Двойной размер битовой посылки
#define PACKET_LENGTH 20    //Размер пакета данных
//unsigned long lastTime = 0;           // таймер
bool  reciever_ctrl = true; //Флаг контроля ресивера (выставляется при приходе импулься, сбрасывается в таймере)
bool  reciever_status = false; // Результат проверки состояния ресивера
//Массивы для хранения полученных данных:
//данные 0-"o" (111 - индикатор передающего), 1-ochan, 2-otemp, 3-ohum, 4-obat, 5-htemp, 6-hhum, 7-pressure 
int8_t  datadat[8]={111, 0, 0, 0, 0, 0, 0, 0}; 
float r_tmp[3];         //Температура 
byte  r_hmdty[3];       //Влажность. Если = 101 - значит нет данных 
bool  r_bat[3];         //Флаг батареи
bool  r_isreceived[3];  //Флаг о том, что по данному каналу приходил хоть один правильный пакет
word  r_type[3];        //Тип датчика
unsigned long rcv_time[3]; // времена прихода пакетов
byte _chnl;              
byte receive_status, packet_number;
byte start_pulse_cnt;
unsigned long pulse_length, work_time,  timer_marklong;  
unsigned long pulse_marker, right_pulse_marker, last_premarker;
unsigned long pre_marker[4]; // Для хранения временных меток преамбулы ()
unsigned long first_packet_end;
bool crc_c;             //Результат проверки CRC
word sens_type;         //Тип сенсора
bool pulse_type;        //Пустое
byte data_length, data_length2; //Длины пакетов
int data_val, data_val2;        // Качество пакетов
//Массивы данных для записи данных с канала и полученных битов
byte collect_data[READ_BITS2], collect_data2[READ_BITS2];//А когда становится массивом полученных битов, то значения такие: 0 - неизвестен >0 - единица <0 - ноль
byte decode_tacts[READ_BITS2]; //Массив тактов. значения 0=ноль1=единица2=неизвестен3=переход+4=переход-
/////////    ПРЕРЫВАНИЕ   /////////////////////////////////////////
volatile unsigned long pm;
volatile unsigned long pl, timer_mark;  
void int_0(void) {  //вычисляем начало и динну импульса
  if(digitalRead(MHZ)){ pl = 0; pm = micros(); }  else{ pl = micros() - pm; pm += pl; }
}  
//////////////// УСТАНОВКИ  ///////////////////////////////////////
void setup (){ 
 Serial.begin(115200);
 dht.begin(); delay (200);
  datadat[5] = dht.readTemperature(); datadat[6] = dht.readHumidity(); 
 bmp.begin();
  datadat[7] = ((bmp.readPressure()/133.33)-700);  //давление
  datadat[5] = bmp.readTemperature(); 
 display.begin();
  display.setContrast(55);
  display.setRotation(2);
  display.display();
  display.clearDisplay();
  //Прерывание по сигналу от приёмника
  pinMode(MHZ,INPUT);   
  attachInterrupt(0, int_0, CHANGE);  
  receive_status = FIND_PACKET;  start_pulse_cnt = 0;  packet_number = 0;
  for (int i = 0; i < 3; i++){ rcv_time[i] = 7000000; r_isreceived[i] = 0;}
  //pinMode(13, OUTPUT);   digitalWrite(13, LOW);       //Будем мигать светодиодом

radio.begin();
radio.setRetries(10,10);
  radio.setDataRate(RF24_250KBPS); // скорость обмена данными RF24_1MBPS или RF24_2MBPS
  radio.stopListening(); // Во-первых, перестать слушать, потому что нам нужно говорить.
  radio.openWritingPipe(pipe); // открыть канал на отправку  
 // radio.setPALevel      (RF24_PA_MAX);                      // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm)
   radio.printDetails();
}

//////////////// ГЛАВНЫЙ ЦИКЛ  ///////////////////////////////////////////////
void loop() {
  noInterrupts(); pulse_length = pl; pl = 0; pulse_marker = pm; interrupts();
//////////////////////////////////////////////////////////////////////////////
//Получен некий импульс,//////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
  if (pulse_length != 0 && receive_status == FIND_PACKET){  reciever_ctrl = 1;
    //Режим поиска пакета в эфире
    if (start_pulse_cnt == 0){//Найдена первый "правильный" импульс - длинная единица!
      if (pulse_length < (PER_LENGTH) && pulse_length > (THR_LENGTH) ) {start_pulse_cnt = 1; pre_marker[start_pulse_cnt] = pulse_marker; pulse_length = 0;}
    }
    else{//Ищем следующие импульсы
     if (pulse_length <= (PER_LENGTH) && pulse_length >= (THR_LENGTH)) {//Найден импуль правильной длины
        //Если импульс в правильном месте, то добавляем счётчик найденых стартовых импульсов
        if(pulse_marker - pre_marker[start_pulse_cnt] > (PER_LENGTH*2-LENGTH_TOLERANCE) && pulse_marker - pre_marker[start_pulse_cnt] < (PER_LENGTH * 2 + LENGTH_TOLERANCE)){
          start_pulse_cnt++;  pre_marker[start_pulse_cnt] = pulse_marker; pulse_length = 0; }
        ///...если  в неправильном месте то назначаем его первым
        else{ start_pulse_cnt = 1; pre_marker[start_pulse_cnt] = pulse_marker; pulse_length = 0;} 
     }
      else{ //Если импульс неправильной длины, то стоит проверить, а не вышло ли время ожидания правильного импульса
        if(pulse_marker - pre_marker[start_pulse_cnt] < (PER_LENGTH * 2 + LENGTH_TOLERANCE)){pulse_length = 0; } //Время ещё не вышло, скорее всего это помеха. Пропускаем...
        else{  start_pulse_cnt = 0;pulse_length = 0;}//Время вышло, начинаем искать заново
      }
    }
  }
    if(packet_number == 1 && (millis() - first_packet_end) > 200){ // Если найден первый пакет и вышло вермя ожидания второго
    receive_status = ANALIZE_PACKETS;}                             // Не ждём второго, а переходм в режим анализа
//////////////////////////////////////////////////////////////////////
// Сбор данных////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
    //Если Найдены три длинных единицы и два длинных нуля. Возможно это начало посылки...
    if (start_pulse_cnt == 3 && receive_status == FIND_PACKET){ work_time = millis(); last_premarker = pre_marker[3]; start_pulse_cnt = 0;
        if (packet_number == 0){ collect(collect_data, &data_length); first_packet_end = millis(); packet_number = 1; }
        else{ collect(collect_data2, &data_length2); packet_number = 2; receive_status = ANALIZE_PACKETS; }
    }
      if  (receive_status == ANALIZE_PACKETS){ //digitalWrite(13, HIGH);
      //////////////////////////////////////////////////////////////////////
      // Анализ данных//////////////////////////////////////////////////////
      //////////////////////////////////////////////////////////////////////      
      bool halfshift; get_bits(collect_data); 
      if (get_data(0, collect_data) > get_data(1, collect_data)){ data_val = get_data(0, collect_data); halfshift = 0; }
      else {data_val = get_data(1, collect_data); halfshift = 0;}
      if (packet_number == 2){get_bits(collect_data2); 
      if (get_data(0, collect_data2) > get_data(1, collect_data2)) {data_val2 = get_data(0, collect_data2); halfshift = 0; }
       else { data_val2 = get_data(1, collect_data2); halfshift = 1;}
      }
      int correlation = correlate_data(collect_data, collect_data2); byte* result_data, result_data_start, aux_data;
        if (packet_number == 1){ result_data = collect_data;}
        else{if (correlation > 0){ result_data = collect_data2; assemble_data(collect_data2,collect_data, &correlation); }
        else{result_data = collect_data; correlation = -correlation; assemble_data(collect_data, collect_data2, &correlation);}
        //Вывод готовой посылки
        }
        //Обработка посылки
//      Serial.print("PERIOD: ");
//      Serial.print(millis()/40000);
      byte packet[PACKET_LENGTH], valid_p[PACKET_LENGTH]; 
      if (get_info_data(result_data, packet, valid_p)){ sens_type = get_sensor(packet); //Определяем тип пакета по типу датчика
        restore_data(packet, sens_type); // Восстанавливаем данные по типу датчика
        crc_c = check_CRC(packet, sens_type); // Проверяем CRC, если оно верно, то все сомнительные биты делаем уверенными
        if (crc_c) for (byte www = 0; www < PACKET_LENGTH; www++) valid_p[www] = 0x0f;
//        Serial.print(" PACKET: ");
//        for (int q = 0;q < PACKET_LENGTH; q++) Serial.print(packet[q], HEX);
//        Serial.print(" VALIDITY: ");
//          for (int q = 0; q < PACKET_LENGTH; q++) Serial.print(valid_p[q], HEX);
      }
      if (sens_type == THGN132/* || sens_type == THN132*/){
//        Serial.print(" TYPE: ");
//        if (sens_type == THGN132) Serial.print("THGN132N ");
//        if (sens_type == THN132) Serial.print("THN132N  ");
//        Serial.print("ID: ");
//        Serial.print(get_id(packet), HEX);
//        Serial.print(" CHNL: ");
      _chnl=get_channel(packet)-1;
//        Serial.print(_chnl+1);
      r_type[_chnl] = sens_type;
        if (crc_c) r_isreceived[_chnl] = 1;
        
        //......Если получен правильный пакет, сбрасываем таймер
        if(crc_c) rcv_time[_chnl] = millis();  //         
        if(get_temperature_mask(valid_p)){
          if(crc_c) r_tmp[_chnl] = get_temperature(packet);
        }
        if (sens_type == THGN132) {
          if (get_humidity_mask(valid_p)) {
            if(crc_c) r_hmdty[_chnl] = get_humidity(packet);
        }
        else{
//          Serial.print("--");
          r_hmdty[_chnl] = 101;
        }
        if(crc_c) r_bat[_chnl] = get_battery(packet);
      }
//      Serial.print(" CRC: ");
//      if (crc_c) Serial.print("OK ");
//      else Serial.print("-- ");
      
          
//      Serial.print(" PROC. TIME: ");
//      Serial.print(millis() - work_time);
//      Serial.println("ms ");
    }
    else Serial.print(sens_type); 
    //в массив данные с oregon
      datadat[1]=_chnl+1; 
 if (_chnl=2){ //если передаёт наш датчик (3 канал) присваеваем данные на отправку
      datadat[2]=r_tmp[_chnl];
      datadat[3]=r_hmdty[_chnl];
      datadat[4]=r_bat[_chnl];  
 }
    // Возвращаемся к исходному состоянию
    receive_status = FIND_PACKET;    packet_number = 0;    start_pulse_cnt = 0;    sens_type = 0;     _chnl = 0;

//    digitalWrite(13, LOW);
  }
//////////////////////////////////////////////////////////////////////      
//Здесь записываются остальные  процедуры
//////////////////////////////////////////////////////////////////////      
  if ((millis() - timer_mark) > 10000) { timer_mark=millis();
//      { if (!reciever_status) Serial.println("RECEIVING...");  reciever_status = 1; }
      reciever_ctrl = 0; 
//      digitalWrite(13, !digitalRead(13)); 

datadat[5]=dht.readTemperature();datadat[6]=dht.readHumidity(); datadat[7]=((bmp.readPressure()/133.33)-700);
Serial.println (""); Serial.print("oTMP:"); Serial.print( datadat[2]);  Serial.print(" oHUM:"); Serial.print( datadat[3]); 
Serial.print(" hTMP:"); Serial.print(datadat[5]); Serial.print(" hHUM:"); Serial.print(datadat[6]); Serial.print(" PRES:");Serial.print(datadat[7]+700);
Serial.print(" oBAT:"); Serial.print(datadat[4]); Serial.print(" oCH: "); Serial.println(datadat[1],DEC);
  display.clearDisplay();   display.setTextSize(3); 
  display.setCursor(0,0);   display.print(datadat[2]);     if (datadat[2]>-10) display.print(char(247));
  display.setCursor(0,27);  display.print(datadat[5]);     display.print(char(247));
  display.setTextSize(1); 
  display.setCursor(65,0);  display.print(datadat[3]);     display.print("%");
  display.setCursor(53,13); display.print(datadat[7]+700); display.print("mm");
  display.setCursor(65,27); display.print(datadat[6]);     display.print("%");
  display.setCursor(63,40); if (int(datadat[4]) != 1 )     display.print(char(31)); //батарея разряжена
  display.setCursor(77,40); display.print(char(datadat[1])); //сердечко - свой канал

if (radio.write(&datadat, sizeof(datadat))) Serial.print ("Send");
  else  {Serial.print ("NoSend");  display.setCursor(70,40);  display.print(char(18));} //стрелочки - передача не удалась

display.display();

}  
////////////////////////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Извлекает из тактовой последовательности - битовую
//Параметры: cdptr - указатель на записанную тактовую последовательность
////////////////////////////////////////////////////////////////////////////////////////////////////
void get_bits(byte* cdptr){
  
//Сброс массивов
  byte* cdp=cdptr;
  for(int bt=0 ; bt<READ_BITS*2; bt++) decode_tacts[bt]=2;
      
        
  for(int bt=0 ; bt<READ_BITS*2; bt++){
        
    if ((*cdp&0xf0)>0x20 && (*cdp&0x0f)>0x04) decode_tacts[bt]=1;
    if ((*cdp&0xf0)<0x30 && (*cdp&0x0f)<0x05) decode_tacts[bt]=0;
    if ((*cdp&0xf0)<0x20 && (*cdp&0x0f)>0x04) decode_tacts[bt]=4;
    if ((*cdp&0xf0)>0x40 && (*cdp&0x0f)<0x02) decode_tacts[bt]=3;
    *cdp++;
  }
  return;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Извлекает из записи канала тактовую последовательность
//Параметры: cdptr - указатель на записанные данные
// btt - смещение в тактах. Смещение на такт при анализе может поммочь восстановить пакет, у которого разрушено начало
// Функция вовзращает качество или "годность" расшифровки - количесвто уверенно узнаных тактов.
// Сравнивая годность с btt=0 и btt=1 выбираем лучшую
////////////////////////////////////////////////////////////////////////////////////////////////////
int get_data(int btt, byte* cdptr){ //btt - смещение на такт при анализе может поммочь восстановить пакет, у которого разрушено начало
  
  byte* cdp=cdptr;
  for(int bt=0 ; bt<READ_BITS; bt++){
    *cdp=128;
    cdp++;
  }
  cdp=cdptr;
  *cdp=(128+2);
  cdp++;
  int packet_validity=0;
  for(int bt=1 ; bt<READ_BITS; bt++){
    if(decode_tacts[bt*2-btt]==0) *cdp-=1;
    if(decode_tacts[bt*2-btt]==1) *cdp+=1;
    if(decode_tacts[bt*2-2-btt]==1 && decode_tacts[bt*2-1-btt]==4) *cdp-=1;
    if(decode_tacts[bt*2-2-btt]==0 && decode_tacts[bt*2-1-btt]==3) *cdp+=1;
    if(decode_tacts[bt*2-2-btt]==0 && decode_tacts[bt*2-1-btt]==1) *cdp-=1;
    if(decode_tacts[bt*2-2-btt]==1 && decode_tacts[bt*2-1-btt]==0) *cdp+=1;

    if(decode_tacts[bt*2+2-btt]==1 && decode_tacts[bt*2+1-btt]==3) *cdp-=1;
    if(decode_tacts[bt*2+2-btt]==0 && decode_tacts[bt*2+1-btt]==4) *cdp+=1;
    if(decode_tacts[bt*2+2-btt]==0 && decode_tacts[bt*2+1-btt]==1) *cdp-=1;
    if(decode_tacts[bt*2+2-btt]==1 && decode_tacts[bt*2+1-btt]==0) *cdp+=1;

        //Подсчитываем кол-во достоверных бит в пакете
    if (*cdp>(128+1) )  packet_validity+=*cdp-128;
    if (*cdp<(128-1)) packet_validity+=128-*cdp;
    cdp++;
  }
  return packet_validity;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Прослушивание канала с частотой дискретизации 16384Гц
////////////////////////////////////////////////////////////////////////////////////////////////////
void collect(byte* cdptr, byte* dtl){
  
  // В функцию передаётся уазатель на область памяти, куда сливать данные.
  *dtl=0;
  bool cdp_prev_null;
  byte* cdp=cdptr;
  byte nulls_found=0;
  unsigned long tmp_marker=last_premarker+PER_LENGTH/32; //
  byte bt2=0;
  *cdp=0x88; //Первые два такта - единицы. Мы же поймали импульс!
  cdp++;
  //Ждём момента наступления первого считывания (конец последнего импулься зацепки + 1/16 такта)
  while (micros() <= tmp_marker);
  //Начинаем читать данные в память!
  for(int bt=1 ; bt<READ_BITS*2; bt++){// чИТАЕМ максимум ПО 90 БИТ (ПОСЫЛКА thgn - 96БИТ, THN - 76 бИТ + как минимум 3 бита 111, которые мы уже нашли)
    *cdp=0;
    for(byte ckl=0; ckl<8; ckl++){      // Читаем 8 раз за полутакт
      if (digitalRead(MHZ)) *cdp+=0x10; // Измерения запиываем в старший полубайт
      tmp_marker+=PER_LENGTH/16;
      while (micros() < tmp_marker);
    }
    last_premarker+=PER_LENGTH/2;
    tmp_marker=last_premarker+PER_LENGTH/32;
    
    for(byte ckl=0; ckl<8; ckl++){
      if (digitalRead(MHZ)) *cdp+=1;    // В следующий полутакт измерения запиываем в младший  полубайт. Это экономит память.
      tmp_marker+=PER_LENGTH/16;
      while (micros() < tmp_marker);
    }
    last_premarker+=PER_LENGTH/2;
    bt2++;
    //  Каждые 8 тактов добавлять 5мкс для выравнивания периода с 976мкс до 976.56мкс
    if (bt2==8){ 
      last_premarker+=5;
      bt2=0;
    }
    tmp_marker=last_premarker+PER_LENGTH/32;

    while (micros() < last_premarker);//Ждём прихода времени следующего полутакта
    if (*cdp==0 && cdp_prev_null==1) nulls_found++;                    
    //if (*cdp==0 && (*(cdp-1)==0)) nulls_found++;
    else nulls_found=0;
    
    if (nulls_found>1) return; //Если найдено более 7 нулевых тактов подряд, значит либо жёсткая помеха, убившая пакет
                                  //Либо конец посылки. Дальше читать нет смысла.

    cdp_prev_null= (*cdp==0) ? 1 : 0;
    cdp++;                              
    *dtl+=1;
  }
  return;
} 
////////////////////////////////////////////////////////////////////////////////////////////////////
//Определение смещения пакетов друг относительно друга
//В качестве параметров передаются указатели на массивы данных
// Возваращаяется смещение
// >0 - второй пакет начался раньше, <0 - Первый пакет начался раньше
////////////////////////////////////////////////////////////////////////////////////////////////////
int correlate_data(byte* ser1, byte* ser2){
  
  byte best_correl=0;
  int best_shift=0;
  byte best_correl_back=0;
  int best_shift_back=0;
  byte shift_score[READ_BITS];
  byte* s1;
  byte* s2;
  byte* s2t=ser2;
  //смещаем первый пакет относительно второго
  for( byte sht=0; sht<READ_BITS; sht++){
    s1=ser1;
    s2=s2t;
    shift_score[sht]=0;
    for(byte sp=0; sp<READ_BITS-sht; sp++){
      if ((*s1 >(128+1) && *s2 >(128+1))||(*s1 <(128-1) && *s2 <(128-1)) ) shift_score[sht]++;
      s2++;
      s1++;
    }
    s2t++;
  }

  for (int i=0; i<READ_BITS; i++){
    if (shift_score[i]>best_correl){
      best_correl=shift_score[i];
      best_shift=i;
    }
  }
 
//Теперь наоборот -втрой пакет относительно первого
  
  byte* s1t=ser1;
  for( byte sht=0; sht<READ_BITS; sht++){
    s2=ser2;
    s1=s1t;
    shift_score[sht]=0;
    for(byte sp=0; sp<READ_BITS-sht; sp++){
      if ((*s1 >(128+1) && *s2 >(128+1))||(*s1 <(128-1) && *s2 <(128-1)) ) shift_score[sht]++;
      s2++;
      s1++;
    }
    s1t++;
  }
// Ищем наилучшее совпадение для обоих вариантов 
  
  
  for (int i=0; i<READ_BITS; i++){
    if (shift_score[i]>best_correl_back){
      best_correl_back=shift_score[i];
      best_shift_back=i;
    }
  }
  //И возвращаем самое лучшее из двух
  if (best_correl_back > best_correl) return -best_shift_back;
  else return best_shift;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Сборка из двух пакетов
//В качестве параметров передаются указатели на массивы данных
// Причём первым должен идти результирующий пакет, т.е. тот который имеет более длинную преамбулу.
//shift - смещение втрого пакета относительного первого
////////////////////////////////////////////////////////////////////////////////////////////////////
void assemble_data(byte* s1, byte* s2, int* shift){
  
  for (int g=0; g<=*shift-1; g++) s1++;
  for (int i=0; i<(READ_BITS-*shift); i++){
      if(*s1 < (128+2) && *s1 > (128-2) && (*s2>(128+1) || *s2<(128-1))) {
      *s1=*s2;
    }
    s1++;
    s2++;
  }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Создаёт кодовую посылку
//code - указатель на расшифрованную битовую последовательность
//result - указатель на кодовую посылку
//valid - указатель на карту достоверности кодовой посылки
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_info_data(byte* code, byte* result, byte* valid){
  
  byte* rd=result;
  byte* vd=valid;
  //Чистим массивы
  for (int l=0; l<PACKET_LENGTH; l++){
    *vd=0;
    *rd=0;
    vd++;
    rd++;
  }
  int csm;
  for (csm=0; csm<30;csm++){
    if (*code<128 && *(code+1)>128 && *(code+2)<128 && *(code+3)>128) break;//Найдена последовательность 0101 
    code++;
  }
  if (csm>22) return 0;// Стартовая последовательность в первых 20 битах не найдена, такой пакет этим методом не расшифруешь
  
  code+=4;//Переходим на начало считывания
  int ii=0;
  for (int i=0; i<READ_BITS-csm; i++) 
  {
    byte multipl;
    switch (ii){
      case 0: {multipl=0x01; break;}
      case 1: {multipl=0x02; break;}
      case 2: {multipl=0x04; break;}
      case 3: {multipl=0x08; break;}
    }
    if (*code==129 ) *result+=multipl;
    if (*code>129 ) {
      *result+=multipl;
      *valid+=multipl;
    }
    if (*code<127 ) *valid+=multipl;
    code++;
    ii++;
    if (ii==4) {
      ii=0;
      valid++;
      result++;
    }
  }
  return 1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает значение температуры
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
float get_temperature(byte* oregon_data){
  
  float tmprt;
  oregon_data+=8;
  //исправляем возможные ошибки:
  for (int g=0;g<4; g++)  if (*(oregon_data+g)>9) *(oregon_data+g)=*(oregon_data+g)-8;
  tmprt+=*(oregon_data)*0.1;
  tmprt+=*(oregon_data+1);
  tmprt+=*(oregon_data+2)*10;
  return (*(oregon_data+3)) ? -tmprt : tmprt;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает значение маски годности
//validity_data - указатель на карту достоверности кодовой посылки
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_temperature_mask(byte* validity_data){
  
    return (*(validity_data+8)==0x0f && *(validity_data+9)==0x0f && *(validity_data+10)==0x0f && *(validity_data+11)==0x0f) ? 1 : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает тип сенсора
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
word get_sensor(byte* oregon_data){
  
    return (word)(*(oregon_data))*0x1000 + (*(oregon_data+1))*0x0100 + (*(oregon_data+2))*0x10 + *(oregon_data+3);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает значение канала
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_channel(byte* oregon_data){
  
  byte channel;  
  switch (*(oregon_data+4))  
  {  
  case 1:  
    channel = 1;  
    break;  
  case 2:  
    channel = 2;  
    break;  
  case 4:  
    channel = 3;  
    break;  
  }  
  return channel;  
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_battery(byte* oregon_data){

  return (*(oregon_data+7) & 0x4) ? 0 : 1;  
  
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает значение влажности
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_humidity(byte* oregon_data){
  
  byte tmprt;
  oregon_data+=12;
  //исправляем возможные ошибки:
  for (int g=0;g<2; g++)  if (*(oregon_data+g)>9) *(oregon_data+g)=*(oregon_data+g)-8;
  tmprt=*(oregon_data);
  tmprt+=*(oregon_data+1)*10;
  return tmprt;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает значение маски годности влажности 4 бита, старший - знак, младший -  десятые доли градуса
//validity_data - указатель на карту достоверности кодовой посылки
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_humidity_mask(byte* validity_data){
  
 return (*(validity_data+13)==0x0f && *(validity_data+14)==0x0f) ? 1 : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//Возвращает id датчика
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
byte get_id(byte* oregon_data){
  
  byte tmprt;
  oregon_data+=5;
  tmprt=*(oregon_data)*0x10;
  tmprt+=*(oregon_data+1);
  return tmprt;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//Проверка CRC
//oregon_data - указатель на кодовую посылку
////////////////////////////////////////////////////////////////////////////////////////////////////
bool check_CRC(byte* oregon_data, word sensor_type){
  
  byte* pp=oregon_data;
  byte crc, resived_crc;
  crc=0;
  if (sensor_type==THGN132){
    for(int x=0; x<15; x++){
      crc+=*pp;
      pp++;  
    }
    resived_crc=(*(oregon_data+15))+(*(oregon_data+16))*0x10;
   return (resived_crc==crc)? 1 : 0;
  }

 /* if (sensor_type==THN132){
   for(int x=0; x<12; x++){
      crc+=*pp;
      pp++;  
    }
    resived_crc=(*(oregon_data+12))+(*(oregon_data+13))*0x10;
    return (resived_crc==crc)? 1 : 0;
  }*/
  return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//Востановление данных по типу датчика
////////////////////////////////////////////////////////////////////////////////////////////////////
void restore_data(byte* oregon_data, word sensor_type){
  
  byte* pp=oregon_data;
  if (sensor_type==THGN132){
    pp+=8;
    for(int x=0; x<6; x++){
      if(*pp>9 && x!=3) *pp-=8;
      pp++;  
    }
  }
  return;
}

 

snickser
Offline
Зарегистрирован: 02.07.2016

udavst,

Приёмник у вас отличный, вам бы антенну нормальную, думаю слышимость бы стала лучше, хотя кто знает... Но лично у меня с менее чувствительным приёмником но нормальной антенной (пружинка такая, на али продаются для 433 приёмников) ардуина слышит датчик в таком месте, где базовые станции ничего не принимают. Собственно там ретранслятор я и сделал, он принимает уличный сигнал и дублирует его в эфир с другим ID ;)

А ваш кусок провода может просто ловить кучу помех, код Porosenok вроде обещал помехозащиту, может она и срабатывает, или вытягивает слишком слабый сигнал. Потому как в коде с #230 стоит контроль чексуммы и выводятся только правильные пакеты, естественно битые он пропускает мимо.

Porosenok
Offline
Зарегистрирован: 06.11.2016

udavst пишет:

Можно убрать массивы (температура, влажность...) с каналами (если у меня 1 датчик, зачем мне все значения для всех каналов)?, ну и ещё как-то уменьшить использование памяти, убрать ненужные счётчики и переменные (ведь требуется только температура, влажность, канал, батарея, все эти сырые данные и тайминги обработки - зачем?

Я для того и перевёл свой код в библиотеку, чтобы вы могли убрать всё лишнее. Кстати и компилятор стал теперь говорить, что свободной памяти стало процентов на 10 больше. Хотя не пойму, с чего бы это... :)

Да, при возможности попробую соптимизировать библиотеку. чтобы ела меньше памяти. Вообще там ещё есть над чем поработать, но идей пока нету...

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

snickser - антенну надо намотать, согласен, я в командировке, и так ограниченный набор с собой, проволоки нету подходящей, и не продаётся тут ничего, совсем ничего, делаю головой и паяльником ) но кусок провода поймает волну, я бы сказал, что штыревая ловит лучше спирали, но она должна быть штыревая, а не на полшестого, а там места столько нет ( А китайцев месяц ждать, и не факт, что я в другой командировке не окажусь, и они обнаглели.

Porosenok - эх этот форум без шапки ( я краем глаза видел, но теперь опять всё перелистывать, чтобы найти код в виде бибилиотэки.

ЗЫ и всё же я не понимаю, ведь приёмник ловит (причём каждую посылку), помехозащита (ну проверка контрольной суммы) она и там и там, почему такая разница? Мне кажется, что первый код, видя неверную контрольную останавливается и пропускает вторую, код Porosenok, смотрит все посылки, а вторая как раз полная. Я не программер, решать Вам. Просто первый код занимает столько памяти, сколько остаётся свободной после второго )

snickser
Offline
Зарегистрирован: 02.07.2016

Ну вы просто не нашли вот такое https://ru.aliexpress.com/item/5pcs-433MHz-Helical-antenna/32716658013.html?spm=2114.10010208.1000014.9.d0Vr9f&scm=1007.13338.50051.0&pvid=594ceed1-ec84-4aa2-97d0-89a0d24c0a04&tpp=1

:)

udavst пишет:

 и всё же я не понимаю, ведь приёмник ловит (причём каждую посылку), помехозащита (ну проверка контрольной суммы) она и там и там, почему такая разница?

Нет, всё не так, контрольная сумма - это просто число в самой посылке, его проверяют уже после успешного приёма всего пакета. А могут и не проверять (в оригинальном коде нет проверки кстати). А вот декодировка нулей и единиц из эфира может не случится. Ведь ноль по факту передаётся как отсутствие сигнала, но никто не гарантирует что в тот самый момент не появится в эфире левый сигнал (шум). У Porosenok код работает по иному, и имеет помехозащиту судя по всему. В этом разница.

 

snickser
Offline
Зарегистрирован: 02.07.2016

Поковырял немного код приёмника, изучил как он работает, понял что его можно чуток упростить. Что в свою очередь даст более стабильный приём.

Вообще код конечно элементарный до невозможности, в этом то его и прелесть )))

Он вначале ищет 24 длинных импульса в эфире (а тот что в первом посте так и вообще 32 ждёт), вот тут мне кажется и есть некоторая проблема, если в этот момент приходит помеха (короткий импульс) - кранты всему приёму. Я лично у себя это число уменьшил до 5, хотя и 3 тоже будет работать, да даже 1 достаточно, просто будет много ложных срабатываний которые ни к чему не обязывают.

                  }
                  else if (w == 0 && 5 <= flip) 
                  {

Ещё можно ожидание финального посыла уменьшить с 2500 до 1500 микросекунд, смысла долго ждать пустой эфир тоже нет.

         } 
         else if (width >= 1500  && pos >= 8) 
         {

Ещё странный эффект достигается если уменьшить время короткого импульса с 200 до 100. У меня при этом начинает "глуховатый" датчик ловиться стабильнее.

     {
         if (100 <= width && width < 1200) 
         {

В общем думается, что у кого этот код плохо ловит датчики - много помех в эфире, слабая антенна, или всё сразу. Буду думать как к нему прикрутить помехозащиту...  )

Код выводит восклицательные знаки если очередное время приёма профукано. Рекомендую оставить на пару часиков понаблюдать за эфиром, может у вас не всё так плохо как кажется...

// RF Receiver


#define PORT 2

unsigned long upd[8];

class DecodeOOK {
protected:
    byte total_bits, bits, flip, state, pos, data[12];

    virtual char decode (word width) =0;
    
public:

    enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };

    DecodeOOK () { resetDecoder(); }

    bool nextPulse (word width) {
       if (state != DONE)
    
            switch (decode(width)) {
                case -1: resetDecoder(); break;
                case 1:  done(); break;
            }
        return isDone();
    }
    
    bool isDone () const { return state == DONE; }

    const byte* getData (byte& count) const {
        count = pos;
        return data; 
    }
    
    void resetDecoder () {
        total_bits = bits = pos = flip = 0;
        state = UNKNOWN;
    }
    
    // add one bit to the packet data buffer
 
    virtual void gotBit (char value) {
        total_bits++;
        byte *ptr = data + pos;
        *ptr = (*ptr >> 1) | (value << 7);

        if (++bits >= 8) {
            bits = 0;
            if (++pos >= sizeof data) {
                resetDecoder();
                return;
            }
        }
        state = OK;
    }
    
    // store a bit using Manchester encoding
    void manchester (char value) {
        flip ^= value; // manchester code, long pulse flips the bit
        gotBit(flip);
    }
    
    // move bits to the front so that all the bits are aligned to the end
    void alignTail (byte max =0) {
        // align bits
        if (bits != 0) {
            data[pos] >>= 8 - bits;
            for (byte i = 0; i < pos; ++i)
                data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
            bits = 0;
        }
        // optionally shift bytes down if there are too many of 'em
        if (max > 0 && pos > max) {
            byte n = pos - max;
            pos = max;
            for (byte i = 0; i < pos; ++i)
                data[i] = data[i+n];
        }
    }
    
    void reverseBits () {
        for (byte i = 0; i < pos; ++i) {
            byte b = data[i];
            for (byte j = 0; j < 8; ++j) {
                data[i] = (data[i] << 1) | (b & 1);
                b >>= 1;
            }
        }
    }
    
    void reverseNibbles () {
        for (byte i = 0; i < pos; ++i)
            data[i] = (data[i] << 4) | (data[i] >> 4);
    }
    
    void done () {
        while (bits)
            gotBit(0); // padding
        state = DONE;
    }
};

// 433 MHz decoders



class OregonDecoderV2 : public DecodeOOK 
{
   public:  
  OregonDecoderV2() {}

      // add one bit to the packet data buffer
  virtual void gotBit (char value) 
      {
         if(!(total_bits & 0x01))
         {
            data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
         }

         total_bits++;
         pos = total_bits >> 4;

         if (pos >= sizeof data) 
         {
            resetDecoder();
            return;
         }
         state = OK;
      }

  virtual char decode(word width) 
      {
         if (100 <= width && width < 1200) 
         {
            byte w = width >= 700;
    
            switch (state) 
            {
               case UNKNOWN:
               
                  if (w != 0) 
                  {
                     // Long pulse
                     ++flip;
                  } 
                  else if (w == 0 && 5 <= flip) 
                  {
                     // Short pulse, start bit
                     flip = 0;
                     state = T0;
                  } 
                  else 
                  {
                     // Reset decoder
                     return -1;
                  }
                  break;
               case OK:
    
                  if (w == 0) 
                  {
                     // Short pulse
                     state = T0;
                  }
                  else 
                  {
                     // Long pulse
                     manchester(1);
                  }
                  break;
              case T0:
                  if (w == 0) 
                  {
                     // Second short pulse
                     manchester(0);
                  } 
                  else 
                  {
                     // Reset decoder
                     return -1;
                  }
               break;
            }
         } 
         else if (width >= 1500  && pos >= 8) 
         {
            return 1;
         } 
         else 
         {
            return -1;
         }
       return 0;
     }
};

OregonDecoderV2 orscV2;

volatile word pulse;

void ext_int_1(void) {
    static word last;
    // determine the pulse length in microseconds, for either polarity
    pulse = micros() - last;
    last += pulse;
}

//
// Oregon packet decoder
//

float temperature(const byte* data)
{
   int sign = (data[6]&0x8) ? -1 : 1;
   float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
   return sign * temp;
}

byte humidity(const byte* data)
{
   return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4) ;
}

float humi_ext(const byte* data)
{
   return  humidity(data) + ((data[7]&0xF0)>>4)/10.0 ;
}

byte battery(const byte* data)
{
   return (data[4] & 0xF) ;
}

byte serial(const byte* data)
{
   return (data[3]);
}

byte channel(const byte* data)
{
   byte channel;
   switch (data[2]>>4)
   {
      case 0x1:
         channel = 1;
         break;
      case 0x2:
         channel = 2;
         break;
      case 0x3:
         channel = 3;
         break;
      case 0x4:
         if((data[2]&0xF)==3){
          channel = 4;
         } else {
          channel = 3;
         } 
         break;
      case 0x5:
         channel = 5;
         break;
      case 0x6:
         channel = 6;
         break;
      case 0x7:
         channel = 7;
         break;
   }
 return channel;
} 

int Sum(byte count, const byte* data)
{
  int s = 0;
 
  for(byte i = 0; i<count;i++)
  {
    s += (data[i]&0xF0) >> 4;
    s += (data[i]&0xF);
  }
 
  if(int(count) != count)
    s += (data[count]&0xF0) >> 4;
 
  return s;
}


void reportSerial (const char* s, class DecodeOOK& decoder) {
    byte pos;
    const byte* data = decoder.getData(pos);

    Serial.print(s);
    Serial.print(' ');
    for (byte i = 0; i < pos; ++i) {
        Serial.print(data[i] >> 4, HEX);
        Serial.print(data[i] & 0x0F, HEX);
    }

    if( pos>8 ){
      if( data[8] == (Sum(8,data)-0xa)&0xFF ){
        Serial.print(" ");
        unsigned long m = millis();
        if ( (m-upd[channel(data)]) > 50000 ){
          Serial.print("!!!!! ");
        }
        Serial.print((m-upd[channel(data)])/1000.,3);
        upd[channel(data)]=m;
        Serial.print(" "+String(channel(data))+" "+String(serial(data),HEX));
        Serial.print(" "+String(temperature(data),1)+" "+String(humidity(data))+"%");
        Serial.print(" "+String(battery(data)));
      } else {
        Serial.print(" Checksum error");
      }
    } else if( pos>6 ) {
      int sum = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);
      if( (sum&0xF) == (data[6]>>4) && (sum>>4) == (data[7]&0xF) ){
        Serial.print(" "+String(channel(data))+" "+String(serial(data),HEX));
        Serial.print(" "+String(temperature(data),1));
        Serial.print(" "+String(battery(data)));
      } else {
        Serial.print(" Checksum error");
      }
    }
    Serial.println();

    decoder.resetDecoder();
}




void setup () {
    Serial.begin(9600);
    Serial.println("\n[ookDecoder]");
    
    pinMode(PORT, INPUT);  // use the AIO pin

  attachInterrupt(digitalPinToInterrupt(PORT), ext_int_1, CHANGE);

}

void loop () {
    
    cli();
    word p = pulse;
    pulse = 0;
    sei();
  
    if (p != 0) {
        if (orscV2.nextPulse(p))
            reportSerial("OSV2", orscV2);  
      }
}


 

Sprite
Offline
Зарегистрирован: 30.10.2016

snickser пишет:

Поковырял немного код приёмника, изучил как он работает, понял что его можно чуток упростить. Что в свою очередь даст более стабильный приём.

Этот код отлично ловит мой датчик THGN122N, а вот датчик RTGN318 немного похуже.

А вот предыдущая версия кода, которую вы правили для меня, наоборот RTGN318 ловит хорошо, а с THGN122N бывают проблемы.

Но могу сказать однозначно, что этот код ловит датчики лучше, чем предыдущий.

Каждую минуту приходят показания с обоих датчиков.

Спасибо вам!

 
snickser
Offline
Зарегистрирован: 02.07.2016

В общем, мне удалось найти причину сбоя вначале и конце передачи. И как не странно, я ещё ни разу не смог получить ошибку в процессе передачи данных, что несколько досадно. Думаю у других будет тоже самое...

Глючный датчик мой в произвольный момент времени начинает в самом конце передачи там где должен быть финальный пилот-тон, вставлять микроимпульст неизвестного происхождения. Что напрочь рушит весь алгоритм приёма, хотя все данные по факту уже приняты.

Тупо избавился от условия

        } 
         else if ( width >= 2500 && pos >= 8 ) 
         {

убрав из него проверку финального тона

         } 
         else if ( pos >= 8 ) 
         {

Собственно уменьшение с 200 до 100 времени минимального пульса делало то же самое, как оказалось. Но лично мне кажется проще убрать условия в конце, чем уменьшать чувствительность пульса, дабы не дёргать лишний раз код на всякий чих в эфире. Хотя катастрофы в этом нет, просто оптимизация... можно хоть 0 там поставить ;)

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

snickser пишет:

Глючный датчик мой в произвольный момент времени начинает в самом конце передачи там где должен быть финальный пилот-тон, вставлять микроимпульст неизвестного происхождения.

Не, ну так не пойдёт. Для ардуинщика вашего уровня непростительно оставлять в программе "импульсы неизвестного происхождения". :) Заталкивание вылезшего камня обратно в землю - удел неофитов.

snickser
Offline
Зарегистрирован: 02.07.2016

Да ну его, он и так уже логигу поломал, проще его незаметить и пойти дальше... ))) типа так

         } 
         else if ( ( width <= 200 || width >= 1200) && pos >= 8 ) 
         {

Есть более интересная проблема, датчик выводит два разных пакета.

begin
____________||_||_||_||___||_____||_||_||___||_||_||___________||_||_______||_____||___||___||_______||___||___||_||_||_||_______________||_||_||___||_||_||_||_||___________||___||_____||_||_||_____||___||___||#
OSV2 1A2D10CE3005680546 38.442 1 ce -5.3 56% 0
begin
__________||_||_||_||___||_____||_||_||___||_||_||___________||_||_______||_____||___||___||___||_||_||___||___||_||_||_||_______________||_||_||___||_||_||_||_||___________||_||_||_||___||_||_____||___________#
OSV2 1A2D10CE340568054AFC 0.310 1 ce -5.3 56% 4

:) без CRC и батарейки.

snickser
Offline
Зарегистрирован: 02.07.2016

Ребяты, ни в коем случае не заказывайте радиомодуль указанный в первом посте, это срань несусветная, даже с хорошей антенной из него много мусора валится. Используйте нормальные, с супергетеродином!...

Sprite
Offline
Зарегистрирован: 30.10.2016

Поддерживаю!
Такие работают намного лучше.

http://s.aliexpress.com/3INviANN

http://s.aliexpress.com/eMrYR7nq

Porosenok
Offline
Зарегистрирован: 06.11.2016

Зато передатчик из первого поста по опыту самый мощный.

Sprite
Offline
Зарегистрирован: 30.10.2016

Porosenok пишет:

Зато передатчик из первого поста по опыту самый мощный.

Этот передатчик слабее?

http://s.aliexpress.com/3INviANN

Sprite
Offline
Зарегистрирован: 30.10.2016

Прошу совета.

Записываю данные с датчиков на SD с помощью W5100.

Сейчас всё пишется в один файл.

Подскажите, как мне реализовать создание нового txt файла каждый час и запись в него данных?

Porosenok
Offline
Зарегистрирован: 06.11.2016

Sprite пишет:

Этот передатчик слабее?

http://s.aliexpress.com/3INviANN

Мне показался заметно слабее

snickser
Offline
Зарегистрирован: 02.07.2016

Sprite пишет:

Этот передатчик слабее?

http://s.aliexpress.com/3INviANN

Конечно, ведь он от 3.3В работает, а в первый можно до 12В подать... )) Хотя у меня он успешно через всю квартиру простреливает.

И кстати там есть ещё одна бага, ножка EN закорочена на питание, он всегда потребляет электричество, что плохо если работать на батарейках. Но можно отпаять резистор и тогда всё становится как надо )))

 

Sprite
Offline
Зарегистрирован: 30.10.2016

Подскажите, использую код, чтобы записывать показания с датчиков в .txt файл. Хотелось бы каждый час начинать писать в новый файл. Хоть убей, не понимаю как и куда  добавить к названию следующего файла счётчик "+1".

Извиняюсь за оффтоп, но похожей информации по записи на SD не нашёл.

Краткий пример:

#include <SD.h>

File myfile;

unsigned long close_file = 60000, close_interval = 60000;

void setup () {


pinMode(53, OUTPUT);

if (!SD.begin(4)) {
    Serial.println("initialization of the SD card failed!");
    }
    else {
    Serial.println("initialization of the SDcard is done.");
    myfile = SD.open("oregon.txt", FILE_WRITE);
    Serial.println("oregon.txt - OPEN");
    myfile.print("oregon.txt - OPEN");     
    }

void loop () {

 if (millis() > close_file) {
      
      myfile.print("oregon.txt - CLOSE");
      myfile.close();
      Serial.print("oregon.txt - CLOSE");

      close_file = millis() + close_interval;

      myfile = SD.open("oregon.txt", FILE_WRITE);
      Serial.println("oregon.txt - OPEN_AGAIN");
      myfile.print("oregon.txt - OPEN_AGAIN");
}



Полный код:

// RF Receiver
// ATmega 2560
// Приёмник подключается к PWM(2)
// "LCD 2004", "DS3231": SCL -> SCL(21); SDA -> SDA(20)

#include <iarduino_RTC.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <SD.h>

#define PORT 2
#define printByte(args) write(args);

File myfile;
unsigned long obnov_clock = 0, clock_interval = 1000;
unsigned long close_file = 60000, close_interval = 60000;

iarduino_RTC time(RTC_DS3231);
LiquidCrystal_I2C lcd(0x27,20,4);  // Адрес моего "LCD 2004" 0x27



uint8_t cels[8]  = {0x2,0x5,0x5,0x2};                         // Значёк цельсия
uint8_t batlow[8]  = {0xE,0x1f,0x0,0x11,0x0,0x11,0x0,0x1f};   // Значок пустой батарейки
uint8_t ygol[8] = {0x0,0x8,0x4,0x2,0x4,0x8,0x0,0x0};          // Значок ">"
uint8_t termom[8] = {0x4,0xA,0xA,0xE,0xE,0x1f,0x1f,0xE};      // Значок термометра
uint8_t kapla[8] = {0x4,0x4,0xA,0xA,0x11,0x11,0x11,0xE};      // Значок капли
uint8_t pe[8] = {0x1f,0x11,0x11,0x11,0x11,0x11,0x11,0x0};     // Буква П
uint8_t che[8] = {0x11,0x11,0x11,0x11,0xf,0x1,0x1,0x0};       // Буква Ч
uint8_t be[8] = {0x1f,0x10,0x10,0x1E,0x11,0x11,0x1E,0x0};     // Буква Б


unsigned long upd[8];

class DecodeOOK {
protected:
    byte total_bits, bits, flip, state, pos, data[12];

    virtual char decode (word width) =0;
    
public:

    enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };

    DecodeOOK () { resetDecoder(); }

    bool nextPulse (word width) {
       if (state != DONE)
    
            switch (decode(width)) {
                case -1: resetDecoder(); break;
                case 1:  done(); break;
            }
        return isDone();
    }
    
    bool isDone () const { return state == DONE; }

    const byte* getData (byte& count) const {
        count = pos;
        return data; 
    }
    
    void resetDecoder () {
        total_bits = bits = pos = flip = 0;
        state = UNKNOWN;
    }
    
    // add one bit to the packet data buffer
 
    virtual void gotBit (char value) {
        total_bits++;
        byte *ptr = data + pos;
        *ptr = (*ptr >> 1) | (value << 7);

        if (++bits >= 8) {
            bits = 0;
            if (++pos >= sizeof data) {
                resetDecoder();
                return;
            }
        }
        state = OK;
    }
    
    // store a bit using Manchester encoding
    void manchester (char value) {
        flip ^= value; // manchester code, long pulse flips the bit
        gotBit(flip);
    }
    
    // move bits to the front so that all the bits are aligned to the end
    void alignTail (byte max =0) {
        // align bits
        if (bits != 0) {
            data[pos] >>= 8 - bits;
            for (byte i = 0; i < pos; ++i)
                data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
            bits = 0;
        }
        // optionally shift bytes down if there are too many of 'em
        if (max > 0 && pos > max) {
            byte n = pos - max;
            pos = max;
            for (byte i = 0; i < pos; ++i)
                data[i] = data[i+n];
        }
    }
    
    void reverseBits () {
        for (byte i = 0; i < pos; ++i) {
            byte b = data[i];
            for (byte j = 0; j < 8; ++j) {
                data[i] = (data[i] << 1) | (b & 1);
                b >>= 1;
            }
        }
    }
    
    void reverseNibbles () {
        for (byte i = 0; i < pos; ++i)
            data[i] = (data[i] << 4) | (data[i] >> 4);
    }
    
    void done () {
        while (bits)
            gotBit(0); // padding
        state = DONE;
    }
};

// 433 MHz decoders



class OregonDecoderV2 : public DecodeOOK 
{
   public:  
  OregonDecoderV2() {}

      // add one bit to the packet data buffer
  virtual void gotBit (char value) 
      {
         if(!(total_bits & 0x01))
         {
            data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
         }

         total_bits++;
         pos = total_bits >> 4;

         if (pos >= sizeof data) 
         {
            resetDecoder();
            return;
         }
         state = OK;
      }

  virtual char decode(word width) 
      {
         if (100 <= width && width < 1200) 
         {
            byte w = width >= 700;
    
            switch (state) 
            {
               case UNKNOWN:
               
                  if (w != 0) 
                  {
                     // Long pulse
                     ++flip;
                  } 
                  else if (w == 0 && 5 <= flip) 
                  {
                     // Short pulse, start bit
                     flip = 0;
                     state = T0;
                  } 
                  else 
                  {
                     // Reset decoder
                     return -1;
                  }
                  break;
               case OK:
    
                  if (w == 0) 
                  {
                     // Short pulse
                     state = T0;
                  }
                  else 
                  {
                     // Long pulse
                     manchester(1);
                  }
                  break;
              case T0:
                  if (w == 0) 
                  {
                     // Second short pulse
                     manchester(0);
                  } 
                  else 
                  {
                     // Reset decoder
                     return -1;
                  }
               break;
            }
         } 
         else if (width >= 1500  && pos >= 8) 
         {
            return 1;
         } 
         else 
         {
            return -1;
         }
       return 0;
     }
};

OregonDecoderV2 orscV2;

volatile word pulse;

void ext_int_1(void) {
    static word last;
    // determine the pulse length in microseconds, for either polarity
    pulse = micros() - last;
    last += pulse;
}

//
// Oregon packet decoder
//

float temperature(const byte* data)
{
   int sign = (data[6]&0x8) ? -1 : 1;
   float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
   return sign * temp;
}

byte humidity(const byte* data)
{
   return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4) ;
}

float humi_ext(const byte* data)
{
   return  humidity(data) + ((data[7]&0xF0)>>4)/10.0 ;
}

byte battery(const byte* data)
{
   return (data[4] & 0xF) ;
}

byte serial(const byte* data)
{
   return (data[3]);
}

byte channel(const byte* data)
{
   byte channel;
   switch (data[2]>>4)
   {
      case 0x1:
         channel = 1;
         break;
      case 0x2:
         channel = 2;
         break;
      case 0x3:
         channel = 3;
         break;
      case 0x4:
         if((data[2]&0xF)==3){
          channel = 4;
         } else {
          channel = 3;
         } 
         break;
      case 0x5:
         channel = 5;
         break;
      case 0x6:
         channel = 6;
         break;
      case 0x7:
         channel = 7;
         break;
   }
 return channel;
} 

int Sum(byte count, const byte* data)
{
  int s = 0;
 
  for(byte i = 0; i<count;i++)
  {
    s += (data[i]&0xF0) >> 4;
    s += (data[i]&0xF);
  }
 
  if(int(count) != count)
    s += (data[count]&0xF0) >> 4;
 
  return s;
}


void reportSerial (const char* s, class DecodeOOK& decoder) {
    byte pos;
    const byte* data = decoder.getData(pos);

    Serial.print(s);
    Serial.print(' ');
    for (byte i = 0; i < pos; ++i) {
        Serial.print(data[i] >> 4, HEX);
        Serial.print(data[i] & 0x0F, HEX);
    }

    if( pos>8 ){
      if( data[8] == (Sum(8,data)-0xa)&0xFF ){
        Serial.print(" ");
        unsigned long m = millis();
        if ( (m-upd[channel(data)]) > 50000 ){
          Serial.print("!!!!! ");
        }
        Serial.print((m-upd[channel(data)])/1000.,3);
        upd[channel(data)]=m;
        Serial.print(" "+String(channel(data))+" "+String(serial(data),HEX));
        Serial.print(" "+String(temperature(data),1)+" "+String(humidity(data))+"%");
        Serial.print(" "+String(battery(data)));
        myfile.println(time.gettime("d/m/y - H:i:s"));
        myfile.println("Ch. "+String(channel(data))+" T="+String(temperature(data),1)+"'С H="+String(humidity(data))+"%");
        } else {
        Serial.print(" Checksum error");
      }
    } else if( pos>6 ) {
      int sum = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);
      if( (sum&0xF) == (data[6]>>4) && (sum>>4) == (data[7]&0xF) ){
        Serial.print(" "+String(channel(data))+" "+String(serial(data),HEX));
        Serial.print(" "+String(temperature(data),1));
        Serial.print(" "+String(battery(data)));
        myfile.println(time.gettime("d/m/y-H:i:s"));
        myfile.println("Ch. "+String(channel(data))+" T="+String(temperature(data),1)+"'С ");
        } else {
        Serial.print(" Checksum error");
      }
    }
    Serial.println();

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
///////////////////  Вывод результата на LCD   /////////////////////
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
 
/// Датчик с влажностью

 if( data[8] == (Sum(8,data)-0xa)&0xFF )
 {
 if ((channel(data))==1|(channel(data))==3|(channel(data))==5)    // Канал датчика 1, 3, 5
  {
   lcd.setCursor(0,1); lcd.print("          ");
   lcd.setCursor(0,2); lcd.print("          ");
   lcd.setCursor(0,3); lcd.print("          ");
   lcd.setCursor(0,1); lcd.print("Ch"); lcd.print (channel(data)); 
   lcd.setCursor(0,2); lcd.printByte(3); 
   if (temperature(data) < 0)
   {
   lcd.setCursor(1,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   else
   { 
   lcd.setCursor(2,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   lcd.setCursor(0,3); lcd.printByte(4); lcd.print(" "); lcd.print(String(humidity(data))); lcd.print(" %");
   if( (battery(data)) == 0xC)
   {
    lcd.setCursor(8,3); lcd.printByte(1);
   }
   lcd.setCursor(3,1); lcd.printByte(2); lcd.print(time.gettime("H:i")); // выводим время
   lcd.setCursor(7,3); lcd.print (time.gettime("s")); // выводим время
  }
  else   // Канал датчика 2, 4, 6                                               
 {
   lcd.setCursor(10,1); lcd.print("          ");
   lcd.setCursor(10,2); lcd.print("          ");
   lcd.setCursor(10,3); lcd.print("          ");
   lcd.setCursor(11,1); lcd.print("Ch"); lcd.print (channel(data));
   lcd.setCursor(11,2); lcd.printByte(3); 
   if (temperature(data) < 0)
   {
   lcd.setCursor(12,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   else
   { 
   lcd.setCursor(13,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   lcd.setCursor(11,3); lcd.printByte(4); lcd.print(" "); lcd.print(String(humidity(data))); lcd.print(" %");
   if( (battery(data)) == 0xC)
   {
    lcd.setCursor(18,3); lcd.printByte(1);
   }
   lcd.setCursor(14,1); lcd.printByte(2); lcd.print(time.gettime("H:i")); // выводим время
   lcd.setCursor(18,3); lcd.print(time.gettime("s"));
 }
 }
/// Датчик без влажности      
 else
 {
  int sum = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);
  if( (sum&0xF) == (data[6]>>4) && (sum>>4) == (data[7]&0xF) )
  {
 if ((channel(data))==1|(channel(data))==3|(channel(data))==5)    // Канал датчика 1, 3, 5
  {
   lcd.setCursor(0,1); lcd.print("          ");
   lcd.setCursor(0,2); lcd.print("          ");
   lcd.setCursor(0,3); lcd.print("          ");
   lcd.setCursor(0,1); lcd.print("Ch"); lcd.print (channel(data)); 
   lcd.setCursor(0,2); lcd.printByte(3); 
   if (temperature(data) < 0)
   {
   lcd.setCursor(1,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   else
   { 
   lcd.setCursor(2,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   if( (battery(data)) == 0xC)
   {
    lcd.setCursor(8,3); lcd.printByte(1);
   }
   lcd.setCursor(3,1); lcd.printByte(2); lcd.print (time.gettime("H:i")); // выводим время
   lcd.setCursor(7,3); lcd.print (time.gettime("s")); // выводим время
  }
  else   // Канал датчика 2, 4, 6                                               
 {
   lcd.setCursor(10,1); lcd.print("          ");
   lcd.setCursor(10,2); lcd.print("          ");
   lcd.setCursor(10,3); lcd.print("          ");
   lcd.setCursor(11,1); lcd.print("Ch"); lcd.print (channel(data));
   lcd.setCursor(11,2); lcd.printByte(3); 
   if (temperature(data) < 0)
   {
   lcd.setCursor(12,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   else
   { 
   lcd.setCursor(13,2); lcd.print(String(temperature(data),1)); lcd.printByte(0); lcd.print("C");
   }
   if( (battery(data)) == 0xC)
   {
    lcd.setCursor(18,3); lcd.printByte(1);
   }
  lcd.setCursor(14,1); lcd.printByte(2); lcd.print(time.gettime("H:i")); // выводим время
   lcd.setCursor(18,3); lcd.print(time.gettime("s"));
 }
  }
 }   
 

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
////////////////  Конец вывод результата на LCD   //////////////////
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////







    decoder.resetDecoder();
}




void setup () {
    lcd.init();
    lcd.backlight();
    lcd.createChar(0, cels);
    lcd.createChar(1, batlow);
    lcd.createChar(2, ygol);
    lcd.createChar(3, termom);
    lcd.createChar(4, kapla);
    lcd.createChar(5, pe);
    lcd.createChar(6, che);
    lcd.createChar(7, be);

   
    
    Serial.begin(9600);
    time.begin();
    pinMode(53, OUTPUT);
    Serial.println("\n[ookDecoder]");
    Serial.print("Start Arduino: ");
    Serial.println(time.gettime("d/m/y - H:i:s"));
          
    lcd.setCursor(12,0);
    lcd.print (time.gettime("d/m/y")); // выводим дату
     if (time.weekday==1) // ПН
      {
      lcd.setCursor(9,0);
      lcd.printByte(5); lcd.print ("H");
      }
       if (time.weekday==2) // ВТ
      {
      lcd.setCursor(9,0); lcd.print ("BT");
      }
       if (time.weekday==3) // СР
      {
      lcd.setCursor(9,0); lcd.print ("CP");
      }
       if (time.weekday==4) // ЧТ
      {
      lcd.setCursor(9,0);
      lcd.printByte(6); lcd.print ("T");
      }
      if (time.weekday==5) // ПТ
      {
      lcd.setCursor(9,0);
      lcd.printByte(5); lcd.print ("T");
      }
      if (time.weekday==6) // СБ
      {
      lcd.setCursor(9,0);
      lcd.print ("C"); lcd.printByte(7);
      }
      if (time.weekday==0) // ВС
      {
      lcd.setCursor(9,0);
      lcd.print ("BC");
      }
      
      lcd.setCursor(0,0);
      lcd.print (time.gettime("H:i:s")); // выводим время 
     
    
    pinMode(PORT, INPUT);  // use the AIO pin

  attachInterrupt(digitalPinToInterrupt(PORT), ext_int_1, CHANGE);

    if (!SD.begin(4)) {
    Serial.println("initialization of the SD card failed!");
    }
    else {
    Serial.println("initialization of the SDcard is done.");
    myfile = SD.open("oregon.txt", FILE_WRITE);
    Serial.println("oregon.txt - OPEN");
    myfile.print("Start Arduino: "); 
    myfile.println(time.gettime("d/m/y - H:i:s"));
    }

}

void loop () {
    
    cli();
    word p = pulse;
    pulse = 0;
    sei();
  
    if (p != 0) {
        if (orscV2.nextPulse(p))
            reportSerial("OSV2", orscV2);  
      }
      
      if (millis() > obnov_clock){
      lcd.setCursor(0,0);
      lcd.print(time.gettime("H:i:s")); // выводим время 
      obnov_clock = millis() + clock_interval;
      }

      if (millis() > close_file) {
      
      myfile.print("Close file: ");
      myfile.println(time.gettime("d/m/y - H:i:s"));
      myfile.close();

      Serial.print("Close file: ");
      Serial.println(time.gettime("d/m/y - H:i:s"));
      
      close_file = millis() + close_interval;
      myfile = SD.open("oregon.txt", FILE_WRITE);
      Serial.println("oregon.txt - OPEN");
}


if (time.Hours==0){ // Полночь
      lcd.setCursor(12,0);
      lcd.print (time.gettime("d/m/Y")); // выводим дату
            
      if (time.weekday==1) // ПН
      {
      lcd.setCursor(9,0);
      lcd.printByte(5); lcd.print ("H");
      }
       if (time.weekday==2) // ВТ
      {
      lcd.setCursor(9,0); lcd.print ("BT");
      }
       if (time.weekday==3) // СР
      {
      lcd.setCursor(9,0); lcd.print ("CP");
      }
       if (time.weekday==4) // ЧТ
      {
      lcd.setCursor(9,0);
      lcd.printByte(6); lcd.print ("T");
      }
      if (time.weekday==5) // ПТ
      {
      lcd.setCursor(9,0);
      lcd.printByte(5); lcd.print ("T");
      }
      if (time.weekday==6) // СБ
      {
      lcd.setCursor(9,0);
      lcd.print ("C"); lcd.printByte(7);
      }
      if (time.weekday==0) // ВС
      {
      lcd.setCursor(9,0);
      lcd.print ("BC");
      }
      
      }    
}


 

snickser
Offline
Зарегистрирован: 02.07.2016

У Ардуино нет часов реального времени, потому каждый час - относительное понятие.

 

Sprite
Offline
Зарегистрирован: 30.10.2016

snickser пишет:

У Ардуино нет часов реального времени, потому каждый час - относительное понятие.

 


Каждый час с момента запуска.

snickser
Offline
Зарегистрирован: 02.07.2016

Берём текущее время millis(), делим на час (1000*60*60), приводим к модулю "24" если нужна циклическая запись.

word h = (millis()/3600000) % 24;

вставляем полученное значение в строку имени файла.

"oregon" + String(h) + ".txt"

 

 

Sprite
Offline
Зарегистрирован: 30.10.2016

snickser пишет:

Берём текущее время millis(), делим на час (1000*60*60), приводим к модулю "24" если нужна циклическая запись.

word h = (millis()/3600000) % 24;

вставляем полученное значение в строку имени файла.

"oregon" + String(h) + ".txt"

 

 


Спасибо. Попробую.

Sprite
Offline
Зарегистрирован: 30.10.2016

snickser пишет:

Берём текущее время millis(), делим на час (1000*60*60), приводим к модулю "24" если нужна циклическая запись.

word h = (millis()/3600000) % 24;

вставляем полученное значение в строку имени файла.

"oregon" + String(h) + ".txt"

 

 

Ругается при компиляции на строку:

   myfile = SD.open("oregon"+String(h)+".txt", FILE_WRITE);
 
Oregon_Work_SD:561: error: no matching function for call to 'SDClass::open(StringSumHelper&, int)'
 
     myfile = SD.open("oregon"+String(h)+".txt", FILE_WRITE);
 
 
no matching function for call to 'SDClass::open(StringSumHelper&, int)'

 

snickser
Offline
Зарегистрирован: 02.07.2016

тогда через snprintf() ...

Sprite
Offline
Зарегистрирован: 30.10.2016

snickser пишет:

тогда через snprintf() ...


Не могли бы вы написать по подробнее?

mvn77
Offline
Зарегистрирован: 31.03.2017

Может кому пригодится код для V3 в частности проверил на анемометре WGR 800

В качестве приемника использовал родной WMR100, ардуино - esp8266 (NODEMCU)

данные подаю на D5

код использую  - 

/*Status:  Yet another Oregon Scientific Weather Station 433Mhz Arduino Signal Intercepter
 *Open Source
 *Author: Rob Ward December 2013
 *Finds the header bits and synch's to the waveform's 1->0 going edges, which are '1's
 *Soaks up rest of header bits and waits for first 0
 *Accumulates incoming data stream into bytes, arranged from MSB to LSB by rotating a mask in a Left Rotate
 *Checks for Sensor ID's, the ID's used here are relative to the previous way the data is collected, so not the same as others
 *Using the rotate bit on the fly the two ID bytes are as follows, this program just use first byte to ID the sensor
 *        Oregon-THGN800   0xAF  	Outside Temp-Hygro    AF 82 ?? 4 ?? 1 == (Sensor switch nybble 1,2or3)
 *        Oregon-PCR800    0xA2 	Rain Gauge            A2 91 ?? 4 ?? 0
 *        Oregon-WGR800    0xA1 	Anemometer            A1 98 ?? 4 ?? 0
 *Rolling code is present to differentiate between alternative sensors
 *Calculate the check sum for the sensor and if data passes the test...continue
 *Decode the nybbles (if bites are bytes, nybbles are nybbles :-) and calculate the parameters for each sensor
 *Dump the calculations to the screen, round off to decimal places
 
 Why not use interrrupts and count durations?  Manchester encoding came from the time of slow chips, noisey environments and saggy waveforms.
 The beauty of Manchester encoding is that is it can be sampled so the logic transitions are the most important and at least the logic 
 state in the middle of the timing periods is most likely to be valid.  It also self synchronises throughout the packet and 
 automatically detects timeouts.  This is an old, classic Manchester decoding decoding strategy, but quite robust never the less.
 
 To do:
 *find the battery indicators if they exist. (7) suggests it is (my) upper 4 bits of byte 2.  How to fake old batteries??
 *Add a 1 minute dump of current values in CSF format for my WWW Weather station (Remove current debug sensor dump format).
 
 Reference Material:
 Thanks to these authors, they all helped in some way, especially, the last one Brian!!!!
 http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf (1)
 http://jeelabs.net/projects/cafe/wiki/Decoding_the_Oregon_Scientific_V2_... (2)
 https://github.com/phardy/WeatherStation (3)
 http://lucsmall.com/2012/04/27/weather-station-hacking-part-1/ (4)
 http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (5)
 http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (6)
 http://www.mattlary.com/2012/06/23/weather-station-project/(7)
 https://github.com/lucsmall/WH2-Weather-Sensor-Library-for-Arduino (8)
 http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com
 
 Most of all thanks to Oregon Scientific, who have produced an affordable, high quality product.  I can now have my LCD Home Base in the
 kitchen to enjoy, with the Arduino in the garage also capturing data for WWW Weather pages.  Lovely!!!!  http://www.oregonscientific.com
 Very Highly recommended equipment. Rob Ward
 */

// Read data from 433MHz receiver on digital pin 14
#define RxPin 14    //Just an input
#define ledPin 13  //Human feedback
#define sDelay 230  //One Quarter Manchester Bit duration, is this OK?
#define lDelay 460  //One Half Manchester Bit duration
byte    headerHits = 0; //How many ones detected as a header bit
boolean header = false; //State of header detection
boolean logic = false; //State of the Manchester decoding
byte    signal = 0; //state of RF
boolean test230 = false;
boolean test460 = false;
int     maxBytes = 10; //sets the limits of how many data bytes will be required
int     nosBytes = 0; //counter for the data bytes required
boolean firstZero = false; //flags when the first '0' is found.
byte    dataByte = 0; //accumulates the bits of the signal
byte    dataMask = 16; //rotates, so allows nybbles to be reversed
byte    nosBits = 0; //counts the shifted bits in the dataByte
byte    manchester[11]; //storage array for the data accumulated via manchester format
const char windDir[16][4] = {  
  "N  ", "NNE", "NE ", "ENE",  "E  ", "ESE", "SE ", "SSE",  "S  ", "SSW", "SW ", "WSW",  "W  ", "WNW", "NW ", "NNW"};
double  avWindspeed = 0.0;
double  gustWindspeed = 0.0;
byte    quadrant = 0;
double  rainTotal = 0.0;
double  rainRate = 0.0;
double  temperature = 0.0;
int     humidity = 0;

void setup(){
  Serial.begin(115200);
  Serial.println("Robs V3.0 WMR86 Oregon Decoder");
  pinMode(ledPin, OUTPUT);
  pinMode(RxPin, INPUT);
  digitalWrite(ledPin, HIGH);
  delay(100);//heart beat
  digitalWrite(ledPin, LOW);
  headerHits=0;
}

void loop(){
  //wait here for a header!!!
  // So far this appears as 'inverted' Manchester 1>0==1, 0>1==0 ??? (G. E. Thomas in 1949) http://en.wikipedia.org/wiki/Manchester_code
  while (header == false){
    while (digitalRead(RxPin)==1) { //Stay in loop while logic =1
      //loop while the RxPin==1, first half of bit pattern, just before data transition, 1 to 0
    }//exits when signal == 0, (1->0 falling edge found, transition to value 0) middle of bit pattern, ie the data edge
    delayMicroseconds(sDelay); //Short wait for a 1/4 of the "1" pattern
    if (digitalRead(RxPin) == 0){ //Check signal is still steady at 0 ( !0 = error detection)
      delayMicroseconds(lDelay); // long wait for next 1/2 of bit pattern, 
      // ie now at 3/4 way through, looks like an ok bit "1" now keep track of how many in row we have detected
      if (digitalRead(RxPin) == 1){ // Check Rx polarity has now swapped, good!
        headerHits ++; // Highly likely a "1" waveform, so count it in
        if (headerHits == 20){ //if we can find 20 in a row we will assume it is a header
          header = true; //so we can exit this part, collect rest of header and begin data processing below
          headerHits=0; //reset, so ready to search for another header when next required, or should an error occur in this packet
          //Serial.println("");
          //Serial.print("H"); //uncomment to debug header detection
        }
      }
      else {
        headerHits =0;  // Has not followed the "1" wave pattern, probably badly formed, noisy waveform, so start again
        header = false; // make sure we look for another header
      }
    }
    else {
      headerHits =0;  // Has not followed wave pattern, probably just noise, so start again
      header = false; // make sure we look for another header
    }
  }

  //The program is now synched to the '1' waveform and detecting the 1->0  "data" transitions in the bit waveform
  //The data byte boundaries indicate the Synch '0' is considered a part of the data, so byte boundary begins at that '0'
  logic=1; // look for rest of header 1's, these must be soaked up intil first 0 arrives to denote start of data
  signal = 0; //RF Signal is at 0 after 1's 1->0 transition, inverted Manchester (see Wiki, it is a matter of opinion)
  firstZero = false; //The first zero is not immediately found, but is flagged when found

  while (header == true){
    //now get last of the header, and then store the data after trigger bit 0 arrives, and data train timing remains valid 
    while (digitalRead(RxPin)!=signal){ //halt here while signal matches inverse of logic, if prev=1 wait for sig=0
    }//exits when signal==logic
    delayMicroseconds(sDelay); //wait for first 1/4 of a bit pulse
    test230 = digitalRead(RxPin);//snapshot of the input
    if ((test230 == signal)&&(nosBytes < maxBytes)){  //after a wait the signal level is the same, so all good, continue!
      delayMicroseconds(lDelay); //wait for second 1/2 of a bit pulse
      test460=digitalRead(RxPin);//snapshot of the input
      if (test230==test460){  // finds a long pulse, so the logic to look for will change, so flip the logic value 
        //Assuming the manchester encoding, a long pulse means data flips, otherwise data stays the same
        logic = logic^1;
        signal = signal^1;
        //Serial.print(logic,BIN);  //debug data stream in binary
        if (!firstZero){ //if this is the first 0-1 data transition then is the sync 0
          digitalWrite(ledPin,1); //data processing begins, first though chew up remaining header
          firstZero = true; //flag that legit data has begun
          //VIP OS Seems to put the Synch '0' bit into the data, as it causes the rest to align onto byte boundaries
          dataByte = B00000000; // set the byte as 1's (just reflects the header bit that have preceded the trigger bit=0)
          dataMask = B00010000; // set the byte as 1's (just reflects the header bit that have preceded the trigger bit=0)
          nosBits = 0;  // preset bit counter so we have 7 bits counted already
          //Serial.print("!");  //debug detection of first zero
        }

      }
      //data stream has been detected begin packing bits into bytes
      if (firstZero){
        if (logic){
          dataByte = dataByte | dataMask; //OR the data bit into the dataByte
        }
        dataMask = dataMask << 1;//rotate the data bit
        if (dataMask==0){
          dataMask=1;//make it roll around, is there a cleaner way than this? eg dataMask *=2?
        }
        nosBits++;
        if (nosBits == 8){ //one byte created, so move onto the next one
          manchester[nosBytes] = dataByte; //store this byte
          nosBits = 0;     //found 8, rezero and get another 8
          dataByte = 0;    //hold the bits in this one
          dataMask = 16;   //mask to do reversed nybbles on the fly
          nosBytes++;      //keep a track of how many bytes we have made
        }
      }
    }  
    else {
      //non valid data found, or maxBytes equalled by nosBytes, reset all pointers and exit the while loop
      headerHits = 0;    // make sure header search begins again
      header = false;    // make sure we look for another header
      firstZero = false; // make sure we look for another 1->0 transition before processing incoming stream
      nosBytes = 0;      // either way, start again at beginning of the bank
    }

    if (nosBytes == maxBytes){ 
      if (manchester[0]==0xaf){ //detected the Thermometer and Hygrometer
        if(ValidCS(16)){
          thermom();
        }
      }
      if (manchester[0]==0xa1){  //detected the Anemometer and Wind Direction
        if(ValidCS(18)){
          //hexBank();
          //binBank();
          anemom();
        }
      }
      if (manchester[0]==0xa2){  //detected the Rain Gauge
        if(ValidCS(19)){
          rain();
        }
      }
      headerHits = 0;
      header = false;
      nosBytes =0;       //reset byte pointer into bank
    }
  }
  digitalWrite(ledPin,0); //data processing ends, look for another header
} //end of main loop

//Support Routines for Nybbles and CheckSum

// http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com
// Directly lifted, then modified from Brian's work, due to nybbles bits now in correct order MSB->LSB
// CS = the sum of nybbles, 1 to (CSpos-1) & 0xf, compared to CSpos nybble;
bool ValidCS(int CSPos){
  bool ok = false;
  byte check = nyb(CSPos);
  byte cs = 0;
  for (int x=1; x<CSPos; x++){
    byte test=nyb(x);
    cs +=test;
  }
  byte tmp = cs & 0xf;
  if (tmp == check){
    ok = true;
  }
  return ok;
}
// Get a nybble from manchester bytes, short name so equations elsewhere are neater :-)
byte nyb(int nybble){
  int bite = nybble / 2;  //DIV 2, find the byte
  int nybb  = nybble % 2;  //MOD 2  0=MSB 1=LSB
  byte b = manchester[bite];
  if (nybb == 0){
    b = (byte)((byte)(b) >> 4);
  }
  else{
    b = (byte)((byte)(b) & (byte)(0xf));
  }       
  return b;
}

//Calculation Routines

/*   PCR800 Rain Gauge  Sample Data:
 //  0        1        2        3        4        5        6        7        8        9
 //  A2       91       40       50       93       39       33       31       10       08 
 //  0   1    2   3    4   5    6   7    8   9    A   B    C   D    E   F    0   1    2   3    
 //  10100010 10010001 01000000 01010000 10010011 00111001 00110011 00110001 00010000 00001000 
 //  -------- -------  bbbb---  RRRRRRRR 88889999 AAAABBBB CCCCDDDD EEEEFFFF 00001111 2222CCCC
 
 // byte(0)_byte(1) = Sensor ID?????
 // bbbb = Battery indicator??? (7)
 // RRRRRRRR = Rolling Code ID
 // 222211110000.FFFFEEEEDDDD = Total Rain Fall (inches)
 // CCCCBBBB.AAAA99998888 = Current Rain Rate (inches per hour)
 // CCCC = CRC
 // Message length is 20 nybbles so working in inches
 Three tips caused the following
 Rain total: 11.72   rate: 39.33   tips: 300.41
 Rain total: 11.76   rate: 0.31   tips: 301.51
 Rain total: 11.80   rate: 0.31   tips: 302.54
 1 tip=0.04 inches or mm?
 My experiment
 24.2 converts reading below to mm (Best calibration so far)
 0.127mm per tip
 */
void rain(){
  rainTotal = ((nyb(18)*100000)+(nyb(17)*10000)+(nyb(16)*1000)+(nyb(15)*100)+(nyb(14)*10)+nyb(13))*24.2/1000;
  Serial.print("Total Rain ");
  Serial.print(rainTotal);
  Serial.print(" mm, ");
  float rainRate = ((nyb(7)*100000)+(nyb(8)*10000)+(nyb(9)*1000)+(nyb(10)*100)+(nyb(11)*10)+nyb(12))*24.2/10000;
  Serial.print("Rain Rate ");
  Serial.print(rainRate);
  Serial.println(" mm/hr ");
}

// WGR800 Wind speed sensor
// Sample Data:
// 0        1        2        3        4        5        6        7        8        9
// A1       98       40       8E       00       0C       70       04       00       34
// 0   1    2   3    4   5    6   7    8   9    A   B    C   D    E   F    0   1    2   3
// 10100001 10011000 01000000 10001110 00000000 00001100 01110000 00000100 00000000 00110100
// -------- -------- bbbb---- RRRRRRRR xxx8999- xxxxxxxx CCCCDDDD xxxxFFFF 0000---- CCCC----
// Av Speed 0.4000000000m/s Gusts 0.7000000000m/s  Direction: N  

// byte(0)_byte(1) = Sensor ID?????
// bbbb = Battery indicator??? (7)
// RRRRRRRR = Rolling Code
// 8999 = Direction
// DDDD.CCCC = Gust Speed (m per sec)
// 0000.FFFF = Avg Speed(m per sec)
// multiply by 3600/1000 for km/hr

void anemom(){
  //D A1 98 40 8E 08 0C 60 04 00 A4
  avWindspeed = ((nyb(16)*10)+ nyb(15))*3.6/10;
  Serial.print("Av Speed ");
  Serial.print(avWindspeed);
  float gustWindspeed =((nyb(13)*10)+nyb(12))*3.6/10;
  Serial.print(" km/hr, Gusts ");
  Serial.print(gustWindspeed);
  quadrant = nyb(9)&0xf;
  Serial.print(" km/hr, Direction: ");
  Serial.println(windDir[quadrant]);
}

// THGN800 Temperature and Humidity Sensor
// 0        1        2        3        4        5        6        7        8        9          Bytes
// 0   1    2   3    4   5    6   7    8   9    A   B    C   D    E   F    0   1    2   3      nybbles
// 01011111 00010100 01000001 01000000 10001100 10000000 00001100 10100000 10110100 01111001   Bits
// -------- -------- bbbbcccc RRRRRRRR 88889999 AAAABBBB SSSSDDDD EEEE---- CCCC---- --------   Explanation
// byte(0)_byte(1) = Sensor ID?????
// bbbb = Battery indicator??? (7)
// byte(3) is rolling code R
// nybble(5) is channel selector c
// BBBBAAAA.99998888 Temperature in BCD
// SSSS sign for negative (- is !=0)
// EEEEDDDD Humidity in BCD
// nybble(16) is CRC C
// H 00 01 02 03 04 05 06 07 08 09    Byte Sequence
// D AF 82 41 CB 89 42 00 48 85 55    Real example
// Temperature 24.9799995422 degC Humidity 40.0000000000 % rel

void thermom(){
  Serial.print("Temperature ");
  temperature = (float)((nyb(11)*100)+(nyb(10)*10)+nyb(9))/10; //accuracy to 0.01 degree seems unlikely
  if(nyb(12)==1){//  Trigger a negative temp sign
    Serial.print("-");
  }
  Serial.print(temperature);
  Serial.print(" degC, Humidity ");
  humidity = (nyb(14)*10)+nyb(13);
  Serial.print(humidity);
  Serial.println("% Rel");
}

// Handy Debugging Routines

void binBank(){
  //Print the fully aligned binary data in manchester[] array
  Serial.print("D ");
  for( int i=0; i < maxBytes; i++){ 
    byte mask = B10000000;
    if (manchester[i]<16){
      Serial.print("0"); //pad single digit hex
    }
    Serial.print(manchester[i],HEX);
    Serial.print(" ");
    for (int k=0; k<8; k++){
      if (manchester[i] & mask){
        Serial.print("1");
      }
      else{
        Serial.print("0");
      }
      mask = mask >> 1;
    }
    Serial.print(" ");
  }
  Serial.println();
}

void hexBank(){
  //Print the fully aligned binary data, enable the headers if desired
  //Serial.println("H 00 01 02 03 04 05 06 07 08 09");
  //Serial.println("  00 00 00 00 00 00 00 00 11 11");
  //Serial.println("B 10 32 54 76 98 BA DC FE 10 32");
  Serial.print("D ");
  for( int i=0; i < maxBytes; i++){ 
    if (manchester[i]<16){
      Serial.print("0"); //pad single digit hex
    }
    Serial.print(manchester[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
}



 

plagioklaz
Offline
Зарегистрирован: 03.04.2017

Уважаемые форумчанины, постарался осилить все 400+ постов в этой теме...

Может кто-то в шапку добавить или же тут написать, какой код, на данный момент самый доработанный и оптимизированный?

Как-то можно подытожить информацию, мол код такой-то вот, подключаем приёмник на такой-то пин, в итоге получаем информацию в таком-то виде с такой-то периодичностью.

Общие советы и рекомендации по известным подводным камням.

Спасибо большое.

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

Шапку на этом форуме править нельзя даже топикстартеру, насколько мне известно. Итога тоже пока нет. Вся ветка - эта крыловская басня про лебедя рака и щуку: есть несколько вариантов кода, каждый хорош чем-то своим. Боюсь придётся вам всё-таки эти 400 постов осилить :).

plagioklaz
Offline
Зарегистрирован: 03.04.2017

Porosenok, спасибо за информацию. Как вариант, в случаях ограниченного функционала форумов, создают новую тему с назанием, к примеру: "Продолжение темы: Чтение и эмуляция датчиков Oregon 433Mhz (Часть №2)", где уже в новой шапке отмечают главные достижения старой ветки форума =)

Я постараюсь прочитать и что-то для себя уяснить =) Могли бы вы дать хотя бы пару ссылок на ключевые рабочие скетчи с доработками, которые функциональнее кода из первого поста темы?

На данный момент ещё жду свой приёмник... и пытаюсь заранее разобраться в теме и её тонкостях.

Спасибо.

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

У меня прижился код из #411 сообщенияон хоть еле влез в pro-mini (с индикацией и отправкой по RF24), но он ловит откуда угодно и очень стабильно.

Пробовал код из #283 (почему-то форум меняет нумерацию, так что не найду уже), он не такой дальнобойный, если условия хорошие - всё в порядке, у меня на 433 очень много шума, ардуна постоянно в перывании по сигналу с приёмника, хотя приёмник хороший. 

 

ЗЫ Вообще подумываю отказаться от орегона, сваять на Si7021 и nRFкой передавать. Дома уже стоит такой датчик, он калиброван, и удобен. Надо только в ардуинке бут перешить, чтоб спать умела, фьюзы на минимальное напряжение выставить, и светодиоды с неё отпаять. В таком виде (я собирал приблуду давненько для удалённого отображения состояния охранки), батареек надолго должно хватать, орегон, мне наверное не победить, но ардуинка и nrfка в режиме сна едят какие-то микроамперы. А то с этим проектом всё равно промежуточную ардуину использовать приходится.

4ishops
Offline
Зарегистрирован: 06.10.2012

plagioklaz пишет:

Уважаемые форумчанины, постарался осилить все 400+ постов в этой теме...

Может кто-то в шапку добавить 

 

 

Шапка на этом форуме не исправляется к сожалению, так бы давно :)

 

Если сумеете разгрести всю эту ветку и выбрать самое лучшее - то создавайте новую тему смело :)

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

Выложил на Гитхаб новую версию своей библиотеки для датчиков THGN132N и THN132N.

https://github.com/invandy/Oregon_NR

Из нового:

- Отловлены кое-какие мелкие баги.

- Доведена до ума CRC.

 

 

mvn77
Offline
Зарегистрирован: 31.03.2017

Поедлюсь наработаным - так и не смог добится уверенного приема с датчика ветра, в итоге встроил ESP8266 в сам датчик и данные стал снимать с входа приемника и передовать через WI-Fi. Работает как часы. Питание правдf подал постоянное. Но учитывая режим сна в ESP можно запитать и от батареек.

plagioklaz
Offline
Зарегистрирован: 03.04.2017

Здравствуйте, Porosenok.

Не могли бы вы подсказать пару моментов по своему коду? Я зелёный новичок в arduino и ввязался во всё это )) ради участия в проекте народного мониторинга. Можно с вами списаться по почте? Мой адрес 231745@гмэил.ком

К сожалению, на этом форуме я не нашёл как писать личные сообщения ((

PAV
Offline
Зарегистрирован: 29.10.2012

Запустил. Результат такой

36.510 OSV2 1A2D20EC0003100535E3 2 ec 3.0 51% 0
36.746 OSV2 1A2D20EC0003100535E3 2 ec 3.0 51% 0
51.039 OSV3 BADFFDEA1CA8FFFFBADF Checksum error
51.166 OSV3 BADFFDEA1CA8FFFFBADF Checksum error
 

V2 ловит, а по V3 ошибка ;(  Куда копать?

А еще полный код из шапки выдает такое:


[ookDecoder]
OSV3 BADFFDEA1CA8FFFFBADF
HEZ 033006E0878400
HEZ 8002A8A2A80000
HEZ 8002A8A2A80000
CRES 80CDCC0E6ABADA0D45910080A0
HEZ 08000000042001
CRES C020053F139819C0C02005
HEZ 00600018809000
HEZ 8002A8A2A80000
HEZ 8002A8A2A80000
HEZ 458039C7CC01
HEZ 4E009833091000
CRES 24D3B957A9C161ECD79700
HEZ 61757FF75D0100
CRES 24D3B967A9C161ECD77104
HEZ 60607E72E04000
CRES 24D3B937A9C161ECD7DB07
HEZ 0106E627CF7C00
HEZ 8002A8A2A80000
CRES AEEFFFEFFEFFAFAFFAAFEAEEBE0F
HEZ FF499273CEE703
CRES BEBAFAFFFABBBEFFBFFBFFBFBEEABFAABBFB3E
HEZ 8002A8A2A80000
OSV3 BADFFDEA1CA8FFFFBADF
HEZ 8002A8A2A80000
OSV3 BADFFDEA1CA8FFFFBADF
CRES 94E003003C8319FC9400
HEZ 011806E0878400
HEZ 32000000900C01
HEZ 00600018809000
HEZ 07001866066000
HEZ 8002A8A2A80000
CRES 24D3B957A9C161ECD79700
HEZ 60607EF2090100
CRES 24D3B967A9C161ECD77104
HEZ 60607E72E04000
CRES 24D3B937A9C161ECD7DB07
HEZ 0106E627CF7C00
OSV2 1A2D20EC0003100535E3
CRES BEBAFAFFFABBBEFFBFFBFFBFBEEABFAABBFB3E
CRES BEBAFAFFFABBBEFFBFFBFFBFBEEABFAABBFB3E
HEZ 8002A8A2A80000

Я правильно понимаю, что еще какие-то датчики видит? Их можно расшифровать?

 

snickser
Offline
Зарегистрирован: 02.07.2016

Нет, всё это просто цифровой шум в округе, датчик там только один OSV2.

 

Timuridze
Offline
Зарегистрирован: 22.03.2015

Всем привет.  Во первых хочу сказать спасибо Porosenok и всем форумчанам которые принимали участие в разработки протокола общения с ардуино.

В общем стряхнул я пыль со своей ардуинки и решил что пора её под что нибудь полезное приспособить,  в наличии был датчик Орегон который самый простой, экран 1602, часы RTC и PIR датчик. Благодаря библиотеки Porosenok и примеру в ней довльно быстро накидал код copy/paste. В планах подключить датчик DHT для мониторинга комнатной температуры.

Сейчас возникло несколько проблем, надеюсь кто нибудь поможет.

1. Хочу съкономить место на экране и год выводить 2мя цифрами вместо 4х. Не знаю как убрать первые два символа года.

2. саму погодную станцию хочу повесить около входной двери. И сделать подсветку автоматически отключаемой с помощью пир датчика. Но пока не могу сообразить куда вставить код включения-отключения подсветки

3. Как сделать чтобы часы отображались независимо от есть данные с датчика или нет. Пока у меня если нет данных часы не отображаеются

Ну и был бы рад любым коментариям и дополнениям в коде, заранее спасибо.

#include <Oregon_NR.h>

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include "RTClib.h"

#define UNO
#define pirPin 4
#define ledPin 13

//Oregon_NR oregon(2, 0,13); // Для Arduino UNO/Nano - датчик на выводе D2, Прерывание 0, Светодиод приёма на вывод 13
Oregon_NR oregon(2, 0); // Если светодиод не нужен
LiquidCrystal_I2C lcd(0x27,20,4); 

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() {
   Serial.begin(115200);
  //вкючение прослушивания радиоканала  
  pinMode(pirPin, INPUT);
  pinMode(ledPin,OUTPUT);
  
  oregon.start(); 
 
 lcd.init();  
  lcd.backlight();
//rtc.adjust(DateTime(2017, 6, 14, 7, 8, 0));

}

void loop() {

  

    DateTime now = rtc.now();
  //////////////////////////////////////////////////////////////////////
  //Захват пакета,//////////////////////////////////////////////

  oregon.capture(1);
  //Захваченные данные годны до следующего вызова capture
  //ОБработка полученного пакета//////////////////////////////////////////////
  if (oregon.captured)  {
    //Вывод информации в Serial
    
    
    if ((oregon.sens_type == THGN132 || oregon.sens_type == THN132) && oregon.crc_c){
      lcd.setCursor(0,0);
      
      if (oregon.sens_tmp > 0 && oregon.sens_tmp < 10) lcd.print("TMP:  ");
      if (oregon.sens_tmp < 0 && oregon.sens_tmp >-10) lcd.print("TMP: ");
      if (oregon.sens_tmp <= -10) lcd.print("TMP:");
      if (oregon.sens_tmp >= 10) lcd.print("TMP: ");
      lcd.print(oregon.sens_tmp, 1);
      
     lcd.print("C ");
            
      lcd.print("BAT:");
      if (oregon.sens_battery) lcd.print("F"); else lcd.print("e");
  
    
 lcd.setCursor(0,1);
    lcd.setCursor(0,1);
    lcd.print(now.day(), DEC);
    lcd.print('.');
    lcd.print(now.month(), DEC);
    lcd.print('.');
    lcd.print(now.year(), DEC);
    lcd.print(' ');
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    lcd.print(now.minute(), DEC);
 //   lcd.print(':');
  //  lcd.print(now.second(), DEC);
  
    
        
    }
   
  }
}

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

Вопрос 1  - это решается через работу со строками - Оффтоп, однако!

Вопрос 2 и 3 - 35-я строка  - самое подходящее место для всего - и опроса и управления другой периферией и вывода времени на экран. Тольок вот делать это надо не в каждом цикле, а по таймеру, чтобы минимизировать шансы прозевать начало пакета в эфире.

sega555
Offline
Зарегистрирован: 23.09.2017

Доброго времени суток, нужна помощь делаю проект племяшке


Беспроводная сигнализация на базе Arduino
не могу загрузить скетч

заранее спасибо

01 // Include VirtualWire library
02 #include <VirtualWire.h>
03  
04 int led_pin = 13;
05 int transmit_pin = 12;
06 int pir_pin = 2;
07 int val = 0;
08 int pir_state = LOW;
09  
10 void setup()
11 {
12    Serial.begin(9600);
13    vw_set_tx_pin(transmit_pin);
14    vw_setup(4000); // Transmission rate
15    pinMode(led_pin, OUTPUT);
16    pinMode(pir_pin,INPUT);
17 }
18  
19 void loop()
20 {
21   char msg[1] = {'0'};
22   // Get sensor value
23   val = digitalRead(pir_pin);
24   // Change message if motion is detected
25   if (val == 1)
26   {
27       msg[0] = '1';
28       digitalWrite(led_pin, HIGH); // Flash a light to show transmitting
29       vw_send((uint8_t *)msg, 1);
30       vw_wait_tx(); // Wait until the whole message is gone
31       if (pir_state == LOW)
32       {
33       Serial.println("Motion detected!");
34       pir_state = HIGH;
35       }
36    }
37  else
38  {
39    msg[0] = '0';
40    digitalWrite(led_pin, LOW);
41    vw_send((uint8_t *)msg, 1);
42    vw_wait_tx(); // Wait until the whole message is gone
43    if (pir_state == HIGH)
44    {
45       Serial.println("Motion ended!");
46       pir_state = LOW;
47    }
48   }
49 }

и 

01 // Include VirtualWire library
02 #include <VirtualWire.h>
03  
04 // Pins definition
05 const int led_pin = 13;
06 const int receive_pin = 12;
07 int pinSpeaker = 10;
08  
09 void setup()
10 {
11    Serial.begin(9600); // Debugging only
12    // Initialise the IO and ISR
13    vw_set_rx_pin(receive_pin);
14    vw_setup(4000); // Transmission rate
15    // Start the receiver PLL
16    vw_rx_start();
17    // Set LED pin and Buzzer
18    pinMode(led_pin, OUTPUT);
19    pinMode(pinSpeaker, OUTPUT);
20 }
21  
22 void loop()
23 {
24    uint8_t buf[VW_MAX_MESSAGE_LEN];
25    uint8_t buflen = VW_MAX_MESSAGE_LEN;
26  
27    // Check if a message was received
28     if (vw_get_message(buf, &buflen))
29     {
30       if(buf[0]=='1')
31       {
32       Serial.println("Motion detected!");
33       digitalWrite(led_pin,1);
34       playTone(300, 160);
35       delay(150);
36       
37      if(buf[0]=='0')
38      {
39      Serial.println("Motion ended!");
40      digitalWrite(led_pin,0);
41      playTone(0, 0);
42      delay(300);
43      }
44    }
45 }
46  
47 // duration in mSecs, frequency in hertz
48 void playTone(long duration, int freq)
49 {
50     duration *= 1000;
51     int period = (1.0 / freq) * 1000000;
52     long elapsed_time = 0;
53     while (elapsed_time < duration)
54     {
55     digitalWrite(pinSpeaker,HIGH);
56     delayMicroseconds(period / 2);
57     digitalWrite(pinSpeaker, LOW);
58     delayMicroseconds(period / 2);
59     elapsed_time += (period);
60    }
61 }

 

Sr.FatCat
Offline
Зарегистрирован: 19.02.2016

Надеюсь, мой пост принесет пользу, ну и мне помогут.
Есть датчики температуры/влажности погодных станций WH2, они в 3 раза дешевле орегоновских и для большинства применений - не хуже. Очень качественно изготовлены во всепогодном исполнении,  отличный жк дисплей. Из недостатков можно отметить отсутвие выбора каналов (но в пакете есть ID датчика, который хоть и меняется с каждым рестартом, но вполне может служить для привязки) и сравнительно лаконичные данные по температуре/влажности. Точнее по температуре - все ок, а по влажности, они и Орегоновские /ЛеКроссовские объективностью по известным причинам, не блещут. Вообщем с рекламой закончили :)
Теперь по существу. 
Товарищ Маленький Люк в своем блоге подробно разобрал протокол передачи и своял скетчик с довольно нестандартным подходом к декодированию ООК. Где длительность импульсов определяется не в прерывании изменениея состояния ноги с подключенным приемником, а обработчиком таймера опрашивается постоянно состояние ноги и довольно неочевидным способом получается длительность импульсов. Мне такой подход показался странным и ненадежным и я для стандартного класса DecodeOOK сделал потомка DecodeWH2 (сейчас приложить не могу - на другом компе), но он двухкопеечный: в перекрытой процедуре decode ожидается
пять коротких импульсов и последующие длинные короткие пишутся в перекрытой gotBit. Все работает. Но из-за шума (т.е., проскакивания слишком коротких или очень длинных импульсов) у меня последовательность довольно регулярно сбрасывается.
И получается, что стандартнеым подходом на прерывании у меня в 8 (!!!!) раз меньше получается принять пакетов, чем у Люка (сделал скетч с двумя вариантами и проверил).
Вопрос в следующем: если четко понять логику определиния длинны импульса у Люка - можно встроить шумодав (игнорировании каких-то импульсов) и в стандартный мой подход - но мне мозгов не хватает. Может кто помочь?

 

Stealth086
Offline
Зарегистрирован: 11.11.2017

Люди добрые, помогите разобраться с датчиком THGN122N. Немного не по теме конечно. Он работал, но при пайке замкнулись контакты GND и PWR, сразу не заметил и включил. Индикатор моргает как положено, но сигнал станция не находит. Разобрал - перепаял. Может ли по этой причине накрыться чип или кодировка? Фото тут не смог прикрепить. Ссылка ниже.

https://drive.google.com/folderview?id=1IUWZ_NnV6HK1l1Cx0QfgGB1ygsZXsS3c

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

Проверте транзистор Q8 (ну и что до него - не видно, скорее всего ничего), ну и на всякий случай прозвоните Q3. Чип накрыться , при указанном замыкании, не мог.

Можно просто попробовать замкнуть ноги Q8 (это 2 ноги, но не ту, которая возле надписи R13), тогда если работает - скорее всего Q8 и накрылся.

PS Какая страшная капля, как буд-то её грели со стороны контактов LCD

 

PPS Всё же я собрал замену орегону, это оказалось куда проще, включая программу. Состоит из ардуины и nrf, ардуина и nrf24 ничего фактически не потребляют во сне, я их бужу раз в 5 минут, узнаю температуру, передаю, и отправляю в сон.

Датчик снаружи (солнечная батарея, дырки для конвекции воздуха датчика)Часы в комнате (автояркость с фотодатчика, ретранслятор на базу, измерение комнаты)База в прихожей (звонок, домофон, народный мониторинг и тп)

Первые батарейки (какие-то космос из ближайшей палатки за 5р/шт ничего не весящие) проработали 3 месяца (и могли бы и дальше), потом поставил Nimh, которые, как оказалось, ниже 0 вообще не имеют ёмкости, приеду из командировки, запилю ионистор, тк приделанная солнечная батарея зимой не успевает зарядить аккум даже до старта железяки )

Вот тут, на первом графике, обычные, самые хилые батарейки (перерыв в графике связан с отключением базы из сети, схема работоспособна до 1.8v). На втором - аккумы, но тепло, потом, в середине графика, те же аккумы с солнечной батареей, в конце - стало холодно - аккум умер за несколько дней.

Stealth086
Offline
Зарегистрирован: 11.11.2017

Проверил Q8 исправен. Да и на ножках GND и PWR напряжение присутствует. Светодиод периодически моргает, а вот станция не находит датчик. Будто радиосигнала нет. А что за верхняя плата (которая сидит на ножках DATA, PWR, GND и еще одна без обозначения), на ней антенки, может вней причина? Не отправляет сигнал

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

Это плата передатчика, можно проверть так - на приёмник, который в базе, подключить высокоомный динамик (возможно тестер, не знаю схему Вашей железяки), а тут отпаять от схемы дату и подать на неё сигнал,  хоть даже с наушников телефона (позамыкать через резистор в 1КОм этот выход на + и -).

Sr.FatCat
Offline
Зарегистрирован: 19.02.2016

udavst пишет:

я собрал замену орегону, это оказалось куда проще, включая программу. Состоит из ардуины и nrf

Проект интересный, но я его бы "заменой орегона" никак не назвал бы. 
Вся соль, в том, чтобы использовать готовые датчики. Орегон и ЛяКросс это, конечно, не дешево, но легкодоступно и паять ничего не надо. Кроме того, есть например WH2 - отличные с оговоркой датчики и всего 500р. Ну и еще есть масса китайских копеечных, с которыми надо разбираться.

Ваш проект своего проприетарного датчика тоже далеко не оптимален. Я делал на тиньке с RC_Switch кодированием - прекрасно работает, до 1,5лет от 3хАА раз в минуту, отличная дальнобойность - но главный недостаток для меня - паять надо, а мне в ближайшую перспективу их 6 штук понадобится...