สถานีตรวจวัดอากาศ ATtiny85 หรือ Weather Station ทำหน้าที่หลัก คือ วัดและตรวจสอบสภาพอากาศ ผ่านเครื่องวัดต่างๆ เช่น เซนเซอร์ DHT22 โมดูลเซนเซอร์วัดความชื้นและอุณหภูมิในตัวเดียว ใช้ไฟเลี้ยง 3 V – 5.5 V แล้วนำมาประมวลผล ด้วย ไมโครคอนโทรลเลอร์ จิ๋ว ATtiny85 แล้วแสดงผลที่จอ OLED 96×64 พร้อมสี 64K โดยการอินเทอร์เฟซแบบ SPI

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22

DHT22 Temperature and Humidity Sensor + PCB



ขั้นตอนการทํางาน

1 : ทำเครื่องโปรแกรม AVR เพื่อใช้อัพโหลดโปรแกรม

ขั้นตอนการใช้บอร์ด Arduino UNO เป็น เครื่องเขียนโปรแกรม ตามลิงค์ด้านล่าง


Arduino UNO เป็น เครื่องเขียนโปรแกรม



2 : ติดตั้งไลบรารี TroykaDHT


ไลบรารี TroykaDHT รองรับเซ็นเซอร์ DHT11, DHT21, DHT22 บน ATtiny85 และโปรเซสเซอร์ ATtiny อื่น ๆ

เปิดโปรแกรม Arduino IDE ไปที่ Sketch -> Include Library -> Manage Library…

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



ที่ช่องค้นหา พิมพ์ Troyka จะพบ TroykaDHT แล้วคลิก Install

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22


INSTALLED แสดงการติดตั้งสำเร็จ แล้วปิดหน้าต่างลงไป

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



ตรวจสอบที่ Sketch -> Include Library  จะพบ ไลบรารี TroykaDHT เพิ่มเข้ามาใน Arduino IDE ของเรา

ตรวจสอบที่ Sketch -> Include Library  จะพบ ไลบรารี TroykaDHT


3 : อัพโหลดโปรแกรมให้กับ ATtiny85

เปิดโปรแกรม Arduino IDE เขียนโปรแกรมและอัพโหลดโค้ดด้านล่างนี้ ไปที่ ATtiny85


// ATtiny85 DHT22
#include <TroykaDHT.h>

DHT dht(2, DHT22); // select DHT22 and comm on PB2

// ATtiny85 OLED
int const data = 1;
int const cs = 3;
int const clk = 4;

// Character set for text - stored in program memory
const uint8_t CharMap[107][6] PROGMEM = {
  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00 },
  { 0x00, 0x07, 0x00, 0x07, 0x00, 0x00 },
  { 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00 },
  { 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00 },
  { 0x23, 0x13, 0x08, 0x64, 0x62, 0x00 },
  { 0x36, 0x49, 0x56, 0x20, 0x50, 0x00 },
  { 0x00, 0x08, 0x07, 0x03, 0x00, 0x00 },
  { 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00 },
  { 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00 },
  { 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00 },
  { 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00 },
  { 0x00, 0x80, 0x70, 0x30, 0x00, 0x00 },
  { 0x08, 0x08, 0x08, 0x08, 0x08, 0x00 },
  { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 },
  { 0x20, 0x10, 0x08, 0x04, 0x02, 0x00 },
  { 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00 },
  { 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00 },
  { 0x72, 0x49, 0x49, 0x49, 0x46, 0x00 },
  { 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00 },
  { 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00 },
  { 0x27, 0x45, 0x45, 0x45, 0x39, 0x00 },
  { 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00 },
  { 0x41, 0x21, 0x11, 0x09, 0x07, 0x00 },
  { 0x36, 0x49, 0x49, 0x49, 0x36, 0x00 },
  { 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00 },
  { 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 },
  { 0x00, 0x40, 0x34, 0x00, 0x00, 0x00 },
  { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 },
  { 0x14, 0x14, 0x14, 0x14, 0x14, 0x00 },
  { 0x00, 0x41, 0x22, 0x14, 0x08, 0x00 },
  { 0x02, 0x01, 0x59, 0x09, 0x06, 0x00 },
  { 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00 },
  { 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00 },
  { 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00 },
  { 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00 },
  { 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00 },
  { 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00 },
  { 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00 },
  { 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00 },
  { 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00 },
  { 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00 },
  { 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00 },
  { 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00 },
  { 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00 },
  { 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00 },
  { 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00 },
  { 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00 },
  { 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00 },
  { 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00 },
  { 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00 },
  { 0x26, 0x49, 0x49, 0x49, 0x32, 0x00 },
  { 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00 },
  { 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00 },
  { 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00 },
  { 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00 },
  { 0x63, 0x14, 0x08, 0x14, 0x63, 0x00 },
  { 0x03, 0x04, 0x78, 0x04, 0x03, 0x00 },
  { 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00 },
  { 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00 },
  { 0x02, 0x04, 0x08, 0x10, 0x20, 0x00 },
  { 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00 },
  { 0x04, 0x02, 0x01, 0x02, 0x04, 0x00 },
  { 0x40, 0x40, 0x40, 0x40, 0x40, 0x00 },
  { 0x00, 0x03, 0x07, 0x08, 0x00, 0x00 },
  { 0x20, 0x54, 0x54, 0x78, 0x40, 0x00 },
  { 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00 },
  { 0x38, 0x44, 0x44, 0x44, 0x28, 0x00 },
  { 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00 },
  { 0x38, 0x54, 0x54, 0x54, 0x18, 0x00 },
  { 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00 },
  { 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00 },
  { 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00 },
  { 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00 },
  { 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00 },
  { 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00 },
  { 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00 },
  { 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00 },
  { 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00 },
  { 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 },
  { 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00 },
  { 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00 },
  { 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00 },
  { 0x48, 0x54, 0x54, 0x54, 0x24, 0x00 },
  { 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00 },
  { 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00 },
  { 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00 },
  { 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00 },
  { 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 },
  { 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00 },
  { 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00 },
  { 0x00, 0x08, 0x36, 0x41, 0x00, 0x00 },
  { 0x00, 0x00, 0x77, 0x00, 0x00, 0x00 },
  { 0x00, 0x41, 0x36, 0x08, 0x00, 0x00 },
  { 0x02, 0x01, 0x02, 0x04, 0x02, 0x00 },
  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 },
  { 0x38, 0x44, 0x44, 0x38, 0x00, 0x00 }, // Tiny digits 4x5
  { 0x00, 0x08, 0x7C, 0x00, 0x00, 0x00 },
  { 0x48, 0x64, 0x54, 0x48, 0x00, 0x00 },
  { 0x28, 0x44, 0x54, 0x28, 0x00, 0x00 },
  { 0x1C, 0x10, 0x78, 0x10, 0x00, 0x00 },
  { 0x5C, 0x54, 0x54, 0x24, 0x00, 0x00 },
  { 0x38, 0x54, 0x54, 0x20, 0x00, 0x00 },
  { 0x44, 0x24, 0x14, 0x0C, 0x00, 0x00 },
  { 0x28, 0x54, 0x54, 0x28, 0x00, 0x00 },
  { 0x08, 0x14, 0x14, 0x78, 0x00, 0x00 },
  { 0x00, 0x06, 0x09, 0x06, 0x00, 0x00 } // degree symbol
};

const int Degree = 138;
const int Tiny = 128;

// OLED 96x64 colour display **********************************************

// Initialisation sequence for OLED module
int const InitLen = 6;
const unsigned char Init[InitLen] PROGMEM = {
  0xA0,       // Driver remap and colour depth
  0x22,       // COM split, flip horizontal
  0xA8, 0x3F, // Multiplex
  0xAD, 0x8E, // External supply
};

// Send a byte to the display
void Send (uint8_t d) {
  for (uint8_t bit = 0x80; bit; bit >>= 1) {
    PINB = 1 << clk;                      // clk low
    if (d & bit) PORTB = PORTB | (1 << data); else PORTB = PORTB & ~(1 << data);
    PINB = 1 << clk;                      // clk high
  }
}

void InitDisplay () {
  PINB = 1 << cs;                         // cs low
  for (uint8_t c = 0; c < InitLen; c++) Send(pgm_read_byte(&Init[c]));
  PINB = 1 << cs;                         // cs high
}

// Display off = 0, on = 1
void DisplayOn (uint8_t on) {
  PINB = 1 << cs;                         // cs low
  Send(0xAE + on);
  PINB = 1 << cs;                         // cs high
}

// Graphics **********************************************

// Global plot parameters
uint8_t x0 = 0;
uint8_t y0 = 0;
uint8_t ForeR = 0x3F, ForeG = 0x3F, ForeB = 0x3F;
uint8_t BackR = 0x00, BackG = 0x00, BackB = 0x00;
uint8_t Scale = 1; // Text size - 2 for big characters

// Clear display
void ClearDisplay () {
  PINB = 1 << cs;                         // cs low
  Send(0x25);                             // Clear Window
  Send(0); Send(0); Send(95); Send(63);
  PINB = 1 << cs;                         // cs high
  delay(1);
}

void MoveTo (uint8_t x, uint8_t y) {
  x0 = x; y0 = y;
}

// Draw line to (x,y) in foreground colour
void DrawTo (uint8_t x, uint8_t y) {
  PINB = 1 << cs;                         // cs low
  Send(0x21);                             // Draw Line
  Send(x0); Send(y0); Send(x); Send(y);
  Send(ForeR); Send(ForeG); Send(ForeB);
  PINB = 1 << cs;                         // cs high
  x0 = x; y0 = x;
}

// Plot a point at (x,y)
void PlotPoint (uint8_t x, uint8_t y) {
  MoveTo(x, y);
  DrawTo(x, y);
}

// Draw a rectangle in foreground colour optionally filled with background colour
void DrawRect (boolean filled, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
  PINB = 1 << cs;                         // cs low
  Send(0x26); Send(filled);               // Enable fill
  Send(0x22);                             // Draw rectangle
  Send(x1); Send(y1); Send(x2); Send(y2);
  Send(ForeR); Send(ForeG); Send(ForeB);
  Send(BackR); Send(BackG); Send(BackB);
  PINB = 1 << cs;                         // cs high
  delay(1);
}

// Plot character in foreground colour
void PlotChar (uint8_t ch) {
  PINB = 1 << cs;                         // cs low
  for (uint8_t c = 0 ; c < 6; c++) {      // Column range
    uint8_t bits = pgm_read_byte(&CharMap[ch - 32][c]);
    uint8_t r = 0;
    while (bits) {
      while ((bits & 1) == 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t on = (7 - r) * Scale;
      while ((bits & 1) != 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t off = (7 - r) * Scale + 1;
      for (int i = 0; i < Scale; i++) {
        uint8_t h = x0 + c * Scale + i;
        Send(0x21);                         // Draw line
        Send(h); Send(y0 + on); Send(h); Send(y0 + off);
        Send(ForeR); Send(ForeG); Send(ForeB);
      }
    }
  }
  PINB = 1 << cs;                           // cs high
  x0 = x0 + 6 * Scale;
}

// Plot text from program memory
void PlotText(PGM_P p) {
  while (1) {
    char c = pgm_read_byte(p++);
    if (c == 0) return;
    PlotChar(c);
  }
}

// Widgets **********************************************

int Widget;
enum TextColour { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, OLIVE, ORANGE };

void SetFore (int colour) {
  ForeR = 0x3F * (colour & 1); ForeG = 0x3F * (colour >> 1 & 1) + 0x1F * (colour >> 3 & 1); ForeB = 0x3F * (colour >> 2 & 1);
}

// Draws a titled frame and sets x and y to frame origin
void Frame (PGM_P title, uint8_t *x, uint8_t *y) {
  *x = (Widget % 2) * 48;
  *y = 32 - (Widget / 2) * 32;
  uint8_t shade;
  if (((Widget + 1) / 2) % 2) shade = 0x04; else shade = 0x0C;
  BackR = BackG = BackB = ForeR = ForeG = ForeB = shade;
  DrawRect(1, *x, *y, (*x) + 47, (*y) + 31);
  ForeR = ForeG = ForeB = 0x3F;
  Scale = 1; MoveTo((*x) + 1, (*y) + 23); PlotText(title);
  Widget = (Widget + 1) & 3;
}

// Plot integer with suffix
void PlotInteger (int number, PGM_P suffix) {
  uint8_t len = strnlen_P(suffix, 4);
  boolean dig = false;
  unsigned int j = 1000;
  for (int i = 0; i < len; i++) j = j / 10;
  if (number < 0) {
    PlotChar('-');
    j = j / 10;
  }
  do {
    char c = (abs(number) / j) % 10 + '0';
    if (c == '0' && !dig && j != 1) c = ' '; else dig = true;
    PlotChar(c); j = j / 10;
  } while (j);
  PlotText(suffix);
}

// Displays an integer with an optional suffix
void IntegerWidget (PGM_P title, int value, PGM_P suffix, int colour) {
  uint8_t x0, y0;
  Frame(title, &x0, &y0);
  Scale = 2;
  SetFore(colour);
  MoveTo(x0 + 1, y0 + 1);
  PlotInteger(value, suffix);
}

// Displays a number with one decimal place. Range is 0 to 999 displayed "0.0" to "99.9"
void NumberWidget (PGM_P title, int value, int colour) {
  uint8_t x0, y0;
  Frame(title, &x0, &y0);
  Scale = 2;
  SetFore(colour);
  uint8_t tens = value / 100;
  MoveTo(x0 + 1, y0 + 1);
  if (tens == 0) PlotChar(' '); else PlotChar(tens + '0');
  PlotChar(value / 10 % 10 + '0');
  PlotChar('.'); PlotChar(value % 10 + '0');
}

// Displays bar; range is 0 to 100 displayed "0" to "5"
void BarWidget (PGM_P title, int value) {
  uint8_t x0, y0;
  Frame(title, &x0, &y0);
  // Bar background
  BackG = ForeG = 0x3F; BackR = BackB = ForeR = ForeB = 0x08;
  uint8_t bar = value * 4 / 10;
  DrawRect(1, x0 + bar + 4, y0 + 4, x0 + 44, y0 + 10);
  // Bar value
  BackR = ForeR = 0x3F; BackG = BackB = ForeG = ForeB = 0x08;
  DrawRect(1, x0 + 4, y0 + 4, x0 + bar + 4, y0 + 10);
  // Numbers
  ForeR = ForeG = 0x3F; ForeB = 0x00;
  for (int i = 0; i <= 5; i++) {
    MoveTo(x0 + i * 8 + 2, 12 + y0); PlotChar('\200' + i);
  }
}

// Displays dial; range is 0 to 100 displayed "0" to "5"
void AnalogueWidget (PGM_P title, int value) {
  uint8_t x0, y0;
  Frame(title, &x0, &y0);
  ForeR = 0x3F; ForeG = 0x3F; ForeB = 0x00; // Yellow
  const int Delta = 16;
  int x = -(15 << 9), y = 0;
  for (int i = 0; i <= 100; i++) {
    MoveTo((x >> 9) + 24 + x0, (y >> 9) + 1 + y0);
    x = x + ((y >> 9) * Delta);
    y = y - ((x >> 9) * Delta);
    DrawTo((x >> 9) + 24 + x0, (y >> 9) + 1 + y0);
    if (i == value) {
      MoveTo((x >> 9) + 24 + x0, (y >> 9) + 1 + y0);
      DrawTo(24 + x0, 1 + y0);
    }
    if (i % 20 == 0) {
      MoveTo((x >> 9) + 24 + x0, (y >> 9) + 1 + y0);
      DrawTo((x >> 9) - (x >> 11) + 24 + x0, (y >> 9) - (y >> 11) + 1 + y0);
    }
  }
  // Plot tiny digits 0 to 5
  MoveTo(3 + x0, 0 + y0); PlotChar(Tiny + 0);
  MoveTo(5 + x0, 9 + y0); PlotChar(Tiny + 1);
  MoveTo(14 + x0, 16 + y0); PlotChar(Tiny + 2);
  MoveTo(30 + x0, 16 + y0); PlotChar(Tiny + 3);
  MoveTo(39 + x0, 9 + y0); PlotChar(Tiny + 4);
  MoveTo(42 + x0, 0 + y0); PlotChar(Tiny + 5);
}

// Setup **********************************************

void setup() {
  //  TinyWireM.begin();
  // Define pins
  DDRB = 1 << clk | 1 << cs | 1 << data; // All outputs
  PORTB = 1 << clk | 1 << cs;       // clk and cs high
  InitDisplay();
  ClearDisplay();
  DisplayOn(1);

  dht.begin();
}

void loop()
{

  dht.read();

  switch (dht.getState()) {

    case DHT_OK:


      NumberWidget(PSTR("Temp.\212C"), dht.getTemperatureC() * 10 , ORANGE);
      NumberWidget(PSTR("Temp.\212F"), dht.getTemperatureF() * 10 , MAGENTA);
      NumberWidget(PSTR("Humidity"), dht.getHumidity() * 10 , CYAN);
      BarWidget(PSTR("Current"), 50);
      break;

    case DHT_ERROR_CHECKSUM:
      //PlotText(PSTR("Checksum error"));
      break;

    case DHT_ERROR_TIMEOUT:
      ClearDisplay();
      PlotText(PSTR("Time out error"));
      break;

    case DHT_ERROR_NO_REPLY:
      ClearDisplay();
      PlotText(PSTR("Sensor not connected"));
      break;
  }

  delay(1000);

}



สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



4 : เชื่อมต่ออุปกรณ์เข้ากับ ATtiny85 ตามวงจรด้านล่าง


สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



5 : ผลลัพธ์การทำงาน

ผลลัพธ์การทำงาน จะแสดงอุณหภูมิ องศาเซลเซียส °C , แสดงอุณหภูมิ องศาฟาเรนไฮต์ °F , ค่าชื้นในอากาศ Humidity แสดงว่าประสบความสำเร็จแล้ว

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22

สถานีตรวจวัดอากาศ ATtiny85 ด้วย DHT22



6 : อุปกรณ์ที่ใช้ในโปรเจค



credit : http://www.technoblogy.com/show?2EDJ