26 июля 2015

Arduino метеостанция


arduino oregon

DYI инструкция по сбору домашней метеостанции.

Нам понадобиться:

  • Arduino — 1 шт. 
  • OLDE экран - 1шт.
  • Датчик давления BMP085 - 1 шт. 
  • Датчик температуры и влажности DHT22 - 1 шт. 
  • Беспроводной датчик температуры и влажности Oregon THGN132N - 1 шт. (для измерения показаний на улице) 
  • Приемник на 433МГц — 1 шт.
  • Свободное время - от 60 минут. 
  • Не очень кривые руки - 2 шт.






Схема подключения элементов:



И собранный прототип:

 Scetch
/*
MODULE..........ARDUINO
GND.............GND
VCC.............3.3V
D0 (CLK)........D13
D1 (DATA).......D11
RST (RESET).....D10
DC..............D9
CS (SS).........D8
*/

#include 
#include "HCuOLED.h"
#include "SPI.h"
#include "DHT.h"
#include 
#define DHTPIN 3
#define DHTTYPE DHT22
#define CS_DI 8
#define DC_DI 9
#define RST_DI 10
//#define DISABLE_DEBUG // если нужен вывод в Serial - закомментируйте эту строчку

HCuOLED HCuOLED(SH1106, CS_DI, DC_DI, RST_DI);

BMP085 dps = BMP085();
DHT dht(DHTPIN, DHTTYPE);

long temp3 = 0, Pressure = 0, Altitude = 0;

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

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

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

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

unsigned long ledNow = 0;
long previousMillis = 0;

// Oregon V2 decoder added - Dominique Pierre
// New code to decode OOK signals from weather sensors, etc.
// 2010-04-11  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) {
      resetDecoder();
      return;
    }
    state = OK;
  }

  virtual char decode (word width) {
    if (200 <= width && width < 1200) {
      byte w = width >= 700;
      switch (state) {
      case UNKNOWN:
        if (w != 0) {
          // Long pulse
          ++flip;
        } 
        else if (32 <= 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 {
      return -1;
    }
    return total_bits == 160 ? 1: 0;
  }
};

OregonDecoderV2 orscV2;

volatile word pulse;

void ext_int_1(void) {
  static word last;
  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

  dps.getPressure(&Pressure);
  dps.getAltitude(&Altitude);
  //dps.getTemperature(&temp3);

  float hi = dht.readHumidity();
  float ti = dht.readTemperature();
  
  if(data[0] == 0x1A && data[1] == 0x2D)
  {
    HCuOLED.Erase(0,0,128,64);
    
    HCuOLED.SetFont(LCDLarge_24pt );
    HCuOLED.Cursor(0,0);
    HCuOLED.Print(temperatureO(data), 3, 0);
    
    HCuOLED.Cursor(60,0);
    HCuOLED.Print(humidityO(data), 3, 0);
    
    HCuOLED.SetFont(Terminal_8pt);
    HCuOLED.Cursor(44,0);
    HCuOLED.Print("o");
    
    HCuOLED.Cursor(20,44);
    HCuOLED.Print("o");
    HCuOLED.Cursor(62,49);
    HCuOLED.Print("%");
    HCuOLED.Cursor(110,49);
    HCuOLED.Print("mm");
    
    if (battery(data) <= 15) {
    HCuOLED.Cursor(120,0);
    HCuOLED.Print("b");
    }
    
    HCuOLED.SetFont(MedProp_11pt);
    HCuOLED.Cursor(-20,45);
    HCuOLED.Print(ti, 4, 0);
    
    HCuOLED.Cursor(98,12);
    HCuOLED.Print("%");
  
    HCuOLED.Cursor(23,45);
    HCuOLED.Print(hi, 4, 0);
  
    HCuOLED.Cursor(82,45);
    HCuOLED.Print(Pressure/133.3, 3, 0);
    
    HCuOLED.Refresh();

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

void setup() 
{
  #ifndef DISABLE_DEBUG
  Serial.begin(115200);
  #endif
  
  HCuOLED.Reset();
  analogReference(INTERNAL);
  
  Wire.begin();
  dht.begin();
  delay(2000);

  dps.init(MODE_ULTRA_HIGHRES, 25000, true); // 250 метрov над уровнем моря

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

  attachInterrupt(0, ext_int_1, CHANGE);
}

void loop() {
  
  unsigned long currentMillis = millis();
  
  noInterrupts();
  word p = pulse;

  pulse = 0;
  interrupts();
  
  if (p != 0) {
    if (orscV2.nextPulse(p)) {
      reportSerial("OSV2", orscV2);
    }
  }
  
  if(currentMillis - previousMillis > 180000) { 
    previousMillis = currentMillis;   
    
    HCuOLED.Erase(0,0,128,34);
    HCuOLED.SetFont(MedProp_11pt);
    
    HCuOLED.Cursor(30,12);
    HCuOLED.Print("no signal");
    
    HCuOLED.Refresh();
  }
}

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

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

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;
}