일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- HC-06 사용법
- 익산 보석 박물관
- 128x64
- 익산 명소
- 목포명소
- 좀더큰규모일줄알았다규..
- 이럴때아니면언제이런거보겠어
- 완도 명소
- 완도 구경
- 군산명소
- u8glib
- 군산
- 신안선
- 너무작잖아...
- 목포 박물관
- 보석 관람
- 대중음악박물관
- 해양 전시회
- 시퀀스 그리는 프로그램
- 아두이노
- HC-06
- 당일치기여행
- 시퀀스 프로그램
- 목포
- 비트맵 띄우기
- 눈에보양
- oled
- 군산 구경
- 아두이노 블루투스
- 해양문화재연구소
- Today
- Total
RS's Travel & Electronic
u8glib : OLED에 사용자 메뉴 출력 본문
센서 두세 개에 팬 서너 개 정도 달고 LED좀 꼽아주면 제품이 가지게 되는 상태 스탯은 여러 가지가 생깁니다.
센서에 전원을 투입할지 안할지, 센서 값에 보정을 할지 안 할지, 센서의 값에 의해서 팬이 돌지 아니면 사용자가 매뉴얼로 팬을 켜고 끌지, LED를 켤지 안 켤지.... 등등등
이런 정보들이 생기면 당연히 그것을 제품 사용자가 관찰하고 경우에 따라 조작할 수 있게 해 줘야 되죠.
LED와 버튼으로는 한계가 있어요. LED가 소모시키는 아두이노 핀 여유도 문제고요.
이럴 때 디스플레이 장치에 메뉴를 구성하고 메뉴에 따라 아두이노 내부의 값을 조작할 수 있게 해 주면 좋습니다.
이번엔 U8 GLIB 라이브러리로 128x64 OLED 디스플레이에 이런 사용자 메뉴의 원형이라 할만한 샘플을 구성해보겠습니다.
선 연결은 이전 포스트에서 했던 그대로.
상, 하 버튼은 메뉴 선택 커서를 위아래로 옮기고,
좌, 우 버튼은 메뉴를 선택해서 내부로 진입하거나 상위 메뉴로 이동하는 역할을 합니다.
1, 2 버튼은 값을 증가시키거나 감소시키는 역할을 합니다.
코드입니다.
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
#define BT_U 2
#define BT_R 3
#define BT_L 4
#define BT_D 5
#define BT_1 6
#define BT_2 7
#define DisplayWidth 128
#define DisplayHeight 64
#define CONTINUESEC 500
#define CONTINUECYCLE 80
#define CHATTERINGTIME 5
/* 메뉴 이름 */
char *menu_strings[4] = { "First Menu", "Second Menu", "third Menu", "fourth Menu" };
/* 메뉴번호 */
uint8_t menu_current = 0;
/* 메뉴 진입 여부, 하위메뉴가 증가할 때마다 1씩 증가. 샘플코드에서는 하위메뉴가 1개 뿐이므로
boolean으로 해도 무관함.*/
byte into_menu = 0;
/* 각 메뉴의 내부 변수값 */
int first_val = 0;
int second_val = 0;
int third_val = 0;
int fourth_val = 0;
/* 메뉴커서 위 방향 이동 */
void btPush_U()
{
static boolean bUStat = false;
static boolean labUStat = true;
static unsigned long labUTime = 0;
boolean readingStat = digitalRead(BT_U);
if (readingStat != labUStat) labUTime = millis();
if ( (millis() - labUTime) > CHATTERINGTIME ) {
if (readingStat != bUStat) {
bUStat = readingStat;
if (bUStat == false) {
if (into_menu == 0){
menu_current--;
if ( menu_current == 255) menu_current = 3;
Serial.print("MENU : ");
Serial.println(menu_current);
draw ();
}
}
}
}
labUStat = readingStat;
}
/* 메뉴커서 아래 방향 이동 */
void btPush_D()
{
static boolean bDStat = false;
static boolean labDStat = true;
static unsigned long labDTime = 0;
boolean readingStat = digitalRead(BT_D);
if (readingStat != labDStat) labDTime = millis();
if ( (millis() - labDTime) > CHATTERINGTIME ) {
if (readingStat != bDStat) {
bDStat = readingStat;
if (bDStat == false) {
if (into_menu == 0){
menu_current++;
if ( menu_current > 3) menu_current = 0;
Serial.print("MENU : ");
Serial.println(menu_current);
draw ();
}
}
}
}
labDStat = readingStat;
}
/* 메뉴 진입 명령 */
void btPush_R()
{
static boolean bRStat = false;
static boolean labRStat = true;
static unsigned long labRTime = 0;
boolean readingStat = digitalRead(BT_R);
if (readingStat != labRStat) labRTime = millis();
if ( (millis() - labRTime) > CHATTERINGTIME ) {
if (readingStat != bRStat) {
bRStat = readingStat;
if (bRStat == false) {
into_menu++;
if (into_menu > 1 ) into_menu = 1;
Serial.print("into_menu : ");
Serial.println(into_menu);
draw ();
}
}
}
labRStat = readingStat;
}
/* 상위 메뉴 이동 */
void btPush_L()
{
static boolean bLStat = false;
static boolean labLStat = true;
static unsigned long labLTime = 0;
boolean readingStat = digitalRead(BT_L);
if (readingStat != labLStat) labLTime = millis();
if ( (millis() - labLTime) > CHATTERINGTIME ) {
if (readingStat != bLStat) {
bLStat = readingStat;
if (bLStat == false) {
into_menu--;
if (into_menu == 255 ) into_menu = 0;
Serial.print("into_menu : ");
Serial.println(into_menu);
draw ();
}
}
}
labLStat = readingStat;
}
/* 메뉴 내부 변수값 증가, 지속버튼 타입 */
void btPush_1()
{
static boolean b1Stat = false;
static boolean lab1Stat = true;
static unsigned long lab1Time = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_1);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (into_menu == 1) val_count(1);
draw ();
continueStartTime = millis();
}
}
}
if (readingStat != lab1Stat) lab1Time = millis();
if ( (millis() - lab1Time) > CHATTERINGTIME ) {
if (readingStat != b1Stat) {
b1Stat = readingStat;
if (b1Stat == false) {
nowPushed = true;
pushStartTime = millis();
if (into_menu == 1) val_count(1);
draw ();
}
}
}
lab1Stat = readingStat;
}
/* 메뉴 내부 변수값 감소, 지속버튼 타입 */
void btPush_2()
{
static boolean b2Stat = false;
static boolean lab2Stat = true;
static unsigned long lab2Time = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_2);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (into_menu == 1) val_count(-1);
draw ();
continueStartTime = millis();
}
}
}
if (readingStat != lab2Stat) lab2Time = millis();
if ( (millis() - lab2Time) > CHATTERINGTIME ) {
if (readingStat != b2Stat) {
b2Stat = readingStat;
if (b2Stat == false) {
nowPushed = true;
pushStartTime = millis();
if (into_menu == 1) val_count(-1);
draw ();
}
}
}
lab2Stat = readingStat;
}
/* 실제 변수값의 증감을 담당합니다 */
void val_count (int val){
switch (menu_current){
case 0:
first_val += val;
Serial.print("firstval : ");
Serial.println(first_val);
break;
case 1:
second_val += val;
Serial.print("secondval : ");
Serial.println(second_val);
break;
case 2:
third_val += val;
Serial.print("thirdval : ");
Serial.println(third_val);
break;
case 3:
fourth_val += val;
Serial.print("fourthval : ");
Serial.println(fourth_val);
break;
}
}
/* 내부 변수값을 출력(하위메뉴) */
void drawVal(){
char w[4];
switch (menu_current){
case 0:
u8g.drawStr (10, 25, "first VAL : ");
u8g.drawStr (100, 25, itoa(first_val, w, 10));
break;
case 1:
u8g.drawStr (10, 25, "second VAL : ");
u8g.drawStr (100, 25, itoa(second_val, w, 10));
break;
case 2:
u8g.drawStr (10, 25, "third VAL : ");
u8g.drawStr (100, 25, itoa(third_val, w, 10));
break;
case 3:
u8g.drawStr (10, 25, "fourth VAL : ");
u8g.drawStr (100, 25, itoa(fourth_val, w, 10));
break;
}
}
/* 상위 메뉴 출력, 중앙정렬을 위한 연산과정이 있음 */
void drawMenu(){
uint8_t h = u8g.getFontAscent()-u8g.getFontDescent(); // 세팅된 폰트의 높이를 구한다.
for( int i = 0; i < 4; i++ ) {
/* u8g.getStrWidth(menu_strings[i])는 문자열이 차지할 너비픽셀수를 리턴한다. */
u8g_uint_t d = ( DisplayWidth - u8g.getStrWidth(menu_strings[i]) ) / 2;
if ( i == menu_current ) {
u8g.drawStr(15, i*h, "->");
}
u8g.drawStr(d, i*h, menu_strings[i]);
}
}
/* 메뉴 진입 여부에 따라 상위메뉴를 출력할지 메뉴 내부 변수값을 출력할지 결정 */
void draw() {
u8g.firstPage();
do {
switch (into_menu){
case 0:
drawMenu();
break;
case 1:
drawVal();
break;
}
} while( u8g.nextPage() );
}
void setup() {
pinMode (BT_U, INPUT_PULLUP);
pinMode (BT_D, INPUT_PULLUP);
pinMode (BT_R, INPUT_PULLUP);
pinMode (BT_L, INPUT_PULLUP);
pinMode (BT_1, INPUT_PULLUP);
pinMode (BT_2, INPUT_PULLUP);
Serial.begin (9600);
u8g.setFont(u8g_font_6x13);
u8g.setFontRefHeightText();
u8g.setFontPosTop();
draw ();
}
void loop() {
btPush_U();
btPush_D();
btPush_R();
btPush_L();
btPush_1();
btPush_2();
}
대략 3백 라인 정도 됩니다.
상, 하, 메뉴 진입, 상위 메뉴 이동은 버튼 응용 함수 포스팅의 일반 푸시버튼으로 구성됐고, 변수 증감 버튼은 지속 누름 반응형 버튼으로 구성했습니다.
각 버튼을 누를 때마다 버튼에 해당하는 값을 변경하거나 커서를 이동하거나 혹은 메뉴에 진입, 상위 메뉴로 이동하는 것을 내부적으로 처리한 다음, 실제 OLED에 출력하는 일은 draw() 함수에게 떠넘깁니다.
draw() 함수는 지금 상태가 상위 메뉴 상태인가, 아니면 진입된 상태인가를 따져서 각 상태에 맞는 드로우 함수에게 또 일을 짬 때리지요.
이 상태를 따지기 위한 변수가 byte형 into_menu입니다. 진입인가 아닌가만 알면 되는데 왜 boolean이 아니라 byte냐 라고 한다면 확장을 대비한 겁니다.
실제 시제품을 만들다 보면 의뢰자의 요망으로 제품의 기능이 더 늘어나는 경우가 있습니다.
그럼 출력되는 메뉴도 필연적으로 더 늘어나게 되죠.
만약 하위 메뉴를 2중 이상으로 두는 구조라면 draw() 함수가 짬 때릴 함수의 종류가 3개 이상이 될 겁니다.
짬 때려진 함수는 상위 메뉴일 때는 drawMenu()가 되겠고, 하위 메뉴(변숫값 출력 상태) 일 경우 drawVal()가 됩니다.
얘네들은 자기가 호출된 그 시점의 상태를 그대로 OLED에 출력합니다.
위와 같은 시퀀스로 작동합니다. 시퀀스 자체는 별로 복잡할 게 없죠?
동작 영상입니다.
용량은 이 정도 차지했습니다.
'시퀀스와 아두이노' 카테고리의 다른 글
u8glib : OLED에 한글 비트맵 출력 (0) | 2021.07.15 |
---|---|
u8glib : OLED에 비트맵 이미지 출력하기 (0) | 2021.07.14 |
u8glib : 문자를 로그라이크 게임 처럼 움직이기 (0) | 2021.07.13 |
I2C : 주소 확인하는 코드 (0) | 2021.07.11 |
버튼 : 상황에 맞는 응용 함수 (0) | 2021.07.11 |