Метеостанция с часами

ustas
Offline
Зарегистрирован: 12.03.2012

"я его слепила из того что было"

Ардуино 328, модули Grove (температура-влажность, давление, дисплейчик 16х2, RF-модуль на 443МГЦ, светодиод, кнопка, RTC) и два внешних датчика Oregon (THGN132N - температура и относительная влажность).

Собствено реализованные функции:

  • часы с календарем (RTC на базе DS1307)
  • отображения атмосферного давления (датчик BMP085)
  • отображение температуры и влажности внутри помещения (датчик DHT11)
  • отображение температуры и влажности вне помещения (датчики Орегон THGN132N)

из "фишек":

  • чтение и декодирование радиосигналов, отпраляемых покупными датчиками Oregon
  • вычисление и отображение трендов ("растет", "стабильно", "падает") для всех погодных показателей, настраиваемых индивидуально (параметры для расчета трендов для температуры, влажности и давления - индивидуальные)

Подробнее по ссылке, там же есть файл для скачивания с полным скетчем :)

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

ustas
Offline
Зарегистрирован: 12.03.2012

Первый прототип метеостанции и один из датчиков Oregon THGN132N

ustas
Offline
Зарегистрирован: 12.03.2012

Тема с погодной станцией получила развитие. 

Получилось вот что: http://weather.opushkino.com/ (домен прописан вчера, возможна временная недоступность у тех, кто ходит в Интернет через мобильных операторов).

Как это работает:

1. К исходной версии погодной станции

2. Подключен LAN-модуль Wiznet W5100

3. На сетевом хранилище Synology поднят веб-сервер (php и mySQL)

4. На веб-сервере крутятся две странички. Одна принимает данные от ардуинки (используется метод POST, дополнительно параметром передается "секретный ключ", для того, чтобы форма отрабатывалась только для "своих" девайсов). Эта страница принимает данные и укладывает их в БД. Вторая страница - достает эти данные и отображает с помощью сервиса Google Chart в виде графиков.

Если есть вопросы - готов ответить.

ustas
Offline
Зарегистрирован: 12.03.2012

Тема потихоньку получает новое развитие.

Сегодня с утра на хабре появился вот такой пост: http://habrahabr.ru/post/174793/

Собственно, за 5 минут был написан data-provider и вуаля:

Zapek@n
Offline
Зарегистрирован: 16.02.2012

Вау! Классное развитие. Жарко у Вас дома.

paf
Offline
Зарегистрирован: 25.01.2013

DHT11 ... это...  сравнению не поддается. :)  Похоже, что конструктор уборщиком в НАСА долгое время служил! :)))

ustas
Offline
Зарегистрирован: 12.03.2012

да.. DHT11 - этот только для "оценочного" значения влажности используется

в остальных местах или DHT22, или DS18B20, а барометр BMP085 - он тоже температуру хорошо измеряет..

plb
Offline
Зарегистрирован: 11.05.2013

Привет!

ustas, а к Aduino Uno радио датчик THGN132N подключить можно? Вчера полдня провозился - по нулям - не видит датчик. Видимо нужно код править. Подскажите в каком месте копать?

ustas
Offline
Зарегистрирован: 12.03.2012

Под уно код править не надо - все и так работает. Просто правильно подключайте (так, как выше описано) и все будет работать.

plb
Offline
Зарегистрирован: 11.05.2013

Прошу прощения, неверно задал вопрос. Давайте еще раз :)

Меня интересует только работа с радиодатчиком (без подключения экрана, кнопок, датчиков давления и т.п.)

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

Относительно правильности подключения - думаю все включил правильно - между двумя arduino радиоканал  работает (данные передавал с датчика dallas 18b20 через радиоканал, используя библиотеку virtualware), Ваше решение гораздо интереснее, как раз за счет использования готового радиодатчика.

ustas
Offline
Зарегистрирован: 12.03.2012

plb пишет:

Меня интересует только работа с радиодатчиком (без подключения экрана, кнопок, датчиков давления и т.п.)

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

Относительно правильности подключения - думаю все включил правильно - между двумя arduino радиоканал  работает (данные передавал с датчика dallas 18b20 через радиоканал, используя библиотеку virtualware), Ваше решение гораздо интереснее, как раз за счет использования готового радиодатчика.

1. библиотека virtualware и орегоновский датчик - взаимоисключающие вещи (что-то одно надо оставить)

2. Оставить нужно ту часть кода, которая отвечает за Орегоны :) (убрать всякие SerialLCD, Wire и т.п.)

а вообще все есть по ссылкам выше (и ссылка на первоначальную статью на хабре - там вообще все азы есть).

plb
Offline
Зарегистрирован: 11.05.2013

ustas пишет:

1. библиотека virtualware и орегоновский датчик - взаимоисключающие вещи (что-то одно надо оставить)

это я понимаю, использовал библиотеку для передачи сигнала между двумя ардуинами (проверить хотел исправность приемника)

Цитата:

2. Оставить нужно ту часть кода, которая отвечает за Орегоны :) (убрать всякие SerialLCD, Wire и т.п.)

а вообще все есть по ссылкам выше (и ссылка на первоначальную статью на хабре - там вообще все азы есть).

ok. так и сделал. Подправил скетч под UNO. В результате сигнал с датчика расшифровывается в основном почему-то декодерами HEZ и CRES, а соответственно декодор Oregon не видит датчик. Ниже - то что расшифровал конроллер. Там есть одна строчка декодера OSV2 (9я строчка) , все остальное Home Easy и Cresta

Не сталкивались? У меня ощущения, что сигнал с датчика слабоват. Сейчас попробую сколхозить антенну на приемник. 

Вот, что расшифровывает контроллер:

[ookDecoder]
CRES FAFBFAEAAAAAEBFFFFAFBABEAEEFBE
HEZ 77F7DDDDF77D03
CRES BEBAFABFFEBEBEFAAEEAFAFFFFABEEAEEBAE2B
CRES BEBAFABFFEBEBEFAAEEAFAFFFFABEEAEEBAE2B
HEZ DFDDDDF7D703
HEZ DFDDDDF7D703
HEZ 5D7377D7DD03
OSV2 1A2D10EE912500844ACA
CRES BEBAFABFFEBEFEBEEEEAFAFFFFEBAFAAEBAA3B
CRES EEFAFFFFEBAFFAABBA2F
HEZ DF757FD7F57D03
HEZ 757FD7F57D03
HEZ 757FD7F57D03
CRES EEFAFFFFEBAFFAABBA2F
HEZ 7F7F7FD7D77703
CRES BEFEBEFEEEFAFFFFEBEFFBABEB2E
HEZ DFDD7DD7FD5D03
HEZ DFDD7DD7FD5D03

 

ustas
Offline
Зарегистрирован: 12.03.2012

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

 

plb
Offline
Зарегистрирован: 11.05.2013

ok. спасибо обнадежили, пойду доставать с антресолей осциллограф :).

ustas
Offline
Зарегистрирован: 12.03.2012

подстройка идет в очень небольшом промежутке (буквально +-5 градусов). не сверните настройку совсем - будет еще хуже и настроиться будет крайне проблематично.

freem
Offline
Зарегистрирован: 21.03.2014

Столкнулся с аналогичной проблемой. На выходе получаю картину
 

CRES BAFEFFBBFFBBEABEBA0B
HEZ 9F93E47C927302
CRES BEBAFABFFEABEFBFFEEBFAFFEFFEAFABFBFF2A
CRES BEBAFABFFEABEFBFFEEBFAFFEFFEAFABFBFF2A
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFFFFFEBFAFFEFFEEFBBFBAE2B
CRES BEBAFABFFEABEFBFEEFBFAFFEFFEEFEBBBAF2F
CRES BEBAFABFFEABEFFFEFFBFAFFEFFEAFEABBFE2E
CRES BEBAFABFFEABEFFFEFFBFAFFEFFEAFEABBFE2E
CRES BEBAFABFFEABEFBFAFFBFAFFEFFEEFEEBBBB2B
CRES BEBAFABFFEABEFBFAFFBFAFFEFFEEFEEBBBB2B
CRES BEBAFABFFEABEFFFAEFBFAFFEFFEAFEFBBEA2A
CRES BEBAFABFFEABEFFFAEFBFAFFEFFEAFEFBBEA2A
OSV2 1A2D1074402380813689
CRES BEBAFABFFEABEFFFABFBFAFFEFFEAFAEBBAE2F
CRES BEBAFABFFEABEFFFABFBFAFFEFFEAFAEBBAE2F
CRES BEBAFABFFEABEFBFBBFBFAFFEFFEEFAABBAB3E
CRES BEBAFABFFEABEFBFBBFBFAFFEFFEEFAABBAB3E
CRES BEBAFABFFEABEFFFBAFBFAFFEFFEAFABBBFA3F
CRES BEBAFABFFEABEFFFBAFBFAFFEFFEAFABBBFA3F

припаивал антену к приемнику, пробовал крутить дроссель на приемнике, эффекту ноль. Даже не знаю в какую сторону дальше копать, кто виноват - приемник или датчик ((

ustas
Offline
Зарегистрирован: 12.03.2012

обычно виноват приемник. 

Но у вас, вижу, OSV2 приходят... что за датчик используется?

freem
Offline
Зарегистрирован: 21.03.2014

Виноват оказался декодер. Все заработало, как только поменял алгоритм согласно http://connectingstuff.net/blog/decodage-protocole-oregon-arduino-1/

mkvmaks
Offline
Зарегистрирован: 07.07.2013

ustas, Как с вами можно связаться??? 

mkvmaks
Offline
Зарегистрирован: 07.07.2013

Исправил код "под себя" так сказать. Почему не выводится значение температуры??? Все по 0, т.е. на дисплее так:  0.0С    0%. Если оставить отдельно вывод температур , то все ок. 

#include <Bounce.h>
#include <SoftwareSerial.h>

// библиотеки для дисплея
#include <LiquidCrystalRus.h>


#define DISABLE_DEBUG // если нужен вывод в Serial - закомментируйте эту строчку

#define LED 13 // LED на D13
#define BUTTON 3  //кнопка

#define STATUS_TEMP 0
#define STATUS_TIME 1
#define MAX_STATUS 1
static byte status=STATUS_TEMP;


Bounce bouncer = Bounce( BUTTON,5 ); 


unsigned long pressed_moment; // момент нажатия кнопки
int current_mode = 0; // текущий режим
#define pressed_long 2000 // долговременное нажатие = 2 секунды



// дисплей
LiquidCrystalRus lcd(4, 5, 10, 11, 12, 13);

// переменные для хранения значений
// 0 - THGN132N на "1 канале"
// 1 - THGN132N на "2 канале"

// температура 
float t[2];  

// влажность
byte h[2];

// батарейка
byte b[2];

// счетчик (используем для переключения "экранов")
byte cDisp = 0;

unsigned long dispNow = 0;
unsigned long ledNow = 0;

// Oregon V2 decoder added - Dominique Pierre
// New code to decode OOK signals from weather sensors, etc.
// 2010-04-11 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
// $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $

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

  virtual char decode (word width) =0;

public:

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

  DecodeOOK () { 
    resetDecoder(); 
  }

  bool nextPulse (word width) {
    if (state != DONE)

      switch (decode(width)) {
      case -1: 
        resetDecoder(); 
        break;
      case 1:  
        done(); 
        break;
      }
    return isDone();
  }

  bool isDone () const { 
    return state == DONE; 
  }

  const byte* getData (byte& count) const {
    count = pos;
    return data; 
  }

  void resetDecoder () {
    total_bits = bits = pos = flip = 0;
    state = UNKNOWN;
  }

  // add one bit to the packet data buffer

  virtual void gotBit (char value) {
    total_bits++;
    byte *ptr = data + pos;
    *ptr = (*ptr >> 1) | (value << 7);

    if (++bits >= 8) {
      bits = 0;
      if (++pos >= sizeof data) {
        resetDecoder();
        return;
      }
    }
    state = OK;
  }

  // store a bit using Manchester encoding
  void manchester (char value) {
    flip ^= value; // manchester code, long pulse flips the bit
    gotBit(flip);
  }

  // move bits to the front so that all the bits are aligned to the end
  void alignTail (byte max =0) {
    // align bits
    if (bits != 0) {
      data[pos] >>= 8 - bits;
      for (byte i = 0; i < pos; ++i)
        data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
      bits = 0;
    }
    // optionally shift bytes down if there are too many of 'em
    if (max > 0 && pos > max) {
      byte n = pos - max;
      pos = max;
      for (byte i = 0; i < pos; ++i)
        data[i] = data[i+n];
    }
  }

  void reverseBits () {
    for (byte i = 0; i < pos; ++i) {
      byte b = data[i];
      for (byte j = 0; j < 8; ++j) {
        data[i] = (data[i] << 1) | (b & 1);
        b >>= 1;
      }
    }
  }

  void reverseNibbles () {
    for (byte i = 0; i < pos; ++i)
      data[i] = (data[i] << 4) | (data[i] >> 4);
  }

  void done () {
    while (bits)
      gotBit(0); // padding
    state = DONE;
  }
};

// 433 MHz decoders


class OregonDecoderV2 : public DecodeOOK {
  public:   
 
    OregonDecoderV2() {}
 
    // add one bit to the packet data buffer
    virtual void gotBit (char value) {
        if(!(total_bits & 0x01))
        {
            data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
        }
        total_bits++;
        pos = total_bits >> 4;
        if (pos >= sizeof data) {
            Serial.println("sizeof data");
            resetDecoder();
            return;
        }
        state = OK;
    }
 
    virtual char decode (word width) {
       if (200 <= width && width < 1200) {
            //Serial.println(width);
            byte w = width >= 700;
 
            switch (state) {
                case UNKNOWN:
                    if (w != 0) {
                        // Long pulse
                        ++flip;
                    } else if (w == 0 && 24 <= flip) {
                        // Short pulse, start bit
                        flip = 0;
                        state = T0;
                    } else {
                        // Reset decoder
                        return -1;
                    }
                    break;
                case OK:
                    if (w == 0) {
                        // Short pulse
                        state = T0;
                    } else {
                        // Long pulse
                        manchester(1);
                    }
                    break;
                case T0:
                    if (w == 0) {
                      // Second short pulse
                        manchester(0);
                    } else {
                        // Reset decoder
                        return -1;
                    }
                    break;
              }
        } else if (width >= 2500  && pos >= 8) {
            return 1;
        } else {
            return -1;
        }
        return 0;
    }
};

OregonDecoderV2 orscV2;

volatile word pulse;

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

void reportSerial (const char* s, class DecodeOOK& decoder) {
  byte pos;
  const byte* data = decoder.getData(pos);
#ifndef DISABLE_DEBUG
  Serial.print(s);
  Serial.print(' ');
  for (byte i = 0; i < pos; ++i) {
    Serial.print(data[i] >> 4, HEX);
    Serial.print(data[i] & 0x0F, HEX);
  }
  Serial.println();
#endif
  // Outside/Water Temp : THGN132N,...
  if (data[0] == 0xEA && data[1] == 0x4C)
    {
      #ifndef DISABLE_DEBUG
      
       Serial.print("[THN132N,...] Id:");
       Serial.print(data[3], HEX);
       Serial.print(" ,Channel:");
       Serial.print(channel(data));
       Serial.print(" ,temp:");
       Serial.print(temperature(data));
       Serial.print(" ,bat:");
       Serial.print(battery(data)); 
       Serial.println();
     #endif

    // используем только 2 датчика THGN132N на 1 и 2 канале
    if (channel(data) > 0 && channel(data) < 4){
      t[channel(data)-1]=temperature(data);
      h[channel(data)-1]=humidity(data);
      b[channel(data)-1]=battery(data);
    }
  }
  decoder.resetDecoder();
}


void setup () {
#ifndef DISABLE_DEBUG
  Serial.begin(115200);
  Serial.println("\n[WeatherStation]");
#endif

  // включим дисплей
  lcd.begin(16, 2);  

  pinMode(2, INPUT);  // D2 - RF-модуль
  digitalWrite(2, 1); // включим подтягивающий резистор 
  
  pinMode(LED, OUTPUT);  // LED

  attachInterrupt(0, ext_int_1, CHANGE);
}

void loop () {
  noInterrupts();
  word p = pulse;

  pulse = 0;
  interrupts();

  if (p != 0) {
    if (orscV2.nextPulse(p)) {
      reportSerial("OSV2", orscV2);  
      digitalWrite(LED, HIGH); 
      ledNow = millis()+200;
    }
  }



           // Update the debouncer
 bouncer.update ( );
          // Get the update value
//  int value = bouncer.read();
  
  if (bouncer.read()==0)
  { //если кнопка нажата
                   //      pressed_moment = millis(); // запоминаем время нажатия
      status++; 
      if (status>MAX_STATUS) status=0;
  }
  switch(status)
  {
  case 1: printValues2(); break;
  default: printValues1(); break;
  }
  
  
 
// if ((millis()-dispNow>=20000)|| ( value == HIGH)) {
 //   printValues();
//    dispNow=millis();
 // }
  
  if (millis() >= ledNow) {
    digitalWrite(LED, LOW);
  }
}

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

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

// Ne retourne qu'un apercu de l'etat de la baterie : 10 = faible
byte battery(const byte* data)
{
  return (data[4] & 0x4) ? 10 : 90;
}

byte channel(const byte* data)
{
  byte channel;
  switch (data[2])
  {
  case 0x10:
    channel = 1;
    break;
  case 0x20:
    channel = 2;
    break;
  case 0x40:
    channel = 3;
    break;
  }
  return channel;
}

void printValues1(){
  char buf[16];
  char tbuf[6];
  char templ[]={' ','%','s',char(223),'C',' ','%','3','d','%','%',' ',' ',' ','\0'};
  
  
  
  for (int i=0; i<1; i++) {
    if (b[i] > 50){
      templ[13] = char(219);
    }
    else {
      templ[13] = char(161);
    }
    dtostrf(t[i],5,1,tbuf);
    sprintf(buf, templ, tbuf, h[i]);
    lcd.setCursor(0, i);
    lcd.print(buf);
  }
}
  
void printValues2(){
  lcd.setCursor(0, 0);
  lcd.print(" Привет         ");
}