Перехват данных от метеостанции Misol Arduino

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

Китайские друзья выпускают метеостанции марки Misol, которые состоят из комплекта (блока) внешних датчиков с термо-гидро передатчиком

Блок внешних датчиков метеостанции Misol.

и дисплея, на котором отображаются измеренные значения

Модуль дисплея метеостанции Misol.

Передача данных от внешних датчиков на дисплей осуществляется по радиоканалу на частоте 433 МГц. Технические характеристики определяют дальность передачи данных не более 100 метров (на открытой местности). Если передача данных происходит через радиопоглощающие препятствия, то дальность передачи данных значительно сокращается. Для повышения дальности передачи данных требуется использовать другие способы передачи, для чего требуется перехватывать данные от внешних датчиков.

Подключение к модулю передатчика Misol

Все данные с внешних датчиков собираются в термо-гидро передатчике

Термо-гидро передатчик метеостанции Misol.

Для подключения к нему необходимо три провода: Земля (черная метка — черный провод); +3,3 (синяя метка — зеленый провод); Сигнал (желтая метка — желтый провод).

Подключение к термо-гидро передатчику метеостанции Misol.

Подключение сигнала выполнено через резистор 220 Ом (в кембрике). Для подключения использовался мягкий телефонный четырехпроводный кабель. Одна жила (красная) остается свободной. Кабель выпускается наружу через приоткрытый отсек питания. К Arduino Mega сигнал подключается к выводу D2 (Interrupt 0). Остальные провода по предназначению (земля, +3,3 В).

Временные параметры пакета импульсов

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

Временные параметры импульсов пакетов метеостанции Misol.

В результате анализа временных диаграмм и сравнения с известными способами кодирования информации в радиотехнических системах было установлено, что 0 кодируется импульсом 500 мкс, 1 – 1500 мкс, а пауза составляет 1000 мкс.

 Для декодирования при приеме достаточно отслеживать нарастающий фронт передаваемого импульса и измерять его длительность (интегрировать принимаемый сигнал) и принимать решение о принимаемом символе.

Отслеживать нарастающий фронт импульсов удобно через механизм прерываний микроконтроллера Atmel (МК), на базе которого построена плата Arduino Mega.

Алгоритм декодирования символа следующий: отслеживается изменение (перепад) входного сигнала (или нарастание или спад). В случае нарастания (начало импульса) запускается измерение длительности импульса. В случае спада (конец импульса) измерение длительности останавливается. По длительности импульса принимается решение о принятом символе 0 или 1.

Дальнейший анализ проводился по результатам приема пакета Arduino.

Программа приема пакета импульсов метеостанции Misol Arduino

//  Программа декодирования последовательности передаваемых импульсов метеостанции Misol
byte bitmas[8];
byte bytemas[255];
byte rxpin = 2;              //Пин подключения выхода приемника. Вход Int0
byte RxPinStateOld, RxPinStateNew;     //Значения входа
byte rxb;                    //Принятый бит
byte nbit = 0;               //Количество принятых бит в байте
byte nbyte = 0;              //Количество принятых байт
byte vbyte;                  //Десятичное значение байта
byte errorx;                 //Ошибка приема
volatile unsigned long timpb, timpe, timp, tpause;   //Время начала, конца, длительность импульса, паузы
byte EndPacket = 1;         //Флаг окончания передачи пакета
byte impulse = 0;           //Флаг импульса: 0 - пауза, 1 - импульс
byte Int0was = 0;           //Флаг прерывания

// ***** Бит в байт *****
void BitToByte() {
  byte k = 128;
  vbyte = 0;
  for (int i = 0; i < 8; i++) {
    vbyte = vbyte + bitmas[i] * k;
    k = k / 2;
  }
} // *****

// ***** Прием импульса (Прерывание) *****
void int0rxreceive() {                //
  RxPinStateOld = RxPinStateNew;      //Сохранение предшествующего состояния
  RxPinStateNew = digitalRead(rxpin);  //Получение нового значения

  //Анализ состояний
  if (RxPinStateOld == LOW && RxPinStateNew == HIGH) { //Фронт импульса
    //Старт приема импульса
    timpb = micros(); //Фиксация времени начала импульса
    impulse = 1;      // Установка флага импульса

  }

  else if (RxPinStateOld == HIGH && RxPinStateNew == LOW) //Спад импульса
  { //Окончание приема импульса
    timpe = micros();  //Фиксация времени окончания импульса
    impulse = 0;       //Сброс флага импульса
  }

  Int0was = 1;
}      // *** Окончание приема импульса

// *** Setup ***
void setup() {
  // put your setup code here, to run once:
  attachInterrupt(0, int0rxreceive, CHANGE);    //Подпрограмма обработки принимаемого импульса на 2 (Int0) DPIN
  Serial.begin(230400);
}// *** End setup


// *** Loop ***
void loop() {

  if (Int0was == 1) {     //Обработка прерывания
    if (impulse == 0) {   //Декодирование импульса
      timp = timpe - timpb;
      if (timp > 4500 && timp < 5500) {
        rxb = 0;
      }
      else if ( timp > 12500 && timp < 15500) {
        rxb = 1;
      }        //Окончание принятия решения
      else {
        goto noimp;
      }
      bitmas[nbit] = rxb;   //Присвоение значения биту
      nbit++;               //Определение позиции следующего бита
      if (nbit == 8) {      //Проверка завершения приема байта
        nbit = 0;           //Завершение приема байта
        BitToByte();
        bytemas[nbyte] = vbyte;
        ++nbyte;
        EndPacket = 0;      //Сброс флага окончания обработки пакета
        if (nbyte == 255) {
        }
      }

noimp: {                    //Ошибка приема.

      }

    } //End Прием импульса

    //Подготовка к приему пакета
    nbit = 0;
  } // *** Окончание обработки прерывания


  // *** Определение длительности паузы
  tpause = micros() - timpe;
  if ( tpause > 10500 && EndPacket == 0 ) {  //Признак паузы
    //Пауза

    //Вывод пакета в паузе
    Serial.println (" End data packet: ");
    Serial.print(" Tpause = ");   //Отображение принятых байт
    Serial.println(tpause);
    Serial.print("RxByte(");      
    Serial.print(nbyte);
    Serial.print(") = ");
    for (byte i = 0 ; i < nbyte; i++) {
      Serial.print(bytemas[i]);
      Serial.print(", ");
    }
    Serial.println();
    Serial.println (" ***** ");
    Serial.println();
  } // *** Окончание определения длительности паузы

}//End loop

Обработка пакета данных метеостанции Misol

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

Пакет может содержать 11 (короткий) или 22 (длинный) байт данных. Длинный пакет состоит из двух коротких. Поэтому рассматриваться будет только короткий пакет. Пакет состоит из одного синхробайта и десяти информационных байт. Каждый байт (восемь бит) разделяется на четверки (тетрады (англ. — nibbles) битов.

Информационное содержание пакета данных метеостанции Misol.

Требуемое значение, в зависимости от требуемого числа значений кодируется одной, двумя или тремя тетрадами:

  • abc: Серийный номер (device identifier (SN — serial number))
  • def: Температура (temperature)
  • gh: Влажность (humidity (H %))
  • ij: Скорость ветра средняя (average wind speed (low byte) (AW))
  • kl: Скорость ветра в порыве (gust wind speed (low byte) (GW))
  • m:  Неизвестное назначение (unknown (UK))
  • n: Счетчик дождя (старшая тетрада) (rainfall counter (RC))
  • op: Счетчик дождя (младшие тетрады) (rainfall counter)
  • q: Уровень заряда батареи (battery-low indicator (LB))
  • r: Направление ветра (wind direction (DW))
  • st: Контрольная сумма (checksum (CRC))

Пример пакета данных:

Пример пакета данных с разбивкой на тетрады и привязки к значениям.

Одной тетрадой можно закодировать 16 значений; двумя — 256 значений; тремя — 4096 значений. Каждое значение представляет собой целое число, которое требуется преобразовать в заданную величину. Для преобразования применяются формулы пересчета.

Температура в градусах Цельсия (тетрады 5, 6 7) T = (Nh – 400) / 10
Пример: 2-10-3 = 2*16**2 + 10*16**1 + 3*16**0 = 512 + 160 + 3 = 675
T = (675 – 400)/10 = 27.5

Влажность H % (тетрады 8, 9) отображается непосредственными значениями. Например, 3-10 = 3*16**1 + 10*16**0 = 48 + 10 = 58 %

Средняя скорость ветра AW м/с (тетрады 10, 11) Naw*0,34

Скорость ветра в порыве GW м/с (тетрады 12, 13) Ngw*0.34

Счетчик осадков RC мм (тетрады 15, 16, 17) Nrc*0.3 (значение только накапливается! Для определения дифференциальной величины, например, за час, необходимо в начале каждого часа находить разницу между предшествующим и текущим значением).

Заряд батареи LB (тетрада 18) (значение не определено).

Направление ветра DW (градусы) (тетрада 19)

Определение направления ветра.

N — noth (север); E — eath (восток); S — south (юг); W — west (запад).

В пакете отсутствует величина давления. Она измеряется в модуле дисплея.

Программа декодирования пакета данных метеостанции Misol

// Программа декодирования пакетов данных метеостанции Misol
volatile byte bitmas[528];
volatile byte tetramas[132];
byte rxb;                    // Значение принятого бита
volatile int nbit = 0;
byte nbyte = 0;
byte rxpin = 2;              // Назначен пин 2 ExtInt0
volatile byte rxpinold, rxpinnew;     // Значения входа

volatile unsigned long timpb, timpe, timp, tpause, timpem ;   //Время начала, конца импульса, паузы
volatile byte imprx;

#define CRC3B   40  // Контрольная сумма первых трех байт. Отличается для каждого устройства! Считается по строке приема.


// ***** Подпрограммы *****

// ***** Прием импульса *****
void int0rxreceive() {
  rxpinold = rxpinnew;      //Сохранение предшествующего состояния
  rxpinnew = digitalRead(rxpin);  //Получение нового значения

  //Анализ состояний
  if (rxpinold == LOW && rxpinnew == HIGH) { // *** Фронт импульса
    //Старт приема импульса
    timpb = micros();
  }

  else if (rxpinold == HIGH && rxpinnew == LOW) // *** Спад импульса
  { //Окончание приема импульса
    timpe = micros();
    timpem = millis();
    timp = timpe - timpb;


    if (timp > 450 && timp < 550) {
      rxb = 1;
    }
    else if ( timp > 1250 && timp < 1550) {
      rxb = 0;
    } //Окончание принятия решения

    else {
      Serial.print("/ \n");
      goto noimp;
    }
    bitmas[nbit] = rxb; //Присвоение значения бита элементу массива
    nbit++;             //Определение позиции следующего бита
    imprx = 1;          // Флаг вывода данных

noimp: {
    }
  }
} // End Прием импульса *****

// Окончание подпрограмм


// ***** Setup *****
void setup() {

  attachInterrupt(0, int0rxreceive, CHANGE);    //Подпрограмма обработки принимаемого импульса на 2 DPIN (Interrupt 0)

  Serial.begin(115200);

  //Разметка для экранного вывода данных
  Serial.println("  Sync__  Serial__ Tempera_  Humid  AvWin GstWn UK RC_____ LB DW  CRC__");
} // End setup *****


// ***** Loop *****
void loop() {

  // *** Count Pause ***
  tpause = millis() - timpem;

  if (imprx == 1 && tpause > 200) {

    //****** Вывод пакета данных по тетрадам
    Serial.print(": ");

    //Сброс массива байтов
    for (int i = 0; i < 132; i++) {
      tetramas[i] = 0;
    }

    // *** Преобразование массива бит в массив тетрад
    byte nbyte = 0;
    byte j = 0;
    byte l = 0;
    byte k = 8;
    for (int i = 0; i < nbit; i++ ) {
      tetramas[l] = tetramas[l] + bitmas[i] * k;
      k = k / 2;
      j++;
      if (j == 4) {
        j = 0;
        l++;
        k = 8;
        Serial.print(tetramas[l - 1]);
        Serial.print(", ");
      }
    }
    Serial.println("***** ");
    //***

    //Контрольная сумма первых трех тетрад
    int sum = 0;
    for (byte i = 0; i < 3; i++) {
      sum = sum + tetramas[i];
    }

    //***** Рабочий цикл
    if ( sum == CRC3B) {
      //Преобразование тетрадных значений в float
      //Температура Temp
      int q = 256;
      byte s = 5;
      float Temp = 0;
      for (byte i = 0; i < 3; i++) {
        Temp = Temp + q * tetramas[i + s];
        q = q / 16;
      }
      Temp = (Temp - 400) / 10;
      Serial.print("Temperature = ");
      Serial.print(Temp);
      Serial.println(" *C");

      //Влажность Hum
      q = 16;
      s = 8;
      float Hum = 0;
      for (byte i = 0; i < 2; i++) {
        Hum = Hum + q * tetramas[i + s];
        q = q / 16;
      }
      Serial.print("Humidity    = ");
      Serial.print(Hum);
      Serial.println(" %");

      //Средняя скорость ветра AW
      q = 16;
      s = 10;
      float AW = 0;
      for (byte i = 0; i < 2; i++) {
        AW = AW + q * tetramas[i + s];
        q = q / 16;
      }
      AW = AW * 0.34;
      Serial.print("AW          = ");
      Serial.print(AW);
      Serial.println(" mps");

      //Скорость ветра в порыве GW
      q = 16;
      s = 12;
      float GW = 0;
      for (byte i = 0; i < 2; i++) {
        GW = GW + q * tetramas[i + s];
        q = q / 16;
      }
      GW = GW * 0.34;
      Serial.print("GW          = ");
      Serial.print(GW);
      Serial.println(" mps");

      //Счетчик осадков Rn
      q = 256;
      s = 15;
      float RnC = 0;
      for (byte i = 0; i < 3; i++) {
        RnC = RnC + q * tetramas[i + s];
        q = q / 16;
      }
      RnC = RnC * 0.3;
      Serial.print("Rain        = ");
      Serial.print(RnC);
      Serial.println(" mm");

      //Направление ветра
      byte DW = tetramas[19];
      Serial.print("DW          = ");
      Serial.print(DW);
      Serial.println("  N");

    } //*** Окончание рабочего цикла

    //Подготовка нового цикла приема пакета данных
    nbit = 0;
    imprx = 0;
  }
} // End loop *****

Полученные из пакета данных значения метеопараметров:

  • Temp — температура (0С, float);
  • Hum — влажность (%, float);
  • AW — средняя скорость ветра (м/с, float);
  • GW — скорость ветра в порыве (м/с, float);
  • RnC — уровень осадков (мм, float);
  • DW — направление ветра (номер направления, byte)

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

Объединение блока наружных датчиков с термо-гидро передатчиком метеостанции Misol с Arduino Mega будет называться базовой станцией.

Направлений развития проекта два: