Официальный сайт компании Arduino по адресу arduino.cc
Погодная станция на датчиках Oregon Scientific v2.1
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Именно желание получить в доме ещё одну погодную станцию, понимающую датчики Oregon Scientific, и привело меня к Ардуино. Сразу хочу отметить, что я не программист. Си знаю плохо, пытаюсь объясняться с компьютером тем словарным запасом, которым владею. Поэтому код получается обычно весьма корявый, но понятный компилятору.
Итак, в наличии у меня было следующее:
1. Датчики Oregon THGN132 и THN132, в которых реализован протокол версии 2.1,
2. Arduino Uno,
3. дисплей LCD2004 I2C,
4. модуль часов реального времени DS3231,
5. Простейший приёмник 433Мгц RF-5V,
6. Модуль барометра Oregon Scientific SR-BAR998HG.
Простого пути не получилось. Попытка использовать код, приведённый в теме http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-scientific-433mhz ни к чему не привела. Максимальное расстояние, с которого были видны датчики - 7 метров. При этом стоящая рядом фирменная погодная станция уверенно видела датчик, висящий на хозблоке в 20-и метрах от дома. Разобрав эту станцию, и убедившись, что там установлен простенький приёмник, очень похожий на RF-5V, я понял, что проблема совсем не в приёмнике.
Анализ вышеуказанного кода , подтвердил мои подозрения - код рассчитан на лабораторные условия. Не устроило меня в нём следующее:
1. любые помехи в коде воспринимаются как фатальная ошибка. Даже после короткого всплеска в 200мкс весь пакет данных считается испорченным.
2. Не используются возможности самовосстановления кода за счёт его избыточности,
3. Датчики передают два пакета подряд. Эта особенность практически игнорируется автором.
В итоге было принято решение написать всё с нуля. Анализа осциллограмм выхода приёмника в момент прихода пакетов дал представление о наиболее распространённых видах искажения сигнала:
1. Зажёвывание приёмником начала пакета из-за медленной АРУ,
2. Дробление импульсов при слабом сигнале,
3. Помехи в момент прохождения "нулей".
Исходя из этого была выработана стратегия сбора данных:
- Чтобы начать читать пакет из эфира, надо за что-то зацепиться. Разумнее всего - за преамбулу: на фоне шума проще всего найти несколько импульсов правильной длины с правильными временными интервалами.
- После удачного нахождения такой последовательности в память записывается сигнал с приёмника с частотой дискретизации около 16 384Гц. Это около 8 измерений на такт, что вполне достаточно, чтобы не обращать внимания на дробление импульсов. Продолжительность записи ограничивается либо предельной длиной ожидаемого пакета, либо молчанием канала в течение определённого времени.
- По возможности записываются обе кодовые посылки, идущие с интервалом в несколько миллисекунд.
- Теперь, когда данные собраны, можно неспешно их разобрать. Расшифровывая запись можно получить битовую последовательность. Если избыточность данных посылки позволяет подтвердить отдельные биты, им назначается статус уверенных. Иначе - биты сомнительные.
По причине возможных сбоев синхронизации при обнаружении преамбулы, проводится подсчёт уверенных и сомнительных битов с разными начальными временными смещениями, т.е. вычисляются веса вариантов расшифровки и выбирается лучший из них.
- Если получился пакет с сомнительными участками или записана только часть пакета, то недостающие данные по возможности ищутся во втором пакете. Иными словами, из двух неполных пакетов собирается один полный.
- Собранный пакет должен в обязательном порядке иметь хотя бы четыре последних бита преамбулы (т.н. нибл синхронизации), иначе не всегда можно понять, где начало данных. Если условие соблюдается - пакет считается целым.
- В пакете ещё могут оставаться сомнительные биты. Как их проверить? Конечно же с помощью контрольной суммы! Если она сошлась - пакет признан годным.
Основной недостаток такого подхода к анализу данных - большое количество необходимой для работы оперативной памяти.
Полевые испытания созданного кода весьма порадовали. Видимость датчика выросла практически до уровня фирменной погодной станции, чего, в общем, и добивался.
к коду самого приёмника была дописана сама "погодная станция" - вывод на четырёхстрочный дисплей данных с датчиков (температура, влажность, состояние батареи). Также на дисплее в виде т.н."ёлочки" отображается время прихода данных. Чем дольше программа не получает пакет, тем короче "ёлочка". Если данных не было достаточно долго, информация от такого датчика стирается с экрана.
Если пришёл повреждённый пакет с неправильной контрольной суммой - к "елочке" дорисовывается лишняя "ветка", но данные не обновляются.
По нажатию на кнопку можно посмотреть экстремумы температуры за сегодня. Эта функция очень понравилась в фирменной погодной станции.
В Serial выводится вся информация о ходе расшифровки данных.
Дисплей и модуль часов подключены Ардуино по I2C, кнопка c подтягивающим резистором - к D3, а приемник - к D2. Данные барометра приходят на А0.
Внутренняя температура берётся с модуля часов. Признаться честно, это плохое решение. Во-первых, модуль в процессе работы нагревается на полтора-два градуса. Во-вторых, внутренний термометр выдаёт температуру с точностью до четверти градуса.
Ещё хочется отметить важный момент - качество питания станции. Использование импульсного источника питания снижает радиус приёма примерно в полтора раза. Лучше использовать трансформаторный адаптер или аккумулятор как минимум для питания самого приёмника.
Антенна приёмника - отрезок медного провода диаметром около 1мм и длиной 16см.
Модуль барометра Oregоn SR-BAR998HG - аналоговый, основан на датчике MPXM2102A.
Используемые библиотеки:
LCD2004 I2C: https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home#!downloading-and-installation
DS3231: http://www.rinkydinkelectronics.com/library.php?id=73
Описание протокола Oregon: http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf
Следующим шагом стало написание кода эмулятора датчика THGN132.

#include <DS3231.h> #include <LiquidCrystal_I2C.h> //Типы известных датчиков #define THGN132 0x1D20 #define THN132 0xEC40 #define MHZ 2 #define FIND_PACKET 1 #define ANALIZE_PACKETS 3 //Времена отсутствия датчика, после которых изменять индикатор #define LOST1 2 #define LOST2 4 #define LOST3 8 #define LOST4 32 // убрать строку с дисплея #define RTC_PRESENT 1 //0 - если нет модуля RTC #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 //Размер пакета данных #define DEBUG_INFO 1 //Выводить ли диагностическую информацию volatile byte DISPLAY_MODE = 1; byte maxminshow = 0; bool showdots = 0; float bar_old = 0; bool reciever_ctrl = true; //Флаг контроля ресивера (выставляется при приходе импулься, сбрасывается в таймере) bool reciever_status = false; // Результат проверки состояния ресивера //Для Хранения полученных данных: float r_tmp[4]; float r_tmp_max[4]; float r_tmp_min[4]; byte r_hmdty[4]; bool r_bat[4]; bool r_crc[4]; bool r_isreceived[4]; word r_type[4]; byte lcd_chnl; // unsigned long rcv_time[4]; // время прихода пакетов // set the LCD address to 0x27 for a 20 chars 4 line display // Set the pins on the I2C chip used for LCD connections: // addr, en,rw,rs,d4,d5,d6,d7,bl,blpol LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); byte Cels[8] = { 0b01100, 0b01100, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000}; byte reciv1[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b01000, 0b01001, 0b01001, 0b00000,}; byte reciv2[8] = { 0b00000, 0b00000, 0b01000, 0b01001, 0b01001, 0b01001, 0b01001, 0b00000,}; byte reciv3[8] = { 0b01000, 0b01001, 0b01001, 0b01001, 0b01001, 0b01001, 0b01001, 0b00000,}; byte batemp[8] = { 0b00100, 0b11111, 0b10001, 0b10001, 0b10001, 0b11111, 0b11111, 0b00000, }; byte crcerr[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00001, 0b00001, 0b00000, }; byte dot[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00000, }; byte dot2[8] = { 0b00000, 0b00100, 0b00000, 0b00000, 0b0000, 0b00100, 0b00000, 0b00000,}; #if RTC_PRESENT == 1 //Массив, содержащий время компиляции char compileTime[] = __TIME__; char compileDate[] = __DATE__; DS3231 Clock(SDA, SCL); Time RTC_Time; #endif int bt; 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];//Массивы данных для чтения byte decode_tacts[READ_BITS2]; //Массив тактов. значения // 0=ноль // 1=единица // 2=неизвестен // 3=переход+ // 4=переход- //А когда становится массивом полученных битов, то значения такие: // 0 - неизвестен // >0 - единица // <0 - ноль ///////// ПРЕРЫВАНИЕ /////////////////////////////////////////////////////////////////////////////////////////// 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 int_1(void) { DISPLAY_MODE = 2; timer_mark += 1000; } //////////////// УСТАНОКВКИ /////////////////////////////////////// void setup () { //Иницализации/////////////// #if RTC_PRESENT == 1 Clock.begin(); // Записываем время в часы по времени компиляции //Clock.adjust(compileDate, compileTime); // Выставляем день недели //Clock.setDOW(); #endif lcd.begin(20, 4); lcd.backlight(); lcd.createChar (0, Cels); lcd.createChar (1, reciv1); lcd.createChar (2, reciv2); lcd.createChar (3, reciv3); lcd.createChar (4, batemp); lcd.createChar (5, crcerr); lcd.createChar (6, dot); lcd.createChar (7, dot2); //Настройки АЦП для датчика давления analogReference(DEFAULT) ; Serial.begin(57600); //Прерывание по сигналу от приёмника pinMode(MHZ,INPUT); attachInterrupt(1, int_1, FALLING); attachInterrupt(0, int_0, CHANGE); receive_status = FIND_PACKET; start_pulse_cnt = 0; packet_number = 0; for (int i = 0; i < 4; i++){ r_tmp_max[i] = -110; r_tmp_min[i] = 110; rcv_time[i] = 7000000; r_isreceived[i] = 0; } pinMode(13, OUTPUT); digitalWrite(13, LOW); } //////////////// ГЛАВНЫЙ ЦИКЛ /////////////////////////////////////// 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; } ///...tсли в неправильном месте то назначаем его первым 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; /* if (DEBUG_INFO){ for(bt = 0; bt < 180; bt++){ Serial.print(collect_data[bt], HEX); Serial.print(' '); } Serial.println(" "); for(bt = 0; bt < 180; bt++){ Serial.print(collect_data2[bt],HEX); Serial.print(' '); } Serial.println(" "); } */ 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 (DEBUG_INFO){ Serial.print("1) "); for(bt = 0; bt < READ_BITS; bt++){ if (collect_data[bt] > 128 + 1) Serial.print('I'); if (collect_data[bt] < 128 - 1) Serial.print('O'); if (collect_data[bt] == 128 + 1) Serial.print('i'); if (collect_data[bt] == 128 - 1) Serial.print('o'); if (collect_data[bt] == 128) Serial.print('.'); } Serial.print(" VAL:"); Serial.print(data_val); Serial.print(" SIZE:"); Serial.print(data_length); Serial.print(" SHIFT:"); Serial.print(halfshift); } 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; } if (DEBUG_INFO){ Serial.println(" "); Serial.print("2) "); for(bt = 0; bt < READ_BITS; bt++){ if (collect_data2[bt] > 128 + 1) Serial.print('I'); if (collect_data2[bt] < 128 - 1) Serial.print('O'); if (collect_data2[bt] == 128 + 1) Serial.print('i'); if (collect_data2[bt] == 128 - 1) Serial.print('o'); if (collect_data2[bt] == 128) Serial.print('.'); } Serial.print(" VAL:"); Serial.print(data_val2); Serial.print(" SIZE:"); Serial.print(data_length2); Serial.print(" SHIFT:"); Serial.print(halfshift); } } int correlation = correlate_data(collect_data, collect_data2); if (DEBUG_INFO){ Serial.print(" COR: "); Serial.println(correlation); } 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); } //Вывод готовой посылки } if (DEBUG_INFO){ Serial.print("RESULT "); byte* rdt = result_data; for(bt = 0; bt < READ_BITS; bt++){ if (*rdt > 128 + 1) Serial.print('I'); if (*rdt < 128 - 1) Serial.print('O'); if (*rdt == 128 + 1) Serial.print('i'); if (*rdt == 128 - 1) Serial.print('o'); if (*rdt == 128) Serial.print('.'); rdt++; } Serial.println(" "); } //Обработка посылки Serial.print("PERIOD: "); Serial.print(millis()/40000); Serial.print(" "); 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: "); lcd_chnl=get_channel(packet); r_crc[lcd_chnl] = crc_c; Serial.print(lcd_chnl); r_type[lcd_chnl] = sens_type; if (crc_c) r_isreceived[lcd_chnl] = 1; //......Если получен правильный пакет, сбрасываем таймер if(crc_c) rcv_time[lcd_chnl] = millis(); // Serial.print(" BAT: "); if(crc_c) r_bat[lcd_chnl] = get_battery(packet); if (r_bat[lcd_chnl]) Serial.print("F "); else Serial.print("e "); Serial.print("TMP: "); if(get_temperature_mask(valid_p)){ if(crc_c) r_tmp[lcd_chnl] = get_temperature(packet); //Запись максимальных и минимальных значений за сутки только из пакетов с правильной CRC if (r_tmp[lcd_chnl] >= r_tmp_max[lcd_chnl] && crc_c) r_tmp_max[lcd_chnl] = r_tmp[lcd_chnl]; if (r_tmp[lcd_chnl] <= r_tmp_min[lcd_chnl] && crc_c) r_tmp_min[lcd_chnl] = r_tmp[lcd_chnl]; Serial.print(r_tmp[lcd_chnl], 1); } else { //r_tmp[lcd_chnl] = 101; Serial.print("----"); } Serial.print("C "); if (sens_type == THGN132) { Serial.print("HUM: "); if (get_humidity_mask(valid_p)) { if(crc_c) r_hmdty[lcd_chnl] = get_humidity(packet); Serial.print(r_hmdty[lcd_chnl], 1); } else{ Serial.print("--"); //r_hmdty[lcd_chnl] = 101; } Serial.print("%"); } else Serial.print(" "); 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.println(" WRONG "); // Возвращаемся к исходному состоянию receive_status = FIND_PACKET; packet_number = 0; start_pulse_cnt = 0; sens_type = 0; lcd_chnl = 0; digitalWrite(13, LOW); } ////////////////////////////////////////////////////////////////////// //Здесь записываются ежесекундные процедуры ////////////////////////////////////////////////////////////////////// if ((millis() - timer_mark) > 1000) { timer_mark = millis(); showdots = !showdots; if ( DISPLAY_MODE == 2){ maxminshow++; if (maxminshow > 10){ maxminshow = 0; DISPLAY_MODE = 1; } } //Сброс максимумов и минимумов в полночь /////////////////////////////////////// #if RTC_PRESENT == 1 RTC_Time = Clock.getTime(); if (RTC_Time.hour == 0 && RTC_Time.min == 0){ for (int i = 0; i < 4; i++) { r_tmp_max[i] = r_tmp[i]; r_tmp_min[i]=r_tmp[i]; } } #endif { if (!reciever_status) Serial.println("RECEIVING..."); lcd.setCursor(14, 0); lcd.print(' '); #if RTC_PRESENT == 1 lcd.print(Clock.getTimeStr(FORMAT_SHORT)); lcd.setCursor(17, 0); if (showdots) lcd.print(char(7)); else lcd.print(' '); lcd.setCursor(1, 0); lcd.print(Clock.getTemp() - 1.5);//На 1.5 градуса убавим - нагревается за счёт светодиода lcd.setCursor(5, 0); lcd.print(char(0)); lcd.setCursor(3, 0); lcd.print(char(6)); //Барометр//////// //float bar=491 + analogRead(0) * 0.5833; //мБар float bar=387 + analogRead(0) * 0.4167; //ммртст if (bar_old == 0) bar_old = bar; //первое измерение if ((millis() - timer_marklong) > 10000000) { if (bar_old-bar > 5) { lcd.setCursor(12, 0); lcd.print("--"); } if (bar_old-bar > 1) { lcd.setCursor(12, 0); lcd.print('-'); } if (bar_old-bar < -1) { lcd.setCursor(12, 0); lcd.print('+'); } if (bar_old-bar < -5) { lcd.setCursor(12, 0); lcd.print("++"); } if (bar_old-bar < 2 && bar_old-bar > -2) { lcd.setCursor(12, 0); lcd.print(" "); } bar_old = bar; timer_marklong = millis(); } lcd.setCursor(7, 0); lcd.print(bar, 0); lcd.print("mm"); #endif reciever_status = 1; } reciever_ctrl = 0; // if(DISPLAY_MODE == 1){ //////////////////////////////////////////////////////////////////////////////////// //Вывод на экран полученных данных////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// for (int f = 1; f < 4; f++) { if (r_isreceived[f]) { if (true) { dysplay_tmp(0, f, r_tmp); lcd.print(" "); if (r_type[f]==THGN132) { lcd.print(r_hmdty[f]); lcd.print("% "); } else lcd.print(" "); if (!r_bat[f]) lcd.print(char(4)); else lcd.print(' '); lcd.print(" "); //lcd.setCursor(17, f); //lcd.print(" "); //lcd.setCursor(16, f); //lcd.print(" "); } if(!r_crc[f]){ lcd.setCursor(16, f); lcd.print(char(5)); } } //Запись на экран времени прихода пакетов///////////////////// for( int i=1; i < 4; i++ ){ lcd.setCursor(17, i); unsigned long cdelay = (millis() - rcv_time[i]) / 60000; if (cdelay< LOST1) { lcd.print(char(3)); lcd.print(char(2)); lcd.print(char(1)); } if (cdelay >= LOST1 && cdelay < LOST2) { lcd.print(' '); lcd.print(char(2)); lcd.print(char(1)); } if (cdelay >= LOST2 && cdelay < LOST3) { lcd.print(' '); lcd.print(' '); lcd.print(char(1)); } if (cdelay >= LOST3) lcd.print(" "); if (cdelay > LOST4) { lcd.setCursor(0, i); for (byte zzz = 0; zzz < 19; zzz++) lcd.print(' '); } } } } if(DISPLAY_MODE == 2){ for (int f = 1; f < 4; f++) { if (r_isreceived[f]) { lcd.setCursor(0, f); lcd.print("Max"); lcd.setCursor(10, f); lcd.print("Min"); dysplay_tmp(3, f, r_tmp_max); dysplay_tmp(13, f, r_tmp_min); } } } } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// } //////////////////////////////////////////////////////////////////////////////////////////////////// //Извлекает из тактовой последовательности - битовую //Параметры: cdptr - указатель на записанную тактовую последовательность //////////////////////////////////////////////////////////////////////////////////////////////////// void get_bits(byte* cdptr){ //Сброс массивов byte* cdp=cdptr; for(bt=0 ; bt<READ_BITS*2; bt++) decode_tacts[bt]=2; for(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(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(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++; } } if (sensor_type==THN132){ pp+=8; for(int x=0; x<3; x++){ if(*pp>9) *pp-=8; pp++; } } return; } //////////////////////////////////////////////////////////////////////////////////////////////////// //Вывод на дисплей температуры //////////////////////////////////////////////////////////////////////////////////////////////////// void dysplay_tmp(int colmn, int raw, float* tmp_data){ lcd.setCursor(colmn,raw); lcd.print(" "); if(*(tmp_data+raw)<-10 && *(tmp_data+raw)>=-100){ lcd.setCursor(colmn,raw); lcd.print(*(tmp_data+raw)); } if(*(tmp_data+raw)>-10 && *(tmp_data+raw)<0){ lcd.setCursor(colmn+1,raw); lcd.print(*(tmp_data+raw)); lcd.setCursor(colmn,raw); lcd.print("- "); } if(*(tmp_data+raw)>=0 && *(tmp_data+raw)<10){ lcd.setCursor((colmn)+2,raw); lcd.print(*(tmp_data+raw)); } if(*(tmp_data+raw)>=10 && *(tmp_data+raw)<=100){ lcd.setCursor(colmn+1,raw); lcd.print(*(tmp_data+raw)); } if(*(tmp_data+raw)<=100 && *(tmp_data+raw)<=100){ lcd.setCursor(colmn+5,raw); lcd.print(char(0)); lcd.print(" "); } lcd.setCursor(colmn+3, raw); lcd.print(char(6)); lcd.setCursor(colmn+6, raw); }
Так может проблема то как раз в антенне (куске провода), а не коде как таковом, хотя он в общем то не идеален как вы выяснили?... )
Я использую cупергетеродинный приёмник с промышленно скрученной антенной, слышит вообще весь эфир вокруг, даже брелки автосигнализаций на улице у людей... ))
https://ru.aliexpress.com/item/1set-RF-module-433-Mhz-superheterodyne-receiver-and-transmitter-Support-ASK-OOK-small-size-low-power/32571703475.html?spm=2114.13010608.0.0.Mg7Hy4&detailNewVersion=&categoryId=200003315
Супергетеродинный приёмник - это, безусловно, здорово. Но это же простой путь, это не так захватывающе :-).
Меня смутило то, что сам Oregon использует простые приёмники и всё работает на приличном расстоянии. Захотелось в этом разобраться.
Что же касается антенны, то скрученная спираль уступает прямому проводу своей чувствительностью к близко расположенным проводящим предметам. В старых орегонах ставили полуволновой вибратор на скрученных спиралях. В новых погодных станциях уже прямые куски проволоки.
Кстати, при переходе с четвертьволнового на полуволновой вибратор по идее может вырасти чувствительнось приёмника, если будет согласование с входным контуром. Усиление должен быть 1.5дБ. Но вот сколько можно потерять на рассогласовании - вопрос открытый...
По расшифровке информации в Сериала может быть кое-что непонятно. Данные в посылке отображаются следующим образом:
I и O - уверенно определённые биты,
i и o - неуверенно определённые биты, нет подтверждения за счёт избыточности кода,
. - неизвестно что
Код проверен на тестовой платформе, простейший приемник с "пружинкой". Объективно дальность приема увеличилась от уличного датчика на 1,5 метра. Спасибо огромное, переделаю свой код с использованием Вашего, Porosenok! Вы проделали большую работу.
Надо будет попробовать код, т.к. у меня код как и у автора используется из сторонней ветки и информацию получаю, но не всегда корректную. Может быть с данным кодом что-либо измениться.
Доброго времени суток.
Пробовал ваш скетч из этого поста http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-scientific-433mhz?page=3#comment-233870 работает отлично, спасибо вам за работу!
Не могли бы вы добавить поддержку датчиков v.3 в скетч из этого поста:
http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-s...
Уж очень он хорошо ловит датчики v.2.
Мог бы. Но для этого надо в обязательном порядке обзавестись таким датчиком. Писать скетч без самого датчика, всё равно, что принимать роды по телефону.
Мог бы. Но для этого надо в обязательном порядке обзавестись таким датчиком. Писать скетч без самого датчика, всё равно, что принимать роды по телефону.
Однако, ваш код его не видит, точнее видит, но не расшифровывает.
Не могли бы вы посмотреть, то что приходит в serial от этого датчика? Может подправить получится?
Подправленный код из этого поста у меня работает со всеми датчиками:
http://arduino.ru/forum/proekty/chtenie-i-emulyatsiya-datchikov-oregon-scientific-433mhz?page=4#comment-237285
Ваши датчики скорее всего версии 2, но пакеты явно длиннее и у них другой формат. Можно научиься читать и их, но это делается не за пять минут и даже не за вечер. У меня, к сожалению, нет в настоящее время никакого стимула, да и возможности этим заниматься. Хотя бы потому, что UNO это может уже не потянуть по количеству необходимой памяти. Нужна будет как минимум MEGA или Wemos D1, например.
Для того, чтобы считывать длинные пакеты до конца нужно для начала увеличить количество считываемых байтов изменив в скетче пераметры READ_BITS и READ_BITS2. Но это приведёт, что 132-ые датчики будут читаться хуже - вторая резервная посылка будет читаться неправильно. Т.е. желательно переписать процедуру оцифровки канала в память. Остальное: расшифровка полученных данные, подсчёт CRC - это уже просто, если найти соответствующие руководящие материалы.
Ваши датчики скорее всего версии 2, но пакеты явно длиннее и у них другой формат. Можно научиься читать и их, но это делается не за пять минут и даже не за вечер. У меня, к сожалению, нет в настоящее время никакого стимула, да и возможности этим заниматься. Хотя бы потому, что UNO это может уже не потянуть по количеству необходимой памяти. Нужна будет как минимум MEGA или Wemos D1, например.
Для того, чтобы считывать длинные пакеты до конца нужно для начала увеличить количество считываемых байтов изменив в скетче пераметры READ_BITS и READ_BITS2. Но это приведёт, что 132-ые датчики будут читаться хуже - вторая резервная посылка будет читаться неправильно. Т.е. желательно переписать процедуру оцифровки канала в память. Остальное: расшифровка полученных данные, подсчёт CRC - это уже просто, если найти соответствующие руководящие материалы.
У меня Mega 2560.
Вас понял, жаль.
Здравствуйте. А почему при компиляции выдаёт ошибку?
"POSITIVE" was not declared in this scope
в строке:
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Как я понял POSITIVE не задекларирована.
Можно попробовать поменять POSITIVE на 0 или 1. Но мне кажется, что это проблемы не решит, начнёт ругаться на другое место.
Вижу две причины :
- Не та библиотека для работы с индикатором,
- Где-то при копировании пропала фигурная скобка.
Здравствуйте. А почему при компиляции выдаёт ошибку?
"POSITIVE" was not declared in this scope
в строке:
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Как я понял POSITIVE не задекларирована.
Скорее всего библиотека LiquidCrystal_I2C lcd у вас не такая, как у автора.
Спасибо за проект, я взял ваш код за основу в своем проекте метеостанции. Родная станция прием все же осуществляет более стабильно, думаю дело в приемнике.
Посмотрите функцию dysplay_tmp, помоему там вкралась ошибка для температуры -10. В 1002 строчку нужно изменить <-10 на <=-10.
Здравствуйте. А почему при компиляции выдаёт ошибку?
"POSITIVE" was not declared in this scope
в строке:
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Как я понял POSITIVE не задекларирована.
Правильная библиотека тут
Посмотрите функцию dysplay_tmp, помоему там вкралась ошибка для температуры -10. В 1002 строчку нужно изменить <-10 на <=-10.
Спасибо, поправлю. На днях как раз решил доловить клопов в скетче. Заодно и в класс завернуть, для удобства подключения.
Что касается лучшего приёма у родной станции - так она же от батареек работает, и процессор там медленный и экономный - помех практически нет. У меня зреет идея организовать не только отдельное питания приёмника, но и попробовать его полностью гальванически отвязать.
Посмотрите функцию dysplay_tmp, помоему там вкралась ошибка для температуры -10. В 1002 строчку нужно изменить <-10 на <=-10.
Спасибо, поправлю. На днях как раз решил доловить клопов в скетче. Заодно и в класс завернуть, для удобства подключения.
Что касается лучшего приёма у родной станции - так она же от батареек работает, и процессор там медленный и экономный - помех практически нет. У меня зреет идея организовать не только отдельное питания приёмника, но и попробовать его полностью гальванически отвязать.
Не могли бы вы выложить используемые вами библиотеки? Что то не хочет компилироваться скетч с библиотеками из сети.
Дайте немного времени, допилю свою бибиотеку и выложу всё сразу...
Дайте немного времени, допилю свою бибиотеку и выложу всё сразу...
Вы хотите часть скетча перенести в библиотеку?
Если так, что плохо, поскольку ваш скетч видит только два типа орегоновских датчика.
Нет, это наоборот хорошо. Это позволит сократить код скетча, сделать его более читаемым, и упростит подключение другх библиотек для чтение других типов датчиков.
Подскажите, а из-за чего видно эти два датчика
второй (0xBB) является ретранслятором на ардуино,
но не видно этот датчик
который лежит рядом с приёмником и является почти (канал другой) точной копией 0xBB ?
Хотя с этого датчика сигнал принимается достаточно стабильтно и успешно:
25.934 OSV2 1A2D43BB992040334B56 4 bb 20.9 34% 9
26.273 OSV2 1A2D43BB992040334B56 4 bb 20.9 34% 9
Без осциллограмм сложно сказать. Скорее всего он не точная копия. По провалам в центре могу предположить, что незначительно отличается частота передачи данных. Родная станция, я так понимаю, видит эту копию?
Да, вы правы, ваш код даже строже чем базовая станция, расхождение в микросекунду чувствует... )
Подкорректировал задержку - заработало.
Кстати, есть орегон датчики у которых каналы не с 1-3, а с 1 до 5. У таких 3-й канал это 0x33, а 4-й канал кодируется как 0x43, ваш код такие не видит.
Кстати, есть орегон датчики у которых каналы не с 1-3, а с 1 до 5. У таких 3-й канал это 0x33, а 4-й канал кодируется как 0x43, ваш код такие не видит.
Это-то несложно поправить, особенно если длина пакета не превышает THGN132.
ваш код даже строже чем базовая станция, расхождение в микросекунду чувствует... )
К сожалению, пока так. Но фирменные датчики очень чётко выдерживают частоту передачи данных, род них доработка не требуется...
У меня на других моих ардуинах этот код ничего не слышит... Может конечно китайские ардуины глючные, но вообще странно это очень. Единственное что заметил, импульсы от передатчика приходят в интервалах
444..528
928..1032
Не знаю, что и сказать. Китайские арудины, конечно, иные по настройкам, но на приём это влиять не должно
Добрый день, Porosenok, а поделитесь пожалуйста кодом передатчика.
Спасибо за проект, использую библиотеку Oregon_NR, существует ли возможность добавить расшифровку датчика BTHGN129, в мониторе порта датчик определяется
Конечно возможность есть. Нет в наличии датчика и свободного времени. Задача не совсем простая - времени нужно много
Подскажите, используя вашу библиотеку почему при просмотре инфы с порта идет информация о посылках 1.2. а потом только то, что выводишь через print ? можно это как-то отключить??
oregon.capture(0);
Добрый вечер!
На днях один из датчиков Oregon Scientific THGN132N (уличный), "мявкнув" в эфир несколько некорректных значений, перестал подавать признаки жизни. Замена элемента питания результата не принесла. Путем долгого поиска, удалось выяснить, что у самих метеостанций нередко выходят из строя кварцы. Кварц находится рядом с "кляксой", с обратной стороны. Номинал 32 кГц. Лучше брать с материнских плат или калькуляторов. Все работает.
Да, я тоже за 10 лет эксплуатации двух таких метеостанций этот часовой кварц менял неоднократно.
у меня другая проблема... сдох на нем датчик влажности... заменил на похожий с али.. точность приемлимая
Извините пожалуйста. Как можно с вами связаться. Если есть возможность напишите пожалуйста в почту silverjam2k@gmailточкаcom
Спасибо.
кому адресовано-то???
Сорри. Porosenok