• Реклама

Декодирование сигналов датчиков метеостанции DYKIE и ардуино

Передатчики, приемники, регуляторы скорости, сервоприводы

Декодирование сигналов датчиков метеостанции DYKIE и ардуино

Непрочитанное сообщение Hot Rod » 04 дек 2017, 15:41

Есть у меня необходимость отслеживать температуру в разных зонах и есть китайская метеостанция DYKIE RS 8718 с четырьмя беспроводными датчиками.

DYKIE.jpg

Задался я вопросом как можно принимать и декодировать сигналы с этих датчиков с помощью ардуино. Скорее из спортивного интереса я занялся этим вопросом, потому как заинтересовался попавшей мне в руки ардуинкой: стало интересно сделать какой-либо проект самому после стандартного обучающего курса по ардуино с миганием диодов, кнопкой, буззером и дисплеем. Есть у меня и более распространенная метеостанция Oregon, но декодирование ее сигналов уже давно расписано по всему интернету и скетчей написано немало, а на DYKIE я не нашел такой информации.

Я использовал логический анализатор Saleae Logic и обычный модуль-приемник 433 для ардуино. Запитав приемник и подключив к анализатору выявил следующее:
- беспроводной датчик передает посылку из 6-ти повторяющихся пакетов, разделенных относительно длинной паузой.
Saleae-00.JPG


Разбираем один пакет из шести:
Saleae-04-02.JPG

Импульсы все одинаковой длины - примерно по 600 мкс
Saleae-03-01.JPG

а вот спады (паузы) - отличаются. Есть три длины спада:

самый продолжительный - разделяющий одинаковые пакеты, примерно 4 мс (4000 мкс)
Saleae-04-03.JPG

короткий, примерно 900 мкс
Saleae-02-02.JPG

и длинный - 1950 мкс
Saleae-01-02.JPG

Делаем выводы - короткий спад - это "0", длинный спад - это "1", а самый продолжительный - разделитель пакетов, назовем его сигналом синхронизации.

Итак, мы получили пакет в двоичном коде, осталось понять в каких его частях и каким способом закодированы температура, влажность и номер канала. Это мы делаем записав анализатором несколько пакетов от датчика с разной температурой, влажностью и каналом.
Excel-00.JPG

Потратив на анализ некоторое время мы видим, что в отличии от Oregon`a никакого манчестерского или другого кодирования в этой китайской метеостанции нет, все просто: 9-11 биты - это номер канала датчика, с 12 по 23 - температура, умноженная на 10, а 28-35 биты - влажность. Последним идет сигнал-разделитель пакетов (сигнал синхронизации).
Я не стал разбираться что означают остальные биты в пакете, для моих целей это не нужно, но подозреваю, что биты 0-7 - это обновляемый при каждом включении датчика (при сбросе питания) ИД датчика, а в битах 8 или 24-28 - возможно есть состояние батареек.

Осталось написать скетч для ардуинки. Он у меня почти дописан, остались мелкие к нему хотелки типа сигнализации о выходе из заданного диапазона или продолжительном отсутствии сигнала от датчика, ну и экранчик я хочу прикрутить побольше, а то OLED 0.96 слишком мал для отображения всех моих хотелок даже очень мелким шрифтом. Но об этом как-нибудь в другой раз, если кому-то будет интересно.

Аватара пользователя

Hot Rod
aka RC86.ru
aka RC86.ru
 
Сообщения: 4140
Последний визит: 22 фев 2018, 13:37
Откуда: Сургут - Москва
Благодарил (а): 217 раз.
Поблагодарили: 236 раз.


Реклама

Re: Декодирование сигналов датчиков метеостанции DYKIE

Непрочитанное сообщение Georgi47 » 13 фев 2018, 23:52

Интересно!
Имею ту же метеостанцию, заманчиво было бы её датчики использовать.
Я это все хотел бы собрать и раз в .. не знаю, минут в 10 отправлять на сервер, а с него уже смотреть что там происходит, не пора ли картошку сажать :))))
Вот я чего не понимаю - как выделить из всего мусора в эфире именно последовательности датчиков? Ведь в начале никакого кода чтобы зацепиться

Рандомная аватара. Подставляется автоматически при отсутствии установленной пользователем аватары. Рекомендуется менять на свою в Центре пользователя.

Georgi47
Новичок
Новичок
 
Сообщения: 2
Последний визит: 21 фев 2018, 00:22
Откуда: Miscow
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.


Re: Декодирование сигналов датчиков метеостанции DYKIE

Непрочитанное сообщение Hot Rod » 14 фев 2018, 01:04

Georgi47 писал(а):Вот я чего не понимаю - как выделить из всего мусора в эфире именно последовательности датчиков? Ведь в начале никакого кода чтобы зацепиться

В начале есть ИД передатчика, но я решил, что мне будет достаточно подсчитать кол-во бит между 1-ым и 3-им синхросигналами, а так же сравнить 1 и 2 пакеты в посылке.

На сервер я не отправляю, еще не разбирался с этим. Я отправляю данные через nRF24 на центральную Мегу, к которой подключен большой e-Ink дисплей, а к этому первому устройству у меня подключен только маленький OLED 0.91". Но в коде закомментирован и остался предыдущий вариант c выводом в 4 строки на 0.96 OLED,
там же есть и ID передатчиков, но я ограничился подсчетом бит в посылке.
20180213_223247 — копия.jpg


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

На качество и тем более красоту кода не претендую, т к с ардуино и вообще программированием я не был до этого знаком и это первый мой проект на ардуино после мигания светодиодами и нажимания кнопок :smile:

Код: Выделить всё
// Written by Hot Rod
// http://rc86.ru/phpbb3/viewtopic.php?p=26857#p26857

#include <SPI.h>                                          // Подключаем библиотеку для работы с шиной SPI
#include <RF24.h>                                         // Подключаем библиотеку для работы с nRF24L01+
#include <nRF24L01.h>                                     // Подключаем файл настроек из библиотеки RF24

#include "U8glib.h"

//U8GLIB_SSD1306_128X64 u8g(10, 9, 12, 11, 13);  // для SPI OLED 1306 0.96" 128*64, SCK(SCL)-10, MOSI(SDA)-9, CS-12, A0(DC)-11, reset(RST)-13
U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);  // I2C / TWI  //  для I2C OLED 1306 0.91" 128*32, пины OLED 1306: SCL - A5, SDA - A4, VCC: (DC 3.3 ~ 5V)
//U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_FAST); //  для I2C OLED 1106 1.3" 128*64, пины OLED 1106: SCK - A5, SDA - A4

#define DATA_PIN  3             // Пин приемника
#define BUZ_PIN  4              // Пин буззера
#define SIGN_BUFFER  75         // Размер буфера должен вмещать как минимум 2 пакета из посылки + 3 синхросигнала
#define SYNC_LENGTH  4550       // Длина сигнала синхронизации               
#define BIT1_LENGTH  2500       // Длина "1"
#define BIT0_LENGTH  1500       // Длина "0"
#define VARIATION_LENGTH 500    // возможное отклонение длинн сигналов в пакетах
#define DATA_TIMEOUT 240        // длина таймаута в сек между принятыми посылками от данного канала, для признания данных устаревшими
#define DATA_LOST 600           // длина таймаута в сек между принятыми посылками от данного канала, для признания данных потерянными

// для nRF24
RF24 radio(5, 6);             // Создаём объект radio, указывая выводs (CE, CSN).  Connection: 13 (SCK), 12 (MISO), 11 (MOSI), 3.3, gnd - gnd,
int data[13];                                // Создаём массив для передачи данных
unsigned long transmissionInterval = 30000;       // интервал передачи данных

// переменные обработки сигнала
unsigned char timings[SIGN_BUFFER];   // Массив значений по длине сигналов
unsigned char syncIdx1 = 0;           // первый синхросигнал
unsigned char syncIdx3 = 0;           // третий синхросигнал
bool received = false;                // статус приема посылки

// переменные для записи принятых раскодированных данных
int ch = 0;                                     // канал датчика
int temp = 0, t1 = 0, t2 = 0, t3 = 50, t4 = 0;  // температура и температуры по каналам, t3 установлен в диапазоне, исключающем буззер до приема первого пакета по этому каналу
int humm = 0, h1 = 0, h2 = 0, h3 = 0;           // влажность и влажность по каналам

// переменные для проверки ID передатчиков
/*unsigned char IdTransmitNow = 0, IdTransmitNow1 = 0, IdTransmitNow2 = 0, IdTransmitNow3 = 0, IdTransmitNow4 = 0;
  unsigned char IdTransmitPrev1 = 0, IdTransmitPrev2 = 0, IdTransmitPrev3 = 0, IdTransmitPrev4 = 0;
  bool IdTransmitState1 = true, IdTransmitState2 = true, IdTransmitState3 = true, IdTransmitState4 = true;
*/

// переменные для рассчета таймаутов приема посылок по каждому каналу
unsigned long lastTime1 = 0, lastTime2 = 0, lastTime3 = 0, lastTime4 = 0;
unsigned long checkTime1 = 0, checkTime2 = 0, checkTime3 = 0, checkTime4 = 0;
unsigned char timeoutState1 = 0, timeoutState2 = 0,  timeoutState3 = 0,  timeoutState4 = 0;

// переменные для буззера (заданы так, чтобы при старте был короткий "пилик")
char buzCount = 0, buzMax = 2;                // запуск и нужное число циклов буззера
int buzTone1 = 600, buzTone2 = 900;           // частота1 и частота2 буззера, если частота2 == 0, то "noTone"
int buzLength1 = 100, buzLength2 = 100;       // длина1 и длина2 буззера
unsigned char alarm = 0;

// ================================== Определяем сигнал синхронизации
bool isSync(unsigned char idx) {
  unsigned char v = timings[idx];
  if ( v == 4 ) {
    return true;
  }
  return false;
}

// =================================== Обработчик прерывания и проверка пакетов
void handler() {

  static unsigned long lastTime = 0;
  static unsigned int ringIdx = 0;
  static unsigned int syncCount = 0;
  unsigned long duration = 0;
  unsigned char value = 0;
  unsigned int t = 0, i = 0;
  unsigned long pack1 = 0, pack2 = 0;

  unsigned long time = micros ();  // расчет времени с момента последнего изменения
  duration = time - lastTime;
  lastTime = time;

  if (duration > BIT1_LENGTH - VARIATION_LENGTH && duration < BIT1_LENGTH + VARIATION_LENGTH) {  // рассчет значений по длине сигнала
    value = 1;
  } else if (duration > BIT0_LENGTH - VARIATION_LENGTH && duration < BIT0_LENGTH + VARIATION_LENGTH) {
    value = 0;
  } else if (duration > SYNC_LENGTH - VARIATION_LENGTH && duration < SYNC_LENGTH + VARIATION_LENGTH) {
    value = 4;
  }

  ringIdx = (ringIdx + 1) % SIGN_BUFFER;  // значения в массив
  timings[ringIdx] = value;

  // обнаружение сигналов синхронизации 1 и 3 (два пакета для последующего сравнения)
  if (isSync(ringIdx)) {
    syncCount ++;
    if (syncCount == 1) {  // первая синхронизация найдена
      syncIdx1 = (ringIdx + 1) % SIGN_BUFFER;
    } else if (syncCount == 3) {  // третья синхронизация найдена, начинаем преобразование
      syncCount = 0;
      syncIdx3 = (ringIdx + 1) % SIGN_BUFFER;

      // делаем проверки по длине двух пакетов и значениям
      unsigned char changeCount = (syncIdx3 < syncIdx1) ? (syncIdx3 + SIGN_BUFFER - syncIdx1) : (syncIdx3 - syncIdx1);
      if (changeCount == 74) {          // changeCount должен быть 74  (36 бит пакет + 1 для синхронизации * 2 раза)


        // если да, то сравниваем первый и второй пакеты между собой
        for (i = (syncIdx1 + 4) % SIGN_BUFFER; i != (syncIdx1 + 36) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER)  {
          t = timings[i];
          if (t == 1) {
            pack1 = (pack1 << 1) + 1;
          } else if (t == 0) {
            pack1 = (pack1 << 1) + 0;
          }
        }
        for (i = (syncIdx1 + 41) % SIGN_BUFFER; i != (syncIdx1 + 73) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER)  {
          t = timings[i];
          if (t == 1) {
            pack2 = (pack2 << 1) + 1;
          } else if (t == 0) {
            pack2 = (pack2 << 1) + 0;
          }
        }

        // сравниваем полученные пакеты для исключения ошибок
        if ((pack1 == pack2) &&             // если первый и второй пакеты равны
            (pack1 != 0 )) {                // и не равны 0 (почему-то у меня часто проходили нулевые пакеты)
          received = true;                  // то все в порядке, начинаем преобразование
        }

      } else {
        received = false;               // иначе - отбой
        syncIdx1 = 0;
        syncIdx3 = 0;
      }
    }
  }
}

// ===================================  извлекаем данные из принятой передачи
void convertData () {
  unsigned int t = 0, i = 0;                    // рабочие переменные

  // номер канала
  for (i = (syncIdx1 + 9) % SIGN_BUFFER; i != (syncIdx1 + 12) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER)  {
    t = timings[i];
    if (t == 1) {
      ch = (ch << 1) + 1;
    } else if (t == 0) {
      ch = (ch << 1) + 0;
    }
  }
  ch = ch + 1;    // для приведения номера канала в соответствие с обозначениями на базовой станции (от 1 до 4)

  // температура
  if (ch == 4) {  // температура для датчика 4 (у него другое расположение данных в пакете и нет влажности)
    for (i = (syncIdx1 + 21) % SIGN_BUFFER; i != (syncIdx1 + 32) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER) {
      t = timings[i];
      if (t == 1) {
        temp = (temp << 1) + 1;
      } else if (t == 0) {
        temp = (temp << 1) + 0;
      }
    }
  }

  else if (ch < 4) {  // температура для датчиков 1 - 3
    for (i = (syncIdx1 + 12) % SIGN_BUFFER; i != (syncIdx1 + 24) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER) {
      t = timings[i];
      if (t == 1) {
        temp = (temp << 1) + 1;
      } else if (t == 0) {
        temp = (temp << 1) + 0;
      }
    }
  }

  // Если t отрицательная, то преобразуем
  if (temp > 1000) {
    temp = temp - 4096;
  }

  // влажность для датчиков 1 - 3
  for (i = (syncIdx1 + 29) % SIGN_BUFFER; i != (syncIdx1 + 36) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER) {
    t = timings[i];
    if (t == 1) {
      humm = (humm << 1) + 1;
    } else if (t == 0) {
      humm = (humm << 1) + 0;
    }
  }

  // пишем данные в переменные для вывода на экран разных каналов в разных строках
  // и записываем время прихода посылки по каждому каналу в переменную
  unsigned long timer = millis ();
  if (ch == 1) {                    // проверяем номер канала
    t1 = temp;                      // записываем в переменную температуры для этого канала
    h1 = humm;                      // записываем в переменную влажности для этого канала
    lastTime1 = timer / 1000;       // записываем время приема посылки по этому каналу
    timeoutState1 = 0;             // если пришла посылка по этому каналу, то данные стали актуальными
  } else if (ch == 2) {
    t2 = temp;
    h2 = humm;
    lastTime2 = timer / 1000;
    timeoutState2 = 0;
  } else if (ch == 3) {
    t3 = temp;
    h3 = humm;
    lastTime3 = timer / 1000;
    timeoutState3 = 0;
  } else if (ch == 4) {
    t4 = temp;
    lastTime4 = timer / 1000;
    timeoutState4 = 0;
  }
}

// ==================================== сравниваем ID приемника с полученным в предыдущей посылке
// не пригодилось

/*void checkID () {

  //unsigned char IdTransmitNow = 0, IdTransmitNow1 = 0, IdTransmitNow2 = 0, IdTransmitNow3 = 0, IdTransmitNow4 = 0;  // переменные для проверки ID передатчиков
  //static unsigned char IdTransmitPrev1 = 0, IdTransmitPrev2 = 0, IdTransmitPrev3 = 0, IdTransmitPrev4 = 0;                 // переменные для проверки ID передатчиков

  if (ch == 1) {                                // если это 1 канал
    IdTransmitNow1 = IdTransmitNow;             // то это ID первого канала
    if (IdTransmitNow1 == IdTransmitPrev1) {    // если ID этой посылки равно ID предыдущей посылки
      IdTransmitState1 = true;                  // то статус ID - true
    } else {                                    // иначе
      IdTransmitPrev1 = IdTransmitNow1;         // записываем ID как ID последней посылки
      IdTransmitState1 = false;                 // и статус ID - false
    }
  }

  else if (ch == 2) {
    IdTransmitNow2 = IdTransmitNow;
    if (IdTransmitNow2 == IdTransmitPrev2) {
      IdTransmitState2 = true;
    } else {
      IdTransmitPrev2 = IdTransmitNow2;
      IdTransmitState2 = false;
    }
  }

  else if (ch == 3) {
    IdTransmitNow3 = IdTransmitNow;
    if (IdTransmitNow3 == IdTransmitPrev3) {
      IdTransmitState3 = true;
    } else {
      IdTransmitPrev3 = IdTransmitNow3;
      IdTransmitState3 = false;
    }
  }

  else if (ch == 4) {
    IdTransmitNow4 = IdTransmitNow;
    if (IdTransmitNow4 == IdTransmitPrev4) {
      IdTransmitState4 = true;
    } else {
      IdTransmitPrev4 = IdTransmitNow1;
      IdTransmitState4 = false;
    }
  }
  }
*/
// ===================================  Присваиваем статусы таймаута (для отображения актуальности принятых данных)
void assignStatusTimeout () {
  unsigned long timer = millis ();
  // расчет таймаута (времени с момента последнего приема пакетов по каналу)
  checkTime1 = timer / 1000 - lastTime1;                        // вычисляем время от предыдущей посылки по этому каналу
  if (DATA_TIMEOUT < checkTime1 && checkTime1 <= DATA_LOST) {   // если прошло времени больше timeOut, но меньше lost,
    timeoutState1 = 1;                                         // то данные устарели
  } else if (DATA_LOST < checkTime1 && checkTime1 < 60000) {     // если прошло времени больше lost,  (< 60000 - для случая переполнения)
    timeoutState1 = 2;                                          // то данные совсем не актуальны (утеряны),
  }
  checkTime2 = timer / 1000 - lastTime2;
  if (DATA_TIMEOUT < checkTime2 && checkTime2 <= DATA_LOST) {
    timeoutState2 = 1;
  } else if (DATA_LOST < checkTime2 && checkTime2 < 60000) {
    timeoutState2 = 2;
  }
  checkTime3 = timer / 1000 - lastTime3;
  if (DATA_TIMEOUT < checkTime3 && checkTime3 <= DATA_LOST) {
    timeoutState3 = 1;
  } else  if (DATA_LOST < checkTime3 && checkTime3 < 60000) {
    timeoutState3 = 2;
  }
  checkTime4 = timer / 1000 - lastTime4;
  if (DATA_TIMEOUT < checkTime4 && checkTime4 <= DATA_LOST) {
    timeoutState4 = 1;
  }  else if (DATA_LOST < checkTime4 && checkTime4 < 60000) {
    timeoutState4 = 2;
  }
}

// ===================================  вывод данных на OLED

void draw(void) {

  u8g.setFont(u8g_font_osr29);

 
// для вывода только 3-го канала на маленький OLED 0.91
  if (timeoutState3 == 0) {
    u8g.setPrintPos(0, 32);               // установка курсора 3 строка
    if (t3 >= 100) {
      //      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 >= 0) {
      //      u8g.print("t3:  ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 < -100) {
      //      u8g.print("t3:");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else {
      //      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    }
  } else if (timeoutState3 == 1) {
    u8g.setPrintPos(0, 32);
    if (t3 >= 100) {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 >= 0) {
      u8g.print("t3:  ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 < -100) {
      u8g.print("t3:");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    }
    u8g.setPrintPos(80, 32);
    u8g.print("delay");
  } else if (timeoutState3 == 2) {
    u8g.setPrintPos(0, 32);
    u8g.print("lost");
  }

  /* // для OLED 0.96 или 1.3
    // вывод на экран разных каналов в разных строках:
    // если timeoutState1 == 0 - актуальны - пишем все данные на OLED
    // если timeoutState1 == 1 - задержаны - вместо Humm пишем "delay"
    // если timeoutState1 == 2 - утеряны - вместо t пишем "data lost"

    if (timeoutState1 == 0) {
      u8g.setPrintPos(0, 14);                 // установка курсора 1 строка
      if (t1 >= 100) {                        // если t больше или равно 100 (10.0 градусов) - это двухзначное число
        u8g.print("t1: ");                    // то ставим 1 пробел
        u8g.print(t1 / 10.0, 1);              // и выводим двузначное число с одним знаком после запятой
        u8g.print("°");                       // символ градус С
      } else if (t1 >= 0) {                   // если больше или равно 0, но меньше 100 (т к не попала в 1-е условие)
        u8g.print("t1:  ");                   // то ставим 2 пробела
        u8g.print(t1 / 10.0, 1);              // и к выводу будут только единицы с одним знаком после запятой
        u8g.print("°");                       // символ градус С
      } else if (t1 < -100) {                 // если t меньше -100 (-10.0 градусов) - это двухзначное число
        u8g.print("t1:");                     // то не ставим пробела, его место займет знак "-"
        u8g.print(t1 / 10.0, 1);              // и потом двузначное число с одним знаком после запятой
        u8g.print("°");                       // символ градус С
      } else {                                // осталась не оговоренной отрицательная t, ниже нуля, но выше -100 (-10.0 градусов)
        u8g.print("t1: ");                    // то ставим 1 пробел, место второго займет знак "-"
        u8g.print(t1 / 10.0, 1);              // и к выводу будут только единицы с одним знаком после запятой
        u8g.print("°");                       // символ градус С
      }
      u8g.setPrintPos(80, 14);                // ставим середину строки
      u8g.print("h: ");
      u8g.print(h1, DEC);                     // выводим влажность
      u8g.print("%");
    }
    else if (timeoutState1 == 1) {
      u8g.setPrintPos(0, 14);
      if (t1 >= 100) {
        u8g.print("t1: ");
        u8g.print(t1 / 10.0, 1);
        u8g.print("°");
      } else if (t1 >= 0) {
        u8g.print("t1:  ");
        u8g.print(t1 / 10.0, 1);
        u8g.print("°");
      } else if (t1 < -100) {
        u8g.print("t1:");
        u8g.print(t1 / 10.0, 1);
        u8g.print("°");
      } else {
        u8g.print("t1: ");
        u8g.print(t1 / 10.0, 1);
        u8g.print("°");
      }
      u8g.setPrintPos(80, 14);
      u8g.print("delay");
    } else if (timeoutState1 == 2) {
      u8g.setPrintPos(0, 14);
      u8g.print("t1: .. data lost");
    }

    if (timeoutState2 == 0) {
      u8g.setPrintPos(0, 30);               // установка курсора 2 строка
      if (t2 >= 100) {
        u8g.print("t2: ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else if (t2 >= 0) {
        u8g.print("t2:  ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else if (t2 < -100) {
        u8g.print("t2:");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else {
        u8g.print("t2: ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      }
      u8g.setPrintPos(80, 30);
      u8g.print("h: ");
      u8g.print(h2, DEC);
      u8g.print("%");
    } else if (timeoutState2 == 1) {
      u8g.setPrintPos(0, 30);
      if (t2 >= 100) {
        u8g.print("t2: ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else if (t2 >= 0) {
        u8g.print("t2:  ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else if (t2 < -100) {
        u8g.print("t2:");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      } else {
        u8g.print("t2: ");
        u8g.print(t2 / 10.0, 1);
        u8g.print("°");
      }
      u8g.setPrintPos(80, 30);
      u8g.print("delay");
    } else if (timeoutState2 == 2) {
      u8g.setPrintPos(0, 30);
      u8g.print("t2: .. data lost");
    }

  if (timeoutState3 == 0) {
    u8g.setPrintPos(0, 46);               // установка курсора 3 строка
    if (t3 >= 100) {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 >= 0) {
      u8g.print("t3:  ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 < -100) {
      u8g.print("t3:");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    }
    u8g.setPrintPos(80, 46);
    u8g.print("h: ");
    u8g.print(h3, DEC);
    u8g.print("%");
  } else if (timeoutState3 == 1) {
    u8g.setPrintPos(0, 46);
    if (t3 >= 100) {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 >= 0) {
      u8g.print("t3:  ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else if (t3 < -100) {
      u8g.print("t3:");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    } else {
      u8g.print("t3: ");
      u8g.print(t3 / 10.0, 1);
      u8g.print("°");
    }
    u8g.setPrintPos(80, 46);
    u8g.print("delay");
  } else if (timeoutState3 == 2) {
    u8g.setPrintPos(0, 46);
    u8g.print("t3: .. data lost");
  }


  if (timeoutState4 == 0) {
      u8g.setPrintPos(0, 62);               // установка курсора 4 строка
      u8g.print("Water. ");
      u8g.print(t4 / 10.0, 1);
      u8g.print("°");
    } else if (timeoutState4 == 1) {
      u8g.setPrintPos(0, 62);
      u8g.print("Water.");
      u8g.print(t4 / 10.0, 1);
      u8g.print("°");
      u8g.setPrintPos(88, 62);
      u8g.print("delay");
    } else if (timeoutState4 == 2) {
      u8g.setPrintPos(0, 62);
      u8g.print("Water: .... lost");
    }
  */
}

// ===================================  вывод данных Serial
void outputSerial () {
 
  /* // для отладки

    // два принятых пакета в двоичном
    for (i = syncIdx1; i != syncIdx3; i = (i + 1) % SIGN_BUFFER) {
      Serial.print(timings[i]);
    }
    Serial.println("");

    // в десятичном
    // канал
    Serial.print("ch-");  //
    Serial.print(ch); //
    Serial.print(", ");  //
    // температура
    Serial.print(temp / 10.0, 1);
    Serial.print("В°C, ");
    //     }
    // влажность
    Serial.print(humm);
    Serial.print(" %, ");
    // батарея (не уверен, есть ли отслеживание состояния батареек)
    //         Serial.print("batt_");  //
    //         Serial.print(batt);  //
    Serial.println("");  //
  */
  /*
    // общее время с момента старта в "дн-час:мин:сек,тысячные"
    Serial.print(millis () / 1000 / 60 / 60 / 24);  // дни
    Serial.print("d - ");
    Serial.print(millis () / 1000 / 60 / 60 % 24 / 10);       // часы дес.
    Serial.print(millis () / 1000 / 60 / 60 % 24 % 10);       // часы ед.
    Serial.print(":");
    Serial.print(millis () / 1000 / 60 % 60 / 10);      // минуты дес
    Serial.print(millis () / 1000 / 60 % 60 % 10);      // минуты ед
    Serial.print(":");
    Serial.print(millis () / 1000 % 60 / 10);       //  секунды дес
    Serial.print(millis () / 1000 % 60 % 10);       //  секунды ед
    Serial.print(",");
    Serial.print(millis () % 1000 / 100);       // десятые
    Serial.print(millis () % 1000  % 100 / 10); // сотые
    Serial.print(millis () % 1000 % 10);        // тысячные
    Serial.print(";  ");
  */
  /*
    // общее время с момента старта в "час:мин:сек"
    Serial.print(millis () / 1000 / 60 / 60); //
    Serial.print(":");
    Serial.print(millis () / 1000 / 60 % 60); //
    Serial.print(":");
    Serial.print(millis () / 1000 % 60); //
    Serial.print(",");
    Serial.print(millis () % 1000); //
    Serial.print(";  ");
  */
  // форматируем и выводим преобразованные данные
  // отодвигаем от левого края вывод по каждому каналу на разное расстояние для более легкого восприятия
  if (ch == 1) {
    //    Serial.print(IdTransmitState1);
    Serial.print(" ch-1: ");  //
    Serial.print(temp / 10.0, 1);
    Serial.print("\xC2\xB0, ");
    Serial.print(humm);
    Serial.print(" %, ");
    Serial.print(checkTime1); // время от последней посылки по этому каналу
  }
  else if (ch == 2) {
    Serial.print("                            ");
    //    Serial.print(IdTransmitState2);
    Serial.print(" ch-2: ");  //
    Serial.print(temp / 10.0, 1);
    Serial.print("\xC2\xB0, ");
    Serial.print(humm);
    Serial.print(" %, ");
    Serial.print(checkTime2); //

  }
  else if (ch == 3) {
    Serial.print("                                                        ");
    //    Serial.print(IdTransmitState3);
    Serial.print(" ch-3: ");  //
    Serial.print(temp / 10.0, 1);
    Serial.print("\xC2\xB0, ");
    Serial.print(humm);
    Serial.print(" %, ");
    Serial.print(checkTime3); //
  }
  else if (ch == 4) {
    Serial.print("                                                                                    ");
    //    Serial.print(IdTransmitState4);
    Serial.print(" Water: ");  //
    Serial.print(temp / 10.0, 1);
    Serial.print("\xC2\xB0, ");
    Serial.print(checkTime4); //
  }
  Serial.println("");  //
}

// ===================================  Условия для срабатывания буззера

void requirementBuzzer () {

  // срабатывает при приеме пакета на любой канал
  if (t3 < 30 || t3 > 90) { //  диапазон t3, при выходе из которого срабатывает буззер (значение t до деления на 10)
    buzCount = 0;       // "0" - запуск буззера
    buzMax = 6;         // нужное число циклов буззера
    buzTone1 = 900, buzTone2 = 0;                 // частота1 и частота2 буззера, если частота2 == 0, то noTone
    buzLength1 = 150, buzLength2 = 50;                  // длина1 и длина2 буззера
    alarm = 3;                                 // включение тревоги
  }
  else {
    alarm = 0;
    }                                 // выключение тревоги
   
  // срабатывает только при приеме посылки на 3-й канал
  if ((ch == 3) && (temp < 1 || temp > 115)) {  //  канал и диапазон t, при выходе из которого срабатывает буззер (значение t до деления на 10)
    buzCount = 0;       // "0" - запуск буззера
    buzMax = 6;         // нужное число циклов буззера
    buzTone1 = 600, buzTone2 = 900;                 // частота 1-го и 2-го сигналов буззера
    buzLength1 = 500, buzLength2 = 500;                  // длина 1-го и 2-го сигналов буззера
    alarm = 3;                                 // включение тревоги
  }
   
  // срабатывает при приеме пакета на любой канал
  if (timeoutState3 == 2) { //  если данные на 3 канал не приходят более значения "lost", бип при приеме данных на любой канал
    buzCount = 0;       // "0" - запуск буззера
    buzMax = 1;         // нужное число циклов буззера
    buzTone1 = 1200, buzTone2 = 0;                 // частота1 и частота2 буззера
    buzLength1 = 150, buzLength2 = 500;                  // длина1 и длина2 буззера
    // alarm = 3;    // включение тревоги
  }

}

// ===================================  Работа буззера
void buzzerAlarm () {   //  Буззер "сирена"

  if (buzCount > buzMax + 1)  {  // игнорировать, если буззер отработал до мах и больше не нужен
    return;
  }
  static unsigned long previousMillis;
  unsigned long currentMillis;


  if (buzCount == 0) {                  // если "0" - значит буззер только что запущен
    previousMillis = millis ();          // пишем текущее время как время последнего переключения
    tone (BUZ_PIN, buzTone1);           // вкл буззер
    buzCount = buzCount + 1;            // увеличиваем счетчик циклов
  }
  else if (buzCount == buzMax + 1) {    // если заданное кол-во циклов выполнено, то
    noTone (BUZ_PIN);                   // отключаем буззер
    buzCount = buzCount + 1;            // и увеличиваем счетчик циклов для последующих пропусков ф-ции (return)
  }
  else {                                // в других случаях продолжаем издавать звуки
    if (buzCount % 2 == 1) {            // если счетчик нечетный
      currentMillis = millis ();         // берем текущее время и
      if (currentMillis - previousMillis > buzLength2) {  //проверяем не прошел ли нужный интервал, если прошел то
        previousMillis = currentMillis;                   // сохраняем время последнего переключения
        if (buzTone2 == 0) {            // если buzTone2 == 0, то нам нужен не звук, а пауза
          noTone (BUZ_PIN);             // отключаем буззер
        }
        else {
          tone (BUZ_PIN, buzTone2);     // если частота задана отличная от "0", то выводим звук нужной частоты
        }
        buzCount = buzCount + 1;        // увеличиваем счетчик циклов
      }
    }

    else if (buzCount % 2 == 0) {            // если счетчик четный
      currentMillis = millis ();
      if (currentMillis - previousMillis > buzLength1) {
        previousMillis = currentMillis;
        tone (BUZ_PIN, buzTone1);
        buzCount = buzCount + 1;
      }
    }
  }
}

// ===================================
void transmitt () {

  static unsigned long previousMillisTransmit;
  unsigned long currentMillisTransmit;

  currentMillisTransmit = millis ();
  if (currentMillisTransmit - previousMillisTransmit > transmissionInterval) {
    previousMillisTransmit = currentMillisTransmit;

    data[0] = t1;                             // записываем t1 в 0 элемент массива data
    data[1] = h1;                             // записываем h1 в 1 элемент массива data
    data[2] = timeoutState1;                  // записываем timeoutState1 в 2 элемент массива data
    data[3] = t2;
    data[4] = h2;
    data[5] = timeoutState2;
    data[6] = t3;
    data[7] = h3;
    data[8] = timeoutState3;
    data[9] = t4;
    data[10] = timeoutState4;
    data[11] = alarm;
//    data[12] = reserve;
    radio.write(&data, sizeof(data));         // отправляем данные из массива data указывая сколько байт массива мы хотим отправить
    Serial.print("Transmitted data.");
    Serial.print("  Status Alam - ");
    Serial.println(alarm);
  }
}

// ===================================
void setup() {

  // пин приемника, буззера, прерывание
  pinMode(DATA_PIN, INPUT);
  pinMode(BUZ_PIN, OUTPUT);
  attachInterrupt(1, handler, RISING); // при смене значения с LOW на HIGH

  radio.begin();                                        // Инициируем работу nRF24L01+
  //  radio.setAutoAck(1);                // Установка режима подтверждения приема;
  radio.setDataRate     (RF24_1MBPS);                   // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек
  radio.setChannel(55);                                  // Указываем канал передачи данных (от 0 до 127), 5 - значит передача данных осуществляется на частоте 2,405 ГГц (на одном канале может быть только 1 приёмник и до 6 передатчиков)
  radio.setPALevel      (RF24_PA_MAX);                 // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm)
  radio.openWritingPipe (0x1234567890LL);               // Открываем трубу с идентификатором 0x1234567890 для передачи данных (на ожном канале может быть открыто до 6 разных труб, которые должны отличаться только последним байтом идентификатора)

  // для отладки
  Serial.begin(9600);
  Serial.println("Started _DYKIE-Receiver-052");
}

// =====================
void loop() {

  // постоянно выполняемые
  assignStatusTimeout ();   // Вычисляем таймауты (для определения актуальности принятых данных)
  buzzerAlarm ();           // работа буззера при необходимости
  transmitt ();             // передаем через nRF24

  // если определились синхросигналы 1 и 3, то обрабатываем принятый сигнал
  if (received == true) {
    detachInterrupt(1);     // откл прерывание
    convertData ();         // преобразование данных
    //    checkID ();             // проверяем изменение ID передатчика
    outputSerial ();        // вывод для отладки
    requirementBuzzer ();   // проверка условий для старта буззера

    u8g.firstPage();        // вывод на дисплей;
    do {
      draw();
    } while ( u8g.nextPage() );


    //    delay(500);    // задержка, чтобы избежать повторений
    received = false;
    ch = 0;
    temp = 0;
    humm = 0;

    // вкл прерывание
    attachInterrupt(1, handler, RISING);
  }
}

Аватара пользователя

Hot Rod
aka RC86.ru
aka RC86.ru
 
Сообщения: 4140
Последний визит: 22 фев 2018, 13:37
Откуда: Сургут - Москва
Благодарил (а): 217 раз.
Поблагодарили: 236 раз.


Re: Декодирование сигналов датчиков метеостанции DYKIE

Непрочитанное сообщение Georgi47 » 14 фев 2018, 23:51

Спасибо за код! В выходные буду разбираться/пробовать.
Передавать данные на сервер выглядит несложным, как со стороны Ардуино, так со стороны сервера. Пришлю как получится чего то

Рандомная аватара. Подставляется автоматически при отсутствии установленной пользователем аватары. Рекомендуется менять на свою в Центре пользователя.

Georgi47
Новичок
Новичок
 
Сообщения: 2
Последний визит: 21 фев 2018, 00:22
Откуда: Miscow
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.


Re: Декодирование сигналов датчиков метеостанции DYKIE и ард

Непрочитанное сообщение Hot Rod » 15 фев 2018, 08:21

Да не за что, собственно :smile:
Присылайте, интересно.

Аватара пользователя

Hot Rod
aka RC86.ru
aka RC86.ru
 
Сообщения: 4140
Последний визит: 22 фев 2018, 13:37
Откуда: Сургут - Москва
Благодарил (а): 217 раз.
Поблагодарили: 236 раз.



Вернуться в Аппаратура и электроника

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

  • Реклама

Яндекс.Метрика