
스마트 워치와 같은 디바이스에는 여러 기능이 함께 들어가는 경우가 흔하다, 아두이노 우노와 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 배열(어레이) 데이타를 준비하도록 한다.

헤더 부분의 비트맵 데이터는 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번 핀을 할당한다.

setup(0에서 통신 속도는 9600으로 설정하자. 아울러 display.begin(0 명령에서 OLED 디스플레이의 I2C 인터페이스 0x3C를 정의하도록 하자, 이 I2C 주소는 1602 lcd 에 있어서 0x3F 였음을 기억할 것이다. 즉 배선만 가능하다면 아두이노가 디바이스의 고유주소를 알기 때문에 필요한 디바이스를 호출하여 사용할 수 있게 된다.
아두이노에서 대단히 중요한 millis() 명령에 관해서 알아보자. millis() 명령은 아두이노 코드 업로딩 후 코드가 실행되는 시점부터의 경과 시간을 msec 단위로 출력해준다. 특히 아두이노 시계 코딩을 위해서서 몇날 몇일이고 전원이 제공된다면 아두이노 시계가 작동해야 함과 동시에 millis() 가 출력하게 되는 정수 값이 극도로 커지게 된다, 아두이노에서 가장 큰 정수 값을 사용할 수 있도록 unsigned long 선언이 사용된다. 아두이노 폼페이지 정보를 읽어 보면 50 일 가량 카운팅이 가능하다고 하므로 충분하다 볼 수 있다.
millis() 값을 읽어서 산수 계산을 통해 시간 분 초 값을 얻어낼 수 있다.

즉 36만으로 나눈 상태에서 정수 처리하면 소수점 시간을 버린 시간 값이 얻어진다.
버린 시간 값은 결국 분 데이터 인데 이는 36만으로 나누었을 때 나머지 값에 해당한다. 나머지를 구하기 위한 아두이노 연산은 %로서 수학에서 module 연산에 해당한다. 이 값을 6만으로 %로 나누면 정수 값의 분 데이터가 얻어지고 그 나머지는 초 데이터가 된다. 초 데이터는 msec 단위이므로 1,000 으로 나누어야 한다.
시계 알고리듬과 동시에 일정한 시간 간격으로 예를 들면 30초 즉 3만 msec 간격으로 온습도 데이터를 디스플레이 할 필요가 있다. 온습도 데이터를 디스플레이한 후 몇초를 유지해야 값을 읽을 수 있으므로 3초 가량 출력을 유지한 후 다시 시간 데이터를 출력하도록 하자.
30초에 한번씩 온습도 측정 결과를 출력 2초를 유지하고 나머지는 시계 데이터를 추력하는 알고리듬을 아래의 코드 설명을 참조하여라.

이 시간 계산 알고리듬은 오늘날에도 그대로 사용되는 아주 오래된 고대의 수메르 문명의 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 끝