• Реклама

Декодирование сигналов датчиков метеостанции 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
 
Сообщения: 4146
Последний визит: 22 сен 2018, 19:34
Откуда: Сургут - Москва
Благодарил (а): 218 раз.
Поблагодарили: 236 раз.


Реклама

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

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

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

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

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


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
 
Сообщения: 4146
Последний визит: 22 сен 2018, 19:34
Откуда: Сургут - Москва
Благодарил (а): 218 раз.
Поблагодарили: 236 раз.


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

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

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

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

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


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

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

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

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

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


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

Непрочитанное сообщение Georgi47 » 25 фев 2018, 22:15

Заработало! Шайтан-программа, чесслово! :) Снимаю шляпу!
На самом деле, заработало с пол-тычка, но сначала я не на тот пин подключил приемник, а потом у меня у Arduino IDE крышу снесло.
Несколько интересных выводов/наблюдений:
1.Дальность приема метров 10 в квартире, а мне надо 30 на даче. Там меньше наводок, наверное, и можно поиграть с антенной и настройкой приемника.
2.На разных каналах датчики почему-то передают показания с разной периодичностью - 57 сек на первом, 68 на втором, и 79 на третьем. Ну примерно так. Похоже, что разница в 10 секунд как раз. Наверное чтобы снизить вероятность наложения.
3.Самое интересное - пока ковырялся, пришел к выводу, что оно мне не очень и надо :) - все равно в теплице придется делать несколько разных датчиков, и удобнее все их прямо в ардуино и заводить. Но если датчики метеостанции будут коннектиться, то это будет приятным бонусом.

А что за экран e-ink? Где продается, сколько стоит?

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

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


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

Непрочитанное сообщение Hot Rod » 25 фев 2018, 22:37

с 1 и 2 согласен. :smile:

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


e-ink - это как в электронной книге, потребление идет только в момент смены картинки, а потом в статике практически не жрет энергию.
Я покупал на Али, трехцветный (черный, белый и красный) E-Ink дисплей. Большой минус в долгом обновлении (~ 14 сек) и нельзя обновить часть экрана, только весь. У 2-х цветной (черно-белой) обновление до 2-4-х сек, у некоторых моделей возможно частичное обновление экрана.

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

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


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

Непрочитанное сообщение Georgi47 » 17 мар 2018, 12:40

По поводу GSM модуля.

Модуль SIM800L вот отсюда:
https://ru.aliexpress.com/item/Smallest ... 0.0.lMeCGI

И к нему DC-DC понижающий преобразователь:
https://ru.aliexpress.com/item/Free-Shi ... 0.0.lMeCGI

Основные идеи почерпнуты отсюда:
http://www.2150692.ru/faq/62-gprs-svyaz ... -i-arduino

Хорошее описание как подключать здесь:
http://codius.ru/articles/GSM_модуль_SIM800L_часть_1

И здесь много о проблемах, в основном про питание:
http://arduino.ru/forum/obshchii/sim800l-mini

Довольно долго ковырялся с запуском из-за неправильного питания. Модуль в короткие моменты подключения потребляет до 2 ампер (3.7-4.2 вольт), поэтому пока не нашел источника, который обеспечивал достаточное напряжение преобразователю на нагрузке в 2.5 ома, чтобы преобразователь держал 4 вольта на выходе, подключения к сети не происходило. Выглядело это как серии по 7-10 коротких вспышек с секундной паузой. А, и еще пришлось пропаять все провода от БП к преобразователю и от преобразователя к модулю.
Кстати, в описании на Али нарисована рекомендуемая схема питания - от 5В 0.5А через диод и с конденсатором 1000 мкф, допускаю, что такой вариант может и будет работать. Конденсатор наверняка улучшил бы ситуацию и с моим вариантом с использованием DC-DC преобразователя, может заработало бы с не самым мощным БП. Но у меня не было ни конденсатора, ни диода.

Ну и вот скетч, принимающий с дачтиков Dykie данные, и отсылающий их на страничку:
Код: Выделить всё
#include <SoftwareSerial.h>

#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           // длина таймаута в сек между принятыми посылками от данного канала, для признания данных потерянными


// переменные обработки сигнала
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;           // влажность и влажность по каналам
int batt = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 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;

//SIM800L
String Cmd;

SoftwareSerial GSMport(8, 9); // RX, TX

// ================================== Определяем сигнал синхронизации
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 + 24) % SIGN_BUFFER; i != (syncIdx1 + 29) % SIGN_BUFFER; i = (i + 1) % SIGN_BUFFER) {
    t = timings[i];
    if (t == 1) {
      batt = (batt << 1) + 1;
    } else if (t == 0) {
      batt = (batt << 1) + 0;
    }
  }
 
  // влажность для датчиков 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;                      // записываем в переменную влажности для этого канала
    b1 = batt;
    lastTime1 = timer / 1000;       // записываем время приема посылки по этому каналу
    timeoutState1 = 0;             // если пришла посылка по этому каналу, то данные стали актуальными
  } else if (ch == 2) {
    t2 = temp;
    h2 = humm;
    b2 = batt;
    lastTime2 = timer / 1000;
    timeoutState2 = 0;
  } else if (ch == 3) {
    t3 = temp;
    h3 = humm;
    b3 = batt;
    lastTime3 = timer / 1000;
    timeoutState3 = 0;
  } else if (ch == 4) {
    t4 = temp;
    b4 = batt;
    lastTime4 = timer / 1000;
    timeoutState4 = 0;
  }
}


// ===================================  Присваиваем статусы таймаута (для отображения актуальности принятых данных)
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;
  }
}


// ===================================  вывод данных Serial
void outputSerial () {
     //    Serial.print(IdTransmitState1);
    Serial.print(millis()/1000);
    if (ch==2) Serial.print("                         ");
    if (ch==3) Serial.print("                                                  ");
    Serial.print(" ch: "); 
    Serial.print(ch);
    Serial.print("  ");
    Serial.print(temp / 10.0, 1);
    Serial.print("\xC2\xB0, ");
    if (ch!=4) {
      Serial.print(humm);
      Serial.print("%, ");
    }
    /*Serial.print(batt);
      Serial.print("%, ");*/
   
    Serial.print(checkTime1); // время от последней посылки по этому каналу
    Serial.println();
}

void outputInet(){
   Cmd="channel="+String(ch)+"&temperature="+String(temp)+"&humidity="+String(humm)+"&checktime="+String(checkTime1);
   Serial.println(Cmd);
   gprs_send(Cmd);
}

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

  //  прерывание
  attachInterrupt(0, handler, RISING); // при смене значения с LOW на HIGH

  // для отладки
  Serial.begin(9600);
  Serial.println("Meteo: Dykie+SIM800L");

  Serial.println("Waiting for connection... (20 sec)");
  delay(3000); //дадим время на инициализацию GSM модулю
  GSMport.begin(9600);
  gprs_init();
}

// =====================
void loop() {
  if (GSMport.available()) {  //если GSM модуль что-то послал нам, то
    Serial.println(ReadGSM());  //печатаем в монитор порта пришедшую строку
  }

  // постоянно выполняемые
  assignStatusTimeout ();   // Вычисляем таймауты (для определения актуальности принятых данных)
 
  // если определились синхросигналы 1 и 3, то обрабатываем принятый сигнал
  if (received == true) {
    detachInterrupt(1);     // откл прерывание
    convertData ();         // преобразование данных
    //    checkID ();             // проверяем изменение ID передатчика
    outputSerial ();        // вывод для отладки
    outputInet();        // отправка на сервер
       
        delay(500);    // задержка, чтобы избежать повторений
    received = false;
    ch = 0;
    temp = 0;
    humm = 0;
    batt = 0;

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

void gprs_init() {  //Процедура начальной инициализации GSM модуля
  int d = 500;
  int ATsCount = 7;
  String ATs[] = {  //массив АТ команд
    "AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"",  //Установка настроек подключения
    "AT+SAPBR=3,1,\"APN\",\"internet.beeline.ru\"",
    "AT+SAPBR=3,1,\"USER\",\"beeline\"",
    "AT+SAPBR=3,1,\"PWD\",\"beeline\"",
    "AT+SAPBR=1,1",  //Устанавливаем GPRS соединение
    "AT+HTTPINIT",  //Инициализация http сервиса
    "AT+HTTPPARA=\"CID\",1"  //Установка CID параметра для http сессии
  };
  int ATsDelays[] = {6, 1, 1, 1, 3, 3, 1}; //массив задержек
  Serial.println("GPRG init start");
  for (int i = 0; i < ATsCount; i++) {
    Serial.println(ATs[i]);  //посылаем в монитор порта
    GSMport.println(ATs[i]);  //посылаем в GSM модуль
    delay(d * ATsDelays[i]);
    Serial.println(ReadGSM());  //показываем ответ от GSM модуля
    delay(d);
  }
  Serial.println("GPRS init complete");
}

void gprs_send(String data) {  //Процедура отправки данных на сервер
  //отправка данных на сайт
  int d = 400;
  Serial.println("Send start");
  Serial.println("setup url");
  GSMport.println("AT+HTTPPARA=\"URL\",\"http://trvls.co.nf/phptest/meteo1.php?" + data + "\"");
  delay(d * 2);
  Serial.println(ReadGSM());
  delay(d);
  Serial.println("GET url");
  GSMport.println("AT+HTTPACTION=0");
  delay(d * 2);
  Serial.println(ReadGSM());
  delay(d);
  Serial.println("Send done");
}

String ReadGSM() {  //функция чтения данных от GSM модуля
  int c;
  String v;
  while (GSMport.available()) {  //сохраняем входную строку в переменную v
    c = GSMport.read();
    v += char(c);
    delay(10);
  }
  return v;
}


Вот php-код страницы приема данных (http://trvls.co.nf/phptest/meteo1.php?c ... ecktime=57):
Код: Выделить всё
<?php
 // http://trvls.co.nf/phptest/test2.php?name=George&age=49

 if (!empty($_GET["channel"])&&!empty($_GET["temperature"])&&!empty($_GET["humidity"])&&!empty($_GET["checktime"]))
 { echo " New data: the channel is - ".$_GET["channel"].", the temperature is - ".$_GET["temperature"].", the humidity is - ".$_GET["humidity"].", the checktime is - ".$_GET["checktime"];}
 else { echo "Empty data. Please repeat"; }

$myFile = "meteo1.txt";
$fh = fopen($myFile, 'a') or die("can't open file");
$stringData = date("d.m.Y H.i.s").";channel:".$_GET["channel"].";temperature:".$_GET["temperature"].";humidity:".$_GET["humidity"].";checktime:".$_GET["checktime"]."\n";
fwrite($fh, $stringData);

fclose($fh);

?>


Вот php-код страницы просмотра принятого (http://trvls.co.nf/phptest/meteo1view.php):
Код: Выделить всё
<?php
    $lines = file('meteo1.txt');
    foreach($lines as $single_line)
        echo $single_line . "<br />\n";
?>


PS Мне показалось, что параллельно с работой gsm-модуля дальность приема по 433мгц сократилась буквально до 50 см, но особо не исследовал.

За это сообщение автора Georgi47 поблагодарил:
Hot Rod(20 мар 2018, 13:05)

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

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


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

Непрочитанное сообщение Hot Rod » 20 мар 2018, 13:07

Спасибо, на досуге поковыряю свою приблуду в данном направлении :smile:

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

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



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

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

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

  • Реклама

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

cron