아두이노 코딩-38: 스마트 온습도측정 및 시계-0.96인치 OLED 그래픽 디스플레이(동영상포함)

in #kr6 years ago (edited)

noname01.png

스마트 워치와 같은 디바이스에는 여러 기능이 함께 들어가는 경우가 흔하다, 아두이노 우노와 0.96인치 OLED 디스플레이를 결합하여 온습도 측정기와 시계를 결합하여 스마트 디바이스를 코딩해 보도록 하자.

작은 메모리 공간에 이 두가지 알고리듬을 코딩함에 있어서 여러 가지 주의해야 할 사항이 많다.

헤더 영역에서 온습도 측정센서를 위한 DHT11.h 라이브러리가 필요하다.
위 사진에의 배선에서 보듯이 0.96인치 OLED 디스플레이는 I2C인터페이스를 지원하는 Wire.h가 당연히 도입되어야 한다. 한편 텍스트 출력을 위해서는 Adafruit사의 폰트 지원 라이브러리 Adafruit_GFX.h 가 반드시 필요하며, 텍스트 및 그래픽 출력을 지원하는 Adafruit_SH1106.h 라이브러리가 뒤따라야 한다. 특히 SH1106 라이브러리는 클라스 명령 display를 선언하여 작은 화면을 화려하게 장식할 수 있도록 많은 명령어 세트를 제공하게 된다.
Adafruit 라이브러리 설치에 관해서는 이미 스팀잇에 작성되어 있는 아래의 블로그를 참조하도록 하자.

아두이노 코딩-36: 0.96인치 OLED “Steemit” Smart Watch
https://steemit.com/kr/@codingart/36-0-96-oled-steemit-smart-watch

우선 이미 블로그에서 선을 보였지만 스팀잇 로고라든지 또는 기상 데이타인 온습도를 표현하기 위해서는 별도로 구름 모양의 로고를 비트맵 방식으로 그렸다. 각 데이터별로 맞는 로고를 그래픽으로 보여 주도록 하자.

로고를 그래픽으로 보여주기 위해서는 비트맵 데이터 형식으로 32x32 배열(어레이) 데이타를 준비하도록 한다.

noname02.png

헤더 부분의 비트맵 데이터는 loop()에서 다음과 같은 명령을 사용하여 출력할 수 있다.

display.drawBitmap(0, 0,  W_bmp, W_WIDTH, W_HEIGHT, 1);

즉 0,0은 128x64 좌표계에서 비트맵 그림의 좌표 원점이다. 디스플레이 왼쪽 상단 코너 지점이다. W_bmp는 정의 된 배열 즉 어레이 명칭을 뜻한다. W_WIDTH, W_HEIGHTsms 배열의 크기를 지정해 주는 파라메터로서 32x32 가 사용되었다. 마지막 숫자 1 은 애니메이션과 관련된 파라메타인데 현재는 단순한 로고 출력이므로 그대로 1 로 두자.

이어서 현재 배선된 디스플레이 규격이 과연 128x64 인지 확인하는 것이 좋다. 비슷하게 생겼으나 해상도가 다를 수 있기 때문이다. 예를 들면 124x32 가 있을 수 있기 때문이다. 아울러 온습도 센서를 설치해야 하므로 5번 핀을 할당한다.

noname03.png

setup(0에서 통신 속도는 9600으로 설정하자. 아울러 display.begin(0 명령에서 OLED 디스플레이의 I2C 인터페이스 0x3C를 정의하도록 하자, 이 I2C 주소는 1602 lcd 에 있어서 0x3F 였음을 기억할 것이다. 즉 배선만 가능하다면 아두이노가 디바이스의 고유주소를 알기 때문에 필요한 디바이스를 호출하여 사용할 수 있게 된다.

아두이노에서 대단히 중요한 millis() 명령에 관해서 알아보자. millis() 명령은 아두이노 코드 업로딩 후 코드가 실행되는 시점부터의 경과 시간을 msec 단위로 출력해준다. 특히 아두이노 시계 코딩을 위해서서 몇날 몇일이고 전원이 제공된다면 아두이노 시계가 작동해야 함과 동시에 millis() 가 출력하게 되는 정수 값이 극도로 커지게 된다, 아두이노에서 가장 큰 정수 값을 사용할 수 있도록 unsigned long 선언이 사용된다. 아두이노 폼페이지 정보를 읽어 보면 50 일 가량 카운팅이 가능하다고 하므로 충분하다 볼 수 있다.

millis() 값을 읽어서 산수 계산을 통해 시간 분 초 값을 얻어낼 수 있다.

noname04.png

즉 36만으로 나눈 상태에서 정수 처리하면 소수점 시간을 버린 시간 값이 얻어진다.
버린 시간 값은 결국 분 데이터 인데 이는 36만으로 나누었을 때 나머지 값에 해당한다. 나머지를 구하기 위한 아두이노 연산은 %로서 수학에서 module 연산에 해당한다. 이 값을 6만으로 %로 나누면 정수 값의 분 데이터가 얻어지고 그 나머지는 초 데이터가 된다. 초 데이터는 msec 단위이므로 1,000 으로 나누어야 한다.

시계 알고리듬과 동시에 일정한 시간 간격으로 예를 들면 30초 즉 3만 msec 간격으로 온습도 데이터를 디스플레이 할 필요가 있다. 온습도 데이터를 디스플레이한 후 몇초를 유지해야 값을 읽을 수 있으므로 3초 가량 출력을 유지한 후 다시 시간 데이터를 출력하도록 하자.

30초에 한번씩 온습도 측정 결과를 출력 2초를 유지하고 나머지는 시계 데이터를 추력하는 알고리듬을 아래의 코드 설명을 참조하여라.

noname05.png

이 시간 계산 알고리듬은 오늘날에도 그대로 사용되는 아주 오래된 고대의 수메르 문명의 60진법을 그대로 사용하고 있다. 60진법이 아직도 사용되는 분야는 시간 계산과 GPS 좌표계산을 위한 인공위성의 위도와 경도 데이터가 그 사례이다. 수메르의 60진법에 관한 참고 자료는 다음의 블로그를 재미로 참조하기 바란다.

고대수메르 문명의 비밀과 아두이노 GPS코딩과 연결고리 고고학적 추론
https://steemit.com/kr/@codingart/gps

이렇게 코드가 완성이 되었으면 코드 전체를 아두이노 스케치 편집기에 넣고 컴파일 업로딩하도록 하자. 이런 종류의 코드는 처음부터 직접 코딩할 필요는 전혀 없다. 일단 실행시켜보고 실행이 잘되지만 뭔가 알고리듬을 더 개량할 수 있는 아이디어가 있다면 직접 손보기 시작하면 된다. C/C++ 문법 나고 사람이 난건 아니니까! ㅋ ㅋ

다음의 동영상을 보시고 과연 스마트 아두이노가 얼마나 정확한지 확인해 보시기 바란다. 30초 간격으로 2초간 온습도를 디스플레이한다.

//Uno_steemit_clock_DHT11_01

#include <DHT11.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>

#define OLED_RESET 4
Adafruit_SH1106 display(OLED_RESET);

//스팀잇 로고
#define steemit_HEIGHT 32
#define steemit_WIDTH 32
static const unsigned char PROGMEM steemit_bmp[] = {
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000001, B00000000, B00000000,
B00000000, B00000111, B00000000, B00000000,
B00000011, B00001111, B00000111, B00000000,
B00000111, B00011111, B00000111, B00000000,
B00001111, B00011111, B00001111, B00000000,
B00001111, B00111111, B00001111, B00000000,
B00000111, B10001111, B10001111, B10000000,
B00000111, B10000111, B10000011, B10000000,
B00000111, B00000011, B11000011, B10000000,
B00000011, B10000001, B11100001, B11000000,
B00000011, B10000001, B11100001, B11100000,
B00000001, B11000001, B11100000, B11100000,
B00000001, B11100000, B11110000, B11110000,
B00000000, B11100000, B11111000, B11110000,
B00000000, B11100000, B11111000, B11100000,
B00000000, B11000000, B11111000, B11000000,
B00000001, B10000000, B11110000, B10000000,
B00000001, B00000001, B11100001, B00000000,
B00000000, B00000001, B11000000, B00000000,
B00000000, B00000011, B10000000, B00000000,
B00000000, B00000011, B00000000, B00000000,
B00000000, B00000010, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000
};
//구름+해 모양 로고
#define W_HEIGHT 32
#define W_WIDTH 32
static const unsigned char PROGMEM W_bmp[] = {
B00000000, B00010010, B00000000, B00000000,
B00000100, B00010010, B00000000, B00000000,
B00000010, B00010000, B00000000, B00000000,
B00000010, B00111100, B00000000, B00000000,
B01000001, B11011110, B00000000, B00000000,
B00010001, B10000011, B11000000, B00000000, //35
B00001011, B00000111, B11100000, B00000000, //34
B00000011, B0001110, B01110000, B00000000,//33
B00000110, B00111100, B00111100, B00000000, //%32
B00000110, B11110000, B00001111, B00000000, //&31
B00000111, B10000000, B00000001, B11100000,//&30
B00001100, B00000000, B00000000, B00110000,
B00001100, B00000000, B00000000, B00110000,
B00001100, B00000000, B00000000, B00110000,
B00000111, B10000000, B00000001, B11100000,//& 26
B00000000, B11110000, B00001111, B00000000, //25
B00000000, B00111100, B00111100, B00000000, //$24
B00000000, B00001110, B01110000, B00000000,//23
B00000000, B00000111, B11110000, B00000000, //22
B00000000, B00000011, B11000000, B00000000, //21
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000
};

#if (SH1106_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SH1106.h!");
#endif

int pin= 5;//DHT11 데이타 핀
unsigned long currentHr = 0;
unsigned long currentMin = 0;
unsigned long interval = 30000;
unsigned long previous_millis = 0;
DHT11 dht11(pin);

void setup() {
Serial.begin(9600);
display.begin(SH1106_SWITCHCAPVCC, 0x3C);
Serial.println("Clock setting data with 0000 in HR & Min");
while( Serial.available() == 0 ) {
}
int hrmin = Serial.parseInt();
currentHr = hrmin/100;
Serial.println(currentHr);
currentMin = hrmin%100;
Serial.println(currentMin);
}

void loop() {

unsigned long currentMillis = millis();
if( currentMillis - previous_millis > interval ) {
int err;
float temp, humi;
if((err=dht11.read(humi, temp))==0) {
display.clearDisplay();
display.drawBitmap(0, 0, W_bmp, W_WIDTH, W_HEIGHT, 1);
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(40,10);
display.println("Steemit");
display.setCursor(0,30);
display.print("temp:");
int t = temp;
display.print(t);
display.print("deg");

display.setCursor(0,50);
display.print("humid:");
int h = humi;
display.print(h);
display.print("% ");
display.display();
delay(2000);
previous_millis = currentMillis;   

}
else {

display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0,30);
display.print("Error No :");
display.print(err);
display.println();  

}

}
else {
unsigned long hr = currentHr + currentMillis/3600000;
currentMillis = currentMillis%3600000;
unsigned long minute = currentMin + currentMillis/60000;
unsigned long second = currentMillis%60000;
unsigned long sec = second/1000;

display.clearDisplay();
display.drawBitmap(0, 0,  steemit_bmp, steemit_WIDTH, steemit_HEIGHT, 1);  
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(40,10);
display.println("Steemit");
display.setCursor(95,30);
if( hr <12 ) {
  display.println("AM");
}
else if( hr <24 ) {
  display.println("PM");
}
else {
  hr = hr -24;
  display.println("AM");
}
display.setTextSize(2);
display.setCursor(30,50);
if( minute >= 60 ) {
  hr = hr + 1;
  minute = minute - 60;
}
display.print((unsigned long)hr);
display.print(":");
display.print((unsigned long)minute);
display.print(":");
display.print((unsigned long)sec);    
display.display();
delay(50);

}
}//loop 끝

Sort:  

pairplay 가 kr-dev 컨텐츠를 응원합니다! :)

Coin Marketplace

STEEM 0.24
TRX 0.11
JST 0.032
BTC 61649.11
ETH 3003.31
USDT 1.00
SBD 3.77