ESP32, RC카

샤시 조립

샤시 부품

번호 부품 사진 수량 용도 및 참고사항
1 전륜 회전축 2
2 베어링(대) 2
3 스티어링 컵 2
4 커플러 고정핀 2
5 베어링(중) 2
6 (전륜) 육각 커플러 2
7 4
8 나이록 너트(M4) 2
9 십자형 육각 복스 1
10 볼 조인트 커넥팅 로드 1 홀 간격 80.5mm 유지
11 볼 조인트 커넥팅 로드 1 홀 간격 51mm 유지
12 서보모터
MG996R
1
13 ㄴ자 서보모터 브라켓 2
14 휠 베이스 고정판 2
15 서보 혼 1 (볼트 포함)
16 나이록 너트
(M2.5)
1
17 샤시 하판 1
18 DC엔코더 모터 & 커넥터 2
19 모터 브라켓 2
20 샤프트 2
21 써클립 2
22 샤프트 베어링 4
23 기어
(무두볼트(Set Screw) 포함)
2
24 육각 샤프트 커플링 커넥터 2
25 범퍼 1
26 샤시 상판 1
27 락스위치 1
28 황동 육각 스터드
(M3×22mm)
6 휠 베이스 고정용 4개, 상판 고정용 2개
29 황동 육각 스터드
(M3×16mm)
2 범퍼 고정용 2개
30 볼트
(Pan head, M4×6mm)
6 모터 브라켓과 샤시 하판 고정용 6개
31 볼트
(Bind-Washer head, M3×6mm)
10 서보모터와 서보모터 브라켓 고정용 4개, 서보모터 브라켓과 샤시하판 고정용 4개, 범퍼 고정용 2개
32 볼트
(Bind head, M3×8mm)
![`](/image/r/ESP32-RC-Car-30.png) 14 육각 스터드 고정용 12개, 범퍼 고정용 2개
33 볼트
(Pan head, M2.5×10mm)
7 커넥팅 로드 연결용 3개, 휠 베이스 연결용 4개
34 볼트
(Flat head, M2.5×5mm)
4 모터와 모터브라켓 고정
35 육각렌치 1 기어 무두볼트용
36 저항
1K or 5.1K
2 (선택사항) 인코더 풀업저항
37 홀센서 칩
SH41F(SOT-23타입)
2 (선택사항) 홀 센서를 이중채널로 사용할 필요가 있는 경우에, 각 모터마다 1개씩 추가함
36종

전륜 조향 휠 조립

사진 오른쪽부터 차례대로 8개의 부품을 끼워 휠을 조립한다. (왼쪽휠/오른쪽휠 각각 1개씩, 총 2개 조립)

  1. 먼저 검은색의 스티어링 컵에 큰 베어링을 삽입한다. (상당히 뻑뻑하므로 힘을 강하게 주면서 스티어링컵 안쪽 끝까지 밀어넣어야 한다.)

  1. 베어링이 장착된 스티어링 컵에 회전축을 끼운다. 이때, 회전축 중앙에 있는 작은 구멍이 (고정쇠를 끼울 수 있도록) 보여야 하며, 만약 구멍이 보이지 않으면, 이전 과정에서 베어링을 더 밀어넣어 스티어링 컵에 완전히 밀착시켜야 한다.

  1. 회전축이 나온 스티어링컵 밑바닥에 중간 크기의 베어링을 끼운다. 역시 뻑뻑하므로 강한 힘을 주어 밀어넣어야 한다.

  1. 회전축 중앙의 작은 구멍에 커플러 고정핀를 끼워 넣는다.

  1. 커플러 고정핀 끼워 넣는 홈이 파인 육각 커플러를 준비한 뒤, 커플러 고정핀이 꼽혀있는 방향을 고려하여 스티어링컵에 끼운다.

  1. 스티어링컵에 끼운 육각 커플러를, 휠의 안쪽에 끼워 넣으면(이때, 육각 커플러가 휠의 육각 홈 안쪽까지 완전히 밀착되도록 강한 힘을 가하면서 끼운다.) 휠 바깥쪽면에 회전축의 나사산이 나온다.

  1. 바퀴 바깥쪽으로 빠져나온 회전축 나사를 나이록 너트(M4)를 끼워 고정한다. (십자형 육각 복스로 나이록 너트를 돌릴때 ‘딱딱’하는 소리가 몇번 날때까지 돌리면 고정이 된다.)

  1. 동일한 방법으로 하나를 더 만들어 2개의 휠을 완성한다.

  1. 짧은 커넥팅 로드 1개와 긴 커넥팅 로드 1개, M2.5*10mm 나사(Pan head) 3개를 준비한 뒤,

  1. 오른쪽 휠 컵 암(Arm)에 있는 구멍 2개 중, 끝부분의 구멍(빨간색으로 표시된 부분)에

  1. 준비한 볼트(M2.5*10mm (Pan head)) 1개를 이용하여 긴 커넥팅 로드를 고정한다. 이때, 볼트의 끝 부분을 구멍 아래쪽에서 윗쪽으로 넣듯이 하여 고정한다.

​ 또한, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다.

  1. 같은 방법으로 왼쪽 휠 컵의 구멍에 긴 커넥팅 로드의 반대편을 고정한다. (이때, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다. )

  2. 오른쪽 휠 컵의 구멍 2개 중, 파란색으로 표시된 부분에는 동일한 볼트를 사용하여 짧은 커넥팅 로드를 연결한다. (이때, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다. )

  1. 전륜 휠 베이스 완성된 모습


조향 서보모터 조립

  1. MG996R 서보모터를 준비한 후,

  1. 아래 회로도와 스케치를 참고하여, 서보모터의 초기 위치를 90도 위치로 회전시켜 둔다.

    • schematic

      MG996R 갈색 빨간색 주황색
      ESP32 GND 5V GPIO 15
    • sketch

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      const int ledPin = 15;  // corresponds to GPIO 15

      // setting PWM properties
      const int ledChannel = 0;
      const int freq = 50;
      const int resolution = 16;

      void setup()
      {
      // PWM Setup
      ledcSetup(ledChannel, freq, resolution); // PWM CH0, Frequncy 50 Hz, 16bit resolution
      ledcAttachPin(ledPin, ledChannel); // PWM CH0을 GPIO 15번으로 출력
      }

      void loop()
      {
      ledcWrite(ledChannel, 4910); // 1638 ~ 8192 (90degree = 4910)
      delay(500);
      }
      }
  2. ㄴ자 서보모터 브라켓, 브라켓 고정용 볼트(Pan-Washer head, M3×6mm) 8개, 서보 혼(고정용 볼트 1개 포함)를 준비한다.

  1. 와셔볼트 4개를 사용하여 ㄴ자 브라켓을 고정하고, 서버 혼도 고정용 볼트를 사용하여 장착한다. (아래 사진을 잘 보면서 MG996R 스티커의 글씨 방향, ㄴ자 브라켓이 고정된 방향, 서보 혼 방향(현재 사진에 보이는 서보 날개의 위치가 90도가 됨)에 유의하여 장착한다.)

  1. 샤시 하판에 와셔볼트 4개를 사용하여 서보모터를 고정한다.


샤시 하판에 휠 베이스 고정하기

  1. 준비물: 서보모터가 고정된 샤시 하판, 황동 육각 스터드 M3×22mm 4개, 볼트 M3*8mm 8개, M2.5×10mm 4개, 부채꼴 모양의 휠 베이스 고정판 2개

  1. (위 1번 사진의) 빨간색으로 표시된 부분에 육각 스터드 4개를 고정한다.

  1. (위 1번 사진의) 파란색으로 표시된 부분의 구멍에는, 다음 그림과 같이 긴 커넥팅 로드가 붙어있는 쪽의 휠 컵 구멍(아래 사진에서 파란색으로 표시된 부분)을 맞추어 볼트(M2.5×10mm)로 고정시킨다. (이때, 양쪽 휠 컵의 암(Arm) 부분이 차량 전면을 향하도록 한 상태에서 연결해야 한다.)

  1. 고정이 완료된 상태에서 밑면을 보면 다음과 같으며,

​ 위에서 바라본 모습은 다음과 같다.

  1. (파란색 원으로 표시된 부분) 부채꼴 모양의 휠 베이스 고정판을 양쪽 휠에 있는 육각 스터드와 스티어링 컵의 구멍에 고정한다. (볼트 M3×8mm 8개, M2.5×10mm 4개 사용)

    (빨간색 원으로 표시된 부분) M2.5*10mm 나사와 나이록 너트(M2.5)를 사용하여, 짧은 커넥팅 로드를 서보 혼에 고정한다. 이때, 십자형 육각 복스로 나이록 너트를 돌릴때 ‘딱딱’하는 소리가 몇번 날때까지 돌리면 고정이 된다.)

​ 아래 사진은 완성된 전륜 조향 휠 부분을 정면에서 바라본 모습이다.


후륜 모터 파트 조립

  1. 25GA370 엔코더 모터에 모터 커넥터를 끼우고, 볼트 (Flat head, M2.5×5mm) 2개를 사용하여 모터 브라켓를 고정한다.

  2. 베어링 2개, 샤프트, 써클립, 기어, 육각 샤프트 커플링 커넥터, 써클립(Circlip)을 준비한다.

  1. 샤프트 끝부분에 있는 홈에 써클립을 끼운다.

  1. 모터 브라켓 양쪽 끝에 있는 구멍에 베어링을 끼우고, 써클립을 끼운 샤프트를 밀어넣는다.

​ 샤프트를 끼운 후, 모터를 앞쪽과 뒷쪽에서 보았을때의 모습

  1. 모터에 부착되어 있는 작은 황동 기어에 흰색 플라스틱으로 된 기어가 맞물려지도록 샤프트에 끼우고, 기어 측면에 있는 작은 무두볼트(Set Screw) 2개를 육각렌치를 사용하여 적절하게 분배하여 조여준다. 이때 한쪽 무두볼트만 조여서 고정하면 기어에서 무두볼트가 빠지거나 고정이 되지 않을 수 있다. 이를 고려하여 양쪽의 나사를 적절히 분배하여 조이도록 한다.

  1. 육각 샤프트 커플링 커넥터를 준비하여 샤프트에 끼운 후, 커넥터 측면의 볼트를 돌려 고정한다. 이때, 고정하는 볼트의 끝부분이 샤프트의 편평한 면에 닿도록 조여준다.

  1. 같은 방법으로 후면 왼쪽 모터도 조립한다.

  2. 샤시 하판 뒷편 양쪽에 있는 삼각형 형태로 위치한 구멍에, 모터 브라켓을 올려놓고 볼트(Pan head, M4×6mm)를 사용하여 고정한다.

​ 오른쪽 모터를 샤시 후면에 고정한 모습이다. 같은 방법으로 왼쪽 후면 모터도 고정한다.


범퍼 조립

  1. 육각 스터드(M3×16mm) 2개와 볼트 (Bind-Washer head, M3×6mm) 4개 준비

  1. 준비된 육각 스터드를 샤시 하판의 맨 앞쪽에 고정한다.

  1. 범퍼를 육각기둥에 끼운 후, 와셔볼트를 사용하여 고정한다.


샤시 상판 고정

  1. 육각 스터드(M3×22mm) 2개를 서보모터와 DC엔코더모터 사이에 있는 구멍에 볼트(M3.0×8mm)로 고정한다.

​ 육각 스터드를 고정한 모습이다.

  1. 샤시 상판의 보호필름을 떼어내고, 육각 스터드 홀에 맟춰 올린 뒤,

  1. 볼트(Bind head, M3.0×8mm) 2개를 사용하여 상판을 고정한다.

​ 상판의 오른쪽 하단에 락스위치를 꼽아 고정한 뒤에, 배터리의 (+)라인에 직렬로 배선할 수 있도록 준비한다.


후륜 휠 장착

  1. 육각 휠 너트 앞쪽에 있던 나사를 풀러낸 뒤,

  1. 바퀴를 끼우고, 풀러냈던 나사로 다시 고정한다.


샤시 완성



ESP32 Parts

완성된 샤시의 상판과 하판을 이용하여 ESP32, 브레드보드, 모터드라이버, 배터리, 전원모듈 등을 장작한다.


for RC Car (ESP32 + nRF24L01 + TB6612FNG + 25GA370 + MG996R)

sketch

RC Car에 장착하는 ESP32에 업로드

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

struct Value{
uint16_t value_x; // servo motor pwm
uint16_t value_y; // DC motor pwm
};

Value data;

int angle_x = 90; // servo initial position (0~180)
int velo_y = 0; // DC motor inital velocity
int direction_y = 0; // DC motor direction
int i = 0; // joystick center position calculation counter
int center_x = 0; // joystick initial center_x
int center_y = 0; // joystick initial center_y
int ref_xl, ref_xr, ref_ya, ref_yb; // Servo & DC rotation reference value from center position value

//servo setting
const int PIN_SERVO = 15; // servo pwm pin
const int CH_SERVO = 2; // servo pwm channel
const int servoFrequency = 50; // servo pwm frequency (Hz)
const int servoResolution = 16; // servo pwm resolution (bit) (12bit: 0~4095)

// RecvTime
unsigned long lastRecvTime = 0;

void centerData() {
while(i < 100) {
if(radio.available()){
radio.read(&data, sizeof(Value));
center_x = center_x + data.value_x;
center_y = center_y + data.value_y;
i++;
}
}
center_x = center_x / 100; // x축 조이스틱 센터값 (평균)
center_y = center_y / 100; // y축 조이스틱 센터값 (평균)

ref_xl = center_x / 2; // 좌회전 동작 기준값
ref_xr = (4095 + center_x) / 2; // 우회전 동작 기준값
ref_ya = (4095 + center_y) / 2; // accelator 동작 기준값
ref_yb = center_y / 2; // brake 동작 기준값
}

void recvData(){
while(radio.available()){
radio.read(&data, sizeof(Value));
lastRecvTime = millis();
}
}

/*
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_INA1 = 33;
const int PIN_INA2 = 25;
const int PIN_STBY = 26;
const int PIN_INB1 = 27;
const int PIN_INB2 = 14;
const int PIN_PWMB = 12;
*/

// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN2 = 25;
const int PIN_AIN1 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

// led setup
const int PIN_LED = 2;
int ledstate;


void setup() {
// put your setup code here, to run once:
Serial.begin(115200);

radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();

// Servo PWM Setup
ledcSetup(CH_SERVO, servoFrequency, servoResolution); // PWM CH2, Frequncy 50 Hz, 12bit resolution
ledcAttachPin(PIN_SERVO, CH_SERVO); // PWM CH2을 GPIO 15번으로 출력

// center value
centerData();

pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY, OUTPUT);
pinMode(PIN_AIN1, OUTPUT);
pinMode(PIN_AIN2, OUTPUT);
pinMode(PIN_PWMA, OUTPUT);
pinMode(PIN_BIN1, OUTPUT);
pinMode(PIN_BIN2, OUTPUT);
pinMode(PIN_PWMB, OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);
}

void loop() {
ledstate = 0;
unsigned long now = millis();

if( now - lastRecvTime > 5){ // 5ms 마다 서보출력
recvData();
rotate_xy();

move(1, velo_y, direction_y); // motor A(right wheels), velo_y speed, moving direction
move(2, velo_y, direction_y); // motor B(left wheels), velo_y speed, moving direction

if(ledstate > 0) {
digitalWrite(PIN_LED, HIGH);
}
else {
digitalWrite(PIN_LED, LOW);
}
}
}


void move(int motor, int speed, int direction) { //Move specific motor at speed and direction
//motor: A(Right) -> 1, B(Left) -> 2
//speed: 0 is off, and 255 is full speed
//direction: 0 clockwise, 1 counter-clockwise

digitalWrite(PIN_STBY, HIGH); // move

boolean inPin1 = HIGH; // Defalut(direction=0) - Clockwise
boolean inPin2 = LOW;

if(direction == 1) { // Count-clockwise
inPin1 = LOW;
inPin2 = HIGH;
}

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(PIN_AIN1, inPin1);
digitalWrite(PIN_AIN2, inPin2);
ledcWrite(CH_PWMA, speed);
}
else { // if motor is not 1, left wheel
digitalWrite(PIN_BIN1, inPin1);
digitalWrite(PIN_BIN2, inPin2);
ledcWrite(CH_PWMB, speed);
}
}

void stop() {
digitalWrite(PIN_STBY, LOW); // stand-by = stop
}

void rotate_xy() {
Serial.print("x: "); Serial.print(data.value_x);
Serial.print(", y: "); Serial.println(data.value_y);

//x축 서보 구동 조건 설정
if(data.value_x < ref_xl) {
if(angle_x > 55) { // servo 55~125 degree
angle_x--;
}
else {
angle_x = 55;
}
ledstate++;
}
else if(data.value_x > ref_xr) {
if(angle_x < 125) {
angle_x++;
}
else {
angle_x = 125;
}
ledstate++;
}
else {
if(angle_x < 90) {
angle_x++;
}
else if(angle_x > 90) {
angle_x--;
}
else {
angle_x = 90;
}
}
//servo_x.write(angle_x);
servoWrite(CH_SERVO, angle_x);
delay(5); // delay를 더 줄이면 서보 모터 기어에 무리가 감

//y축 DC 구동 조건 설정
if(data.value_y > ref_ya) {
if(direction_y == 0) {
if(velo_y < 255) {
velo_y++;
}
else {
velo_y = 255;
}
}
if(direction_y == 1) {
if(velo_y > 0) {
velo_y--;
}
else {
direction_y = 0;
velo_y = 0;
}
}
ledstate++;
}
else if(data.value_y < ref_yb) {
if(direction_y == 1) {
if(velo_y < 255) {
velo_y++;
}
else {
velo_y = 255;
}
}
if(direction_y == 0) {
if(velo_y > 0) {
velo_y--;
}
else {
direction_y = 1;
velo_y = 0;
}
}
ledstate++;
}
else {
if(velo_y > 0) {
velo_y--;
}
else {
velo_y = 0;
}
}
}

// deg는 0~180도 까지
void servoWrite(int ch, int deg)
{
int servoPWM = map(deg, 0, 180, 2720, 8191); // 16bit (PWM 4.15% ~ 12.5%)
ledcWrite(ch, servoPWM);
}

schematic


16P & 4P jumper block 사용

RC카를 제작해보면 프레임 조립 후 여러가지 구동을 위한 부품을 넣어야 하는데, 큰 브레드보드를 장착하기에는 공간이 좁아 조립이 어려울 수 있다. 그러므로 브레드보드를 제거하고 16P & 4P의 점퍼 블록을 사용해 RC카에 장착해본다.


16P 점퍼블록과 4P 점퍼블록, 그리고 2개의 와이어핀을 준비한다.


2개의 와이어핀을 4P 점퍼블록의 중앙에 꼽아넣음으로써, 4개의 핀소켓이 모두 연결되어 있는 상태가 되도록 만든다. (1개의 와이어만 넣어도 연결은 되지만, 헐거워 고정이 안되기 때문에 2개를 사용하는 것임)


16P 점퍼블록을 사용하여 ESP32 DevkitC V4 보드와 TB6612FNG 모터드라이브를 다음과 같이 연결한다.

32 - PWMA
33 - AIN2
25 - AIN1
26 - NC
27 - BIN1
14 - BIN2
12 - PWMB
GND - GND

ESP32의 5V핀에는 4P 점퍼블록을 꼽아, 5V핀을 3개 사용할 수 있도록 만들어준다. 제작 후 위에서 바라본 모습은 다음과 같다.



for Remote Controller (ESP32 + nRF24L01 + Dual Joystick)

sketch
  • 리모트 컨트롤러로 사용할 TTGO T-energy(ESP32)에 업로드한다.
  • TTGO T-energy를 컴퓨터와 연결하여 사용할 경우에는 반드시 18650 배터리를 제거하거나 스위치를 OFF상태로 두어야 한다.
  • 그러므로 스케치 업로드 시에는 안전한 사용을 위해 18650 배터리를 제거한 뒤 아래의 스케치를 업로드하도록 한다.
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
32
33
34
35
36
37
38
39
40
41
42
43
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

// for no interference, two joysticks must be used with each other ADC!
// one joystick use ADC1: GPIO 36,39,32,33,34,35
// the other must use ADC2: GPIO 4,0,2,15,13,12,14,27,25,26
const int Jstick_x_pin = 25; // Left_Right GPIO25
const int Jstick_y_pin = 32; // Forward_Back GPIO32

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;


void setup() {
Serial.begin(115200);

radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}

void loop() {
data.value_x = Jstick(Jstick_x_pin);
delay(10);
data.value_y = Jstick(Jstick_y_pin);
delay(10);
radio.write(&data, sizeof(Value));

//Serial.print("x: "); Serial.print(data.value_x);
//Serial.print(", y: "); Serial.println(data.value_y);
}

uint16_t Jstick(int Jstick_Pin){
return analogRead(Jstick_Pin);
}

schematic


  • TTGO T-energy 에는 5V 단자가 1개 있음.
  • 2개의 조이스틱에 5V가 각각 입력되어야 하므로, TTGO T-energy의 5V 단자를 2개로 분기할 수 있는 Y자형 케이블을 제작하여 사용하도록 한다.

​ 위 형태는 (오른쪽) 1개의 (F)단자를 (왼쪽) 3개의 (F)단자로 분기하는 형태의 케이블이다.

​ 이를 참고하여 1개의 단자를 2개의 단자로 나눠주는 케이블을 제작하면 된다.

ESP32, DC모터, TB6612FNG

DC모터 사용하기

아두이노의 경우와 마찬가지로 ESP32에서도 TB6612FNG 모터드라이버와 함께, 모터 및 아두이노 단독사용을 위하여 3.7V 18650 2개를 직렬로 연결한 외부전원을 사용한다.


TB6612FNG

기본 핀 배열
VM
VCC
GND(*)
AOUT1
AOUT2
BOUT2
BOUT1
GND
PWMA
AIN2
AIN1
STBY
BIN1
BIN2
PWMB
GND

핀 배열이 다른 경우
GND
VCC
AOUT1
AOUT2
BOUT2
BOUT1
VM
GND
PWMA
AIN2
AIN1
NC (=STBY)
BIN1
BIN2
PWMB
GND
  • VM (모터 전압) = 15V max
  • VCC (로직 전압) = 2.7 ~ 5.5V
  • GND
    • TB6612FNG 모듈을 여러개 테스트한 결과, 기본 핀 배열을 가진 모듈의 3번핀 GND에 연결할 경우 작동이 안되는 몇몇 제품이 있었음
    • 그러므로 8번핀, 9번핀의 GND 사용을 권장
  • 출력전류: 정전류 1.2A (3.2A peak)까지 (모터 2개 사용시 합산 전류임)
  • 모터 제어모드: CW, CCW, short-brake, STOP, stand-by
  • 두개의 모터 출력을 개별 제어하며, 100kHz의 PWM으로 속도 제어
  • 써멀 셧다운 및 저전압 감지회로 내장

Pin의 사용

ESP32와 TB6612FNG모듈을 브레드보드를 통해 연결하기 쉽도록 핀을 구성한 Pinmap이므로, 필요에 따라 수정하여 사용할 수 있다. (아래표는 참고만 할 것!)

핀번호 TB6612FNG 모터드라이브 (기본 핀 배열) ESP32 DEVKIT_C V4 사용시 ESP32 DEVKIT V1 사용시 외부전원 모터 / 역할
1 VM (+) (DC모터에 사용하는 전압 사용)
2 VCC +3.3V +3.3V
3 GND (사용 비추천)
4 A_OUT1 모터A
5 A_OUT2 모터A
6 B_OUT2 모터B
7 B_OUT1 모터B
8 GND (-)
9 GND GND GND
10 B_PWM 12 13 모터B 속도제어
11 B_IN2 14 12 모터B 방향제어
12 B_IN1 27 14 모터B 방향제어
13 STBY 26 27 모터 상태신호
14 A_IN1 25 26 모터A 방향제어
15 A_IN2 33 25 모터A 방향제어
16 A_PWM 32 33 모터A 속도제어

ESP32 DEVKIT V4 사용시
ESP32 DEVKIT V1 사용시


모터 1개 컨트롤하기

(기어박스를 포함한) 일반 DC모터 (엔코더 모듈을 포함한) 엔코더 DC모터
아두이노를 사용한 스마트카 실습용으로 많이 사용됨 Hall sensor를 통해 회전수를 정밀하게 측정가능
(+)와 (-), 2P 단자가 있음 5P or 6P 단자가 있음 (여기서는 (+)와 (-) 2P만 사용)

schematic

ESP32와 컴퓨터를 USB선으로 연결하여 사용할 경우 (단, 모터 외부전원은 사용)


ESP32와 컴퓨터의 연결없이, 외부전원을 사용하여 단독으로 사용할 경우
  • 18650 2S 외부전원(7.4V)을 DC컨버터 모듈에 연결

    • 입력: 8~36V
    • 출력: 1.25~32V 및 USB 5V 출력
    • 75W내 사용 권장, 전류량 최대 5A (4.5A 이내 사용 권장)

  • 여기서는 서보모터 추가 연결을 위해서, 왼쪽 가변 저항을 사용하여 출력 전압을 5V로 세팅하여 사용

  • USB 출력단자는 ESP32의 microUSB 단자에 연결


sketch

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_AIN1 = 33;
const int PIN_AIN2 = 25;
const int PIN_STBY = 26;
const int PIN_BIN1 = 27;
const int PIN_BIN2 = 14;
const int PIN_PWMB = 12;

/*
// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN1 = 25;
const int PIN_AIN2 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;
*/

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

const int PIN_LED = 2;

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY,OUTPUT);
pinMode(PIN_AIN1,OUTPUT);
pinMode(PIN_AIN2,OUTPUT);
pinMode(PIN_PWMA,OUTPUT);
pinMode(PIN_BIN1,OUTPUT);
pinMode(PIN_BIN2,OUTPUT);
pinMode(PIN_PWMB,OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW
}

void loop() {

// Motor A : CW
digitalWrite(PIN_STBY, HIGH); // STBY
digitalWrite(PIN_AIN1, HIGH); // AIN1
digitalWrite(PIN_AIN2, LOW); // AIN2
ledcWrite(CH_PWMA, 255); // PWMA
// Motor B : CW
digitalWrite(PIN_BIN1, HIGH); // BIN1
digitalWrite(PIN_BIN2, LOW); // BIN2
ledcWrite(CH_PWMB, 255); // PWMB
delay(3000);

// Motor : Stand-by
digitalWrite(PIN_STBY, LOW); // STBY
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);

// Motor A : CCW
digitalWrite(PIN_STBY, HIGH); // STBY
digitalWrite(PIN_AIN1, LOW); // AIN1
digitalWrite(PIN_AIN2, HIGH); // AIN2
ledcWrite(CH_PWMA, 255); // PWMA
// Motor B : CCW
digitalWrite(PIN_BIN1, LOW); // BIN1
digitalWrite(PIN_BIN2, HIGH); // BIN2
ledcWrite(CH_PWMB, 255); // PWMB
delay(3000);

// Motor : Stand-by
digitalWrite(PIN_STBY, LOW); // STBY
//delay(1000);

//LED Blink
digitalWrite(PIN_LED, LOW);
delay(3500);
digitalWrite(PIN_LED, HIGH);
delay(500);
digitalWrite(PIN_LED, LOW);
}

Troubleshooting

스케치 중간에 있는 stand-by가 있거나 없거나 동일한 운동을 보일 것 같지만, 실제로는 완전히 다르다.
  • stand-by가 있는 경우

    • 3초간 CW회전 후 정지 → 3.5초 대기 → 0.5초 LED켜졌다가 꺼짐 → 3초간 CCW회전 후 정지→ 3.5초 대기 → 0.5초 LED켜졌다가 꺼짐
  • stand-by가 없는 경우:

    • 모터
      • 모터의 delay 시간(회전시간)이 3초로 되어 있어도 stand-by 상태로 들어가지 않으므로, 실제로는 7초간 회전 후 방향을 바꿈
      • 7초간 CW회전 후 → (정지하지 않고) 곧바로 7초간 CCW회전 (반복)
    • LED
      • 꺼짐: 6.5초간 꺼져있다가 (모터의 delay 시간 3초 + led low delay 시간 3.5초)
      • 켜짐: 0.5초간 켜짐 (반복)
  • 그러므로, 각 상황에 따라 stand-by를 걸어야 할지, 말아야할지를 결정해야 함.

    • stand-by를 걸지 않은 상태에서 멈추려면 PWMA or PWMB를 0으로 주면 됨.

회전 방향이 (생각했던 방향과) 반대로 회전하는 경우

AIN1핀과 AIN2 핀의 번호를 바꿔준다. 즉, 위 스케치에서

1
2
const int AIN2 = 25;
const int AIN1 = 26;

부분을

1
2
const int AIN2 = 26;
const int AIN1 = 25;

로 바꿔준다.



모터 2개 컨트롤하기


sketch

위의 ‘’모터 1개 컨트롤하기’’ 스케치와 동일함.



스케치 최적화

move(), stop()을 만들어서 스케치 최적화

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_AIN1 = 33;
const int PIN_AIN2 = 25;
const int PIN_STBY = 26;
const int PIN_BIN1 = 27;
const int PIN_BIN2 = 14;
const int PIN_PWMB = 12;

/*
// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN1 = 25;
const int PIN_AIN2 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;
*/

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

const int PIN_LED = 2;


void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY,OUTPUT);
pinMode(PIN_AIN1,OUTPUT);
pinMode(PIN_AIN2,OUTPUT);
pinMode(PIN_PWMA,OUTPUT);
pinMode(PIN_BIN1,OUTPUT);
pinMode(PIN_BIN2,OUTPUT);
pinMode(PIN_PWMB,OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW
}


void loop() {

move(1, 128, 0); // motor A(right wheels), half speed, moving forward
move(2, 255, 0); // motor B(left wheels), full speed, moving forward
delay(3000);

stop(); // Motor : Stand-by
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);

move(1, 255, 1); // motor A(right wheels), full speed, moving backward
move(2, 128, 1); // motor B(left wheels), half speed, moving backward
delay(3000);

stop(); // Motor : Stand-by
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
}

void move(int motor, int speed, int direction) { //Move specific motor at speed and direction
//motor: A(Right) -> 1, B(Left) -> 2
//speed: 0 is off, and 255 is full speed
//direction: 0 clockwise, 1 counter-clockwise

digitalWrite(PIN_STBY, HIGH); // move

boolean inPin1 = HIGH; // Defalut(direction=0) - Clockwise
boolean inPin2 = LOW;

if(direction == 1) { // Count-clockwise
inPin1 = LOW;
inPin2 = HIGH;
}

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(PIN_AIN1, inPin1);
digitalWrite(PIN_AIN2, inPin2);
ledcWrite(CH_PWMA, speed);
} else { // if motor is not 1, left wheel
digitalWrite(PIN_BIN1, inPin1);
digitalWrite(PIN_BIN2, inPin2);
ledcWrite(CH_PWMB, speed);
}
}

void stop() {
digitalWrite(PIN_STBY, LOW); // stand-by = stop
}

ESP32, nRF24L01 무선통신

nRF24L01

library

스케치> 라이브러리 포함하기> 라이브러리 관리> nRF24L01 검색한 후, RF24 by TMRh20, Avamander 설치


schematic: nRF24L01 receiver with ESP32

※ nRF24L01 모듈에 따라 10uF 캐페시터가 없으면 통신이 안되는 경우가 있음. 이런 경우에만 사용할 것.


pinmap: (안테나를 위로 향하게 두고, 모듈을 위에서 내려다 볼때의 핀배열)
ESP32 3V3 IO5 IO23 None
nRF24L01 VCC + 10uF CSN MOSI IRQ
(위에서 보이는 핀배열) GND + 10uF CE SCK MISO
ESP32 GND IO4 IO18 IO19

sketch: 1:1 Chat Room

  • 양방향 송수신을 위하여 위 회로도를 보고 2개의 모듈을 만든 뒤, 다음 스케치를 업로드한다.
  • 시리얼 모니터를 띄운 뒤, 메세지를 전송해본다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//Libraries for NRF24L01+ module.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//RF24 object with two pins defined with arguments. CE: 4, CSN: 5
RF24 radio(4, 5);

//Address of the pipe. 40 bit long, you can choose this freely.
//Remember to use different address in different projects.
long long address = 0x1234ABCDEFLL;

int count = 0;
char stext[32] = "";
int spos = 0;
char rtext[32] = "";
int rpos = 0;

void sendText(char * text, int tlen)
{
radio.stopListening();
radio.openWritingPipe(address);
radio.write(stext, tlen);

Serial.print("SEND: ");
Serial.println(text);

memset(stext, 0x00, 32);

radio.openReadingPipe(0, address);
radio.startListening();
}

void setup() {
//Start the radio
Serial.begin(115200);
radio.begin();

//Open reading pipe with given address and start listening for incoming data
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
}

void loop() {
while(0 < Serial.available()) {
stext[spos] = Serial.read();

if(stext[spos] == 0x0a) {
sendText(stext, spos);
spos = 0;
}
else {
spos += 1;
}
}

if (radio.available()) {
while (radio.available()) {
radio.read(rtext, 32);
}
Serial.print("RECV: ");
Serial.println(rtext);
}
}

sketch: 1:1 Chat Room

다음은 처음 실행 후 시리얼모니터에 입력한 ID를 이용하여 Chat Room을 만드는 코드이다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Libraries for NRF24L01+ module.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//RF24 object with two pins defined with arguments. CE: 4, CSN: 5
RF24 radio(4, 5);

//Address of the pipe. 40 bit long, you can choose this freely.
//Remember to use different address in different projects.
long long address = 0x1234ABCDEFLL;

String username = "";
String dataInput;
char dataToSend[32];
char dataReceived[32];

void setup() {
//Start the radio
Serial.begin(115200);
delay(2000);
Serial.println("Enter username: ");
radio.begin();
radio.setRetries(3, 5);
//Open reading pipe with given address and start listening for incoming data
radio.openWritingPipe(address);
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
}

void loop() {

//set username
while(username == "") {
if(Serial.available()) {
username = Serial.readStringUntil('\n');
Serial.print("Welcome ");
Serial.println(username);
}
}

radio.startListening();

if(radio.available()) {
radio.read(&dataReceived, sizeof(dataReceived));
Serial.println(dataReceived);
}

if(Serial.available()) {
radio.stopListening();

dataInput = "[" + username + "] " + Serial.readStringUntil('\n');
Serial.println(dataInput);
dataInput.toCharArray(dataToSend, 32);

radio.write(&dataToSend, sizeof(dataToSend));
}
}


nRF24L01을 이용한 센서값 전송

for Receiver

schematic


sketch
long long address = 0x1234ABCDEFLL;
  • receiver와 transmitter의 페어링을 위한 파이프(주소)를 지정함. (receiver와 transmitter의 파이프(주소)는 동일해야 함)
  • 주변 여러 사람이 동시에 nRF24L01 모듈을 사용할 경우, 서로 다른 고유의 파이프(주소)를 지정하여야 한다.

uint16_t temphumi[2];
  • 8-bit 기반 Arduino와 32-bit 기반 ESP32를 receiver와 transmitter로 혼용하여 사용할 경우, 아래와 같이 int를 사용하여 변수(혹은 배열)를 선언하면 문제가 발생함.

    1
    int temphumi[2];

    8비트 아키텍처에서 int는 16비트이지만, 32비트 아키텍처에서는 32비트로 정의되기 때문이며,

  • 이로 인해 radio.write 또는 radio.read를 할때 일부 데이터가 송신(혹은 수신)되지 않을 수 있다.

  • 그러므로, arduino와 ESP32를 함께 사용할때에는, 아래와 같은 형태로 변수(혹은 배열) 선언시 특정 비트의 변수로 제한하여 선언할 필요가 있다.

    1
    uint16_t temphumi[2];
  • 단, 송수신기로 Arduino만 사용하거나, 혹은 ESP32만 사용하는 경우에는 int를 사용해도 됨


radio.openReadingPipe(1, address);

페어링 파이프(주소)를 오픈


for Transmitter

schematic

스케치를 업로드하고, 컴퓨터와 연결한 USB선을 분리한 뒤, 독립하여 동작하도록 외부 전원을 연결한다.

※ nRF24L01 모듈에 따라 10uF 캐페시터가 없으면 통신이 안되는 경우가 있음. 이런 경우에만 사용할 것.


18650을 사용하여 5V 외부 전원 만들기
  • ESP32를 외부전원을 통해 동작시키려면, ESP32의 5V/GND 단자를 사용하여 512V의 전압(최적 전압은 67V)을 공급하여야 한다.
  • 18650 소켓에 F단자를 납땜하여 연결한다. (M단자는 서로 맞닿을 경우 과열 위험이 있으므로 사용금지!)


  • DC-DC 5V 승압 모듈에 아래 그림과 같이 납땜하여 연결한다. (USB단자는 사용하지 않음)


※ USB단자가 있는 5V 승압 모듈을 사용하였지만, USB단자가 없는 승압모듈을 사용하면 더 깔끔하게 제작 가능하다.


  • 위 사진과 같이 납땜한 승압 모듈을 18650배터리와 ESP32에 연결한다.
승압 모듈 ESP32 18650배터리
파란색 원 (GND) GND
빨간색 원 (5V) 5V
노란색 원(IN+) (+)극
초록색 원(IN-) (-)극

(transmitter의 경우, 많은 전류를 사용하지 않으므로) 위 그림처럼 ESP32에 DC컨버터를 사용하여 전원을 공급하는 형태보다는, TTGO T-Energy 사용을 추천함

TTGO T-Energy

sketch: DHT22 temperature and humidity Sensor
library for DHT11 or DHT22 sensor

먼저 온습도 센서 데이터를 보내기 위해 온습도 센서 라이브러리를 설치

스케치> 라이브러리 포함하기> 라이브러리 관리> DHT 검색

  • 먼저 **DHT sensor library (by Adafruit)**를 검색하여 설치를 누르면,

  • Dependencies for library DHT sensor library 창이 나오면 Install all을 클릭하여 2개의 라이브러리를 동시에 설치


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//Libraries for NRF24L01+ module.
#include <SPI.h>
#include <RF24.h>

//RF24 object with two pins defined with arguments. CE: 4, CSN: 5
RF24 radio(4, 5);

//Address of the pipe. 40 bit long, you can choose this freely.
//Remember to use different address in different projects.
long long address = 0x1234ABCDEFLL;

// for temperature, humidity sensor
#include "DHT.h"
#define DHTPIN 13 // data pin
#define DHTTYPE DHT22 // change to DHT11 if you're using the DHT11
//float temphumi[2]; // AM2301(DHT21) -> DHT21, AM2302(DHT22) -> DHT22
uint16_t temphumi[2];
DHT dht(DHTPIN, DHTTYPE);


void setup() {
//Start the radio
Serial.begin(115200);

radio.begin();
radio.openWritingPipe(address);
//RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
//NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm
radio.setPALevel(RF24_PA_LOW);
radio.stopListening();

dht.begin();
}

void loop() {
//Get temperature from the sensor
uint16_t t = dht.readTemperature();
uint16_t h = dht.readHumidity();

temphumi[0] = t;
temphumi[1] = h;

//Send the temperature wirelessly, print error if failed
if (!radio.write(&temphumi, sizeof(temphumi))) {
Serial.println(F("Sending temperature failed"));

// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
delay(2000);
}
}

  • long long address = 0x1234ABCDEFLL;

    • receiver와 transmitter의 페어링을 위한 파이프(주소)를 지정함. (receiver와 transmitter의 파이프(주소)는 동일해야 함)

    • 주변 여러 사람이 동시에 nRF24L01 모듈을 사용할 경우, 서로 다른 고유의 파이프(주소)를 지정하여야 한다.


  • radio.setPALevel(RF24_PA_LOW);
    • RF24_PA_MIN
    • RF24_PA_LOW
    • RF24_PA_HIGH
    • RF24_PA_MAX: 가장 강함 (전류소모도 큼)

result



nRF24L01 + Dual Joystick

2개의 조이스틱을 사용하여 x축, y축 좌표값을 받아보자.


for Transmitter

schematic: nRF24L01 transmitter with ESP32, Dual Joystick

하나의 조이스틱으로도 일반적인 조정이 가능하지만, 시판되는 조정기의 경우 dual joystick(혹은 dual channel)을 사용하는 형태가 많으므로, 여기서도 이와 같은 형태의 transmitter를 만들어 보자.

  • 위 그림에서는 18650을 DC 컨버터를 사용하여 5V핀에 입력하고 있음.

  • (transmitter의 경우, 많은 전류를 사용하지 않으므로) 위 그림처럼 ESP32에 DC컨버터를 사용하여 전원을 공급하는 형태보다는, TTGO T-Energy 사용을 추천함

    TTGO T-Energy

pinmap
ESP32 Joystick1 (x축) Joystick2 (y축)
GND GND GND
5V 5V 5V
25 VRX
32 VRY

sketch: nRF24L01 transmitter with ESP32, Dual Joystick
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
32
33
34
35
36
37
38
39
40
41
42
43
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

// for no interference, two joysticks must be used with each other ADC!
// one joystick use ADC1: GPIO 36,39,32,33,34,35
// the other must use ADC2: GPIO 4,0,2,15,13,12,14,27,25,26
const int Jstick_x_pin = 25; // Left_Right GPIO25
const int Jstick_y_pin = 32; // Forward_Back GPIO32

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;


void setup() {
Serial.begin(115200);

radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}

void loop() {
data.value_x = Jstick(Jstick_x_pin);
delay(10);
data.value_y = Jstick(Jstick_y_pin);
delay(10);
radio.write(&data, sizeof(Value));

//Serial.print("x: "); Serial.print(data.value_x);
//Serial.print(", y: "); Serial.println(data.value_y);
}

uint16_t Jstick(int Jstick_Pin){
return analogRead(Jstick_Pin);
}

Joystick GPIO의 선정
1
2
3
4
5
// for no interference, two joysticks must be used with each other ADC! 
// one joystick use ADC1: GPIO 36,39,32,33,34,35
// the other must use ADC2: GPIO 4,0,2,15,13,12,14,27,25,26
const int Jstick_x_pin = 25; // Left_Right GPIO25
const int Jstick_y_pin = 32; // Forward_Back GPIO32
  • 2개의 joystick을 동시에 사용할때 동일한 ADC의 GPIO를 사용하면, 한쪽 joystick을 움직여 값을 변화시킬 경우 다른 한쪽 joystick을 움직이지 않았음에도 동시에 값이 일부 (소폭) 변화하는 현상이 생긴다.
  • 이 문제를 해결하기 위해 각각의 joystick을 서로 다른 ADC에 연결한다.
    • ADC1: GPIO 36,39,32,33,34,35 중 1개 사용 (예제에서는 GPIO 32를 y축 joystick으로 사용)
    • ADC2: GPIO 4,(0),(2),15,13,(12),14,27,25,26 중 1개 사용 (()안의 핀은 사용시 주의 필요) (예제에서는 GPIO 25를 x축 joystick으로 사용)
  • 단, ESP32의 wifi 기능을 이용하면 ADC1핀 중에서 wifi 기능에 공유되는 핀의 사용에 제한을 받게 되므로 주의가 필요하다.
  • 그밖에도 사용하는 ADC의 noise를 제거하기 위해 다음과 같은 방법을 쓰는 것이 좋다.
    • GPIO와 GND에 100nF 케패시터를 연결하여 사용
    • 10여회 정도의 값을 받아 평균값으로 사용

for Receiver

schematic: nRF24L01 receiver with ESP32


sketch
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
32
33
34
35
36
37
38
39
40
41
#include <SPI.h>
#include <RF24.h>
RF24 radio(4 ,5);

long long address = 0x1234ABCDEFLL;

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;


void setup() {
Serial.begin(115200);
radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();
}

unsigned long lastRecvTime = 0;

void recvData(){
while( radio.available()){
radio.read(&data, sizeof(Value));
lastRecvTime = millis();
}
}

void loop() {
unsigned long now = millis();

if( now - lastRecvTime > 1000){
recvData();
}

Serial.print("x: "); Serial.print(data.value_x);
Serial.print(", y: "); Serial.println(data.value_y);
}

result



nRF24L01 + Dual Joystick + Dual Servomotors

for Receiver

schematic: nRF24L01 receiver with ESP32, Dual Joystick

  • MG996R과 같이 전류 사용량이 큰 서보 모터를 2개 이상 사용하는 경우, ESP32의 5V pin에서 공급하는 전류량이 부족하여 서보모터 동작이 원활치 않을 수 있다. 이런 경우에는 아래와 같이 외부 전원을 사용하여야 한다.

pinmap
ESP32 servo_x servo_y
GND GND (갈) GND (갈)
5V 5V (빨) 5V (빨)
25 VRX (주)
32 VRY (주)

schematic: nRF24L01 receiver with ESP32, Dual Joystick, External Power (standalone)


외부 전원의 구성
  • 18650 리튬이온 전지 2개를 사용하여 7.4V를 공급 (혹은 18650 3개를 사용하여 11.1V로 구성)

  • XL4015E1칩을 사용한 HW-514 DC-DC regulator를 사용하여, 7.4V → 5V로 전압을 낮춘다. (유사한 형태의 모듈 사용 가능)

    HW-514 HC v0.2 Specification
    Input voltage: 5-36V (6.5V 미만 입력시 디스플레이 동작안됨)

    Output voltage: 1.25-32V continuously adjustable
    Output current: adjustable, up to 5A (4.5A 이내 권장)
    Output power: 75W max (50W 이내 권장)

    Conversion efficiency: up to 96%

    Working frequency: 180KHz
    Short circuit protection: yes
    Over-temperature protection
    Input reverse polarity protection: None,
    (필요시 고전류 다이오드를 입력단에 사용)

    Module Size: 68.2 * 38.8 * 15 (mm)
    • 왼쪽 가변저항으로 출력 전압을 5V 조정 (오른쪽 가변저항은 출력 전류를 조정)

    • 2P 출력단자는 모터에 직접 연결

    • USB포트는 USB-A to microUSB 케이블을 사용하여 ESP32에 연결 (이런 이유로 USB단자가 있는 모듈을 사용하는 것이 편함)

  • 아래와 같은 mini360, mini560 등의 소형 컨버터를 사용하면,

    • SG90 서보모터는 정상적으로 동작
    • MG996R 서보모터를 연결한 경우에는 컨버터 자체가 불안정하여 모터 동작이 멈춤

※ 외부 전원 구성시 주의할 점!
  • 외부 전원을 ESP32의 5V or 3.3V 핀에 직접 연결하지 않도록 한다.
  • 외부 전원을 DC 컨버터를 사용하여 5V or 3.3V로 만든 경우에도 5V or 3.3V 핀에 직접 연결하면 안된다.
  • ESP32의 스펙상 5V or 3.3V 핀을 사용하여 외부 전원을 구성해도 된다고 나와 있지만, 실제로는 모터 등의 모듈이 작동하지 않거나, 심할 경우 ESP32 보드가 망가질 수 있으므로 이와 같은 구성은 지양한다.
  • (경험상) ESP32의 microUSB포트를 통해 전원을 공급하는 것이 가장 안정적으로 동작하였음!

sketch: nRF24L01 receiver with ESP32, Dual Joystick , External Power (standalone)
library for ESP32

스케치> 라이브러리 포함하기> 라이브러리 관리 에서 “esp32servo“ 검색


sketch
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;

//Servo motor library for ESP32
#include <ESP32Servo.h>

Servo servo_x; // create servo object to control a servo
Servo servo_y; // 16 servo objects can be created on the ESP32

int angle_x, angle_y;

// Recommended PWM GPIO pins on the ESP32 include 2,4,12-19,21-23,25-27,32-33
int servo_xPin = 15;
int servo_yPin = 2;

// RecvTime
unsigned long lastRecvTime = 0;

void recvData(){
while( radio.available()){
radio.read(&data, sizeof(Value));
lastRecvTime = millis();
}
}

void setup() {
Serial.begin(115200);

radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();

// Allow allocation of all timers
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo_x.setPeriodHertz(50); // standard 50 hz servo
servo_y.setPeriodHertz(50);
servo_x.attach(servo_xPin, 500, 2500); // attaches the servo on pin 25 & 32 to the servo object
servo_y.attach(servo_yPin, 500, 2500);
// using default min/max of 1000us and 2000us
// different servos may require different min/max settings
// for an accurate 0 to 180 sweep
}

void loop() {
recvData();
unsigned long now = millis();

if( now - lastRecvTime > 1000){
//ResetData();
}

rotate_xy();
}

void rotate_xy() {
Serial.print("x: "); Serial.print(data.value_x);
Serial.print(", y: "); Serial.println(data.value_y);
angle_x = map(data.value_x, 0, 4095, 0, 180);
angle_y = map(data.value_y, 0, 4095, 0, 180);
servo_x.write(angle_x);
servo_y.write(angle_y);
delay(10);
}



조이스틱 중립 조정

  • 2개의 조이스틱을 중립에 두고, 시리얼 모니터를 통해 센서값을 확인했을때, x축 서보모터는 2700정도, y축 서보모터는 2800 정도의 값을 나타내었다.

  • 이를 기준으로 조이스틱의 값에 따른 대략적인 형태를 생각해보면 (아래의 원 그림에 2개의 조이스틱 값을 모두 나타냄)

  • x축 조이스틱 값이

    • 1350~3400 일때: 중립 (서보모터는 90도 위치로 이동)
    • 0~1350 일때: 서보모터 1도씩 감소 (0도 위치가 될때까지)
      • 단, 자동차 조향 시스템에 적용할 경우 55~90도 사이에서만 움직이도록 조정
    • 3400~4095 일때: 서보모터 1도씩 증가 (180도 위치가 될때까지)
      • 단, 자동차 조향 시스템에 적용할 경우 90~125도 사이에서만 움직이도록 조정
  • y축 조이스틱 값이

    • 1400~3350 일때: 중립 (서보모터는 90도 위치로 이동)
    • 0~1400 일때: 서보모터 1도씩 감소 (0도 위치가 될때까지) (자동차에 적용시 브레이크 역할)
    • 3350~4095 일때: 서보모터 1도씩 증가 (180도 위치가 될때까지) (자동차에 적용시 액셀레이터 역할)
  • 위 그림에 나타낸 값은 모든 조이스틱에 적용되는 절대적인 값이 아니며, 조이스틱마다 중립을 나타내는 센서값이 다르므로, 초기값을 100번 읽은 후 평균값을 만들어 사용한다.


sketch
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <SPI.h>
#include <RF24.h>
RF24 radio(4 ,5);

long long address = 0x1234ABCDEFLL;

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;

//Servo motor library for ESP32
#include <ESP32Servo.h>

Servo servo_x; // create servo object to control a servo
Servo servo_y; // 16 servo objects can be created on the ESP32

int angle_x = 90;
int angle_y = 90;
int i = 0;
int center_x = 0;
int center_y = 0;
int ref_xl, ref_xr, ref_ya, ref_yb; // Servo rotation reference value

// for no interference, each joysticks must be used with other ADC! (* not recommended)
// one joystick use ADC1: GPIO 36,39,32,33,34,35
// the other must use ADC2: GPIO 4,0*,2,15,13,12,14,27,25,26
int servo_xPin = 15;
int servo_yPin = 2;

// RecvTime
unsigned long lastRecvTime = 0;

void centerData(){
while(i < 100) {
if(radio.available()){
radio.read(&data, sizeof(Value));
center_x = center_x + data.value_x;
center_y = center_y + data.value_y;
i++;
}
}
center_x = center_x / 100; // x축 조이스틱 센터값
center_y = center_y / 100; // y축 조이스틱 센터값

ref_xl = center_x / 2; // 좌회전 동작 기준값
ref_xr = (4095 + center_x) / 2; // 우회전 동작 기준값
ref_ya = (4095 + center_y) / 2; // accelator 동작 기준값
ref_yb = center_y / 2; // brake 동작 기준값
}

void recvData(){
while(radio.available()){
radio.read(&data, sizeof(Value));
lastRecvTime = millis();
}
}

void setup() {
Serial.begin(115200);

radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();

// Allow allocation of all timers
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo_x.setPeriodHertz(50); // standard 50 hz servo
servo_y.setPeriodHertz(50);
servo_x.attach(servo_xPin, 500, 2500); // attaches the servo on pin 25 & 32 to the servo object
servo_y.attach(servo_yPin, 500, 2500);
// using default min/max of 1000us and 2000us
// different servos may require different min/max settings
// for an accurate 0 to 180 sweep

// center value
centerData();
}

void loop() {
unsigned long now = millis();

if( now - lastRecvTime > 5){ // 5ms 마다 서보출력
recvData();
rotate_xy();
}
}

void rotate_xy() {
//Serial.print("x: "); Serial.print(data.value_x);
//Serial.print(", y: "); Serial.println(data.value_y);

//x축 서보 구동 조건 설정
if(data.value_x < ref_xl) {
if(angle_x > 0) {
angle_x--;
}
else {
angle_x = 0;
}
}
else if(data.value_x > ref_xr) {
if(angle_x < 180) {
angle_x++;
}
else {
angle_x = 180;
}
}
else {
if(angle_x < 90) {
angle_x++;
}
else if(angle_x > 90) {
angle_x--;
}
else {
angle_x = 90;
}
}

//y축 서보 구동 조건 설정
if(data.value_y < ref_yb) {
if(angle_y > 0) {
angle_y--;
}
else {
angle_y = 0;
}
}
else if(data.value_y > ref_ya) {
if(angle_y < 180) {
angle_y++;
}
else {
angle_y = 180;
}
}
else {
if(angle_y < 90) {
angle_y++;
}
else if(angle_y > 90) {
angle_y--;
}
else {
angle_y = 90;
}
}

servo_x.write(angle_x);
servo_y.write(angle_y);
delay(5); // delay를 더 줄이면 서보 모터 기어에 무리가 감
}

ESP32, OLED (한글 출력 하기)

OLED

종류

ESP32나 Arduino에서 사용할 수 있는 oled의 종류는 매우 많지만, 주로 128*64 픽셀을 가진 0.96인치, 1.3인치의 SSD1306, SH1106 컨트롤러를 사용한 oled가 판매되고 있으며, 통신 프로토콜은 I2C나 SPI를 사용한다. oled 모듈 구입시 픽셀수, 컨트롤러 및 통신프로토콜을 잘 알아두어야 스케치 작성시 참고할 수 있음에 유의한다. I2C 방식은 GPIO를 2개만 사용하여 구성이 쉽다는 장점이 있고, SPI방식은 4~5개의 GPIO를 사용하지만 속도가 빠르다는 장점이 있다. 겉모습만으로는 컨트롤러를 확인하기는 어렵다.

I2C SPI

이밖에도 U8g2 라이브러리에서 사용가능한 다양한 oled의 종류를 아래 링크에서 확인할 수 있다.

Constructor Reference for U8g2 library



SPI OLED

pinmap

OLED ESP32 (HW_SPI로 사용시) (VSPI) (I2C겸용보드인 경우)
GND GND
VDD 3.3V
D0 18 (HW_SPI 사용시 GPIO_18로 고정) SCK/CLK/CLOCK SCL
D1 23 (HW_SPI 사용시 GPIO_23으로 고정) DATA/DIN/MOSI SDA
RES 15 RST/RESET
DC 14 Data Command
CS 5 Chip Select

※ HW_SPI로 사용하는 경우에는 D0, D1의 핀번호를 바꿀 수 없으며, SW_SPI로 사용하는 경우에는 사용되는 모든 핀의 번호를 임의로 지정하여 사용할 수 있다.


schematic

  • SW_SPI 연결시에는 스케치에서 지정하는 GPIO핀에 연결한다.
  • HW_SPI로 연결할 때의 회로도는 아래 그림과 같다.


sketch

oled를 SPI로 연결하는 경우, 다음을 추가해야 한다.

1
#include <SPI.h>

sketch: SH1106, HW_SPI

HW_SPI를 사용하는 경우에는 D0(CLK), D1(MOSI)을 ESP32에 지정된 핀으로 사용하기 때문에, 나머지 3개의 핀만 sketch에서 지정해주면 된다.

1
2
// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);

sketch: SH1106, SW_SPI

SW_SPI를 사용하는 경우, 4~5개의 핀을 sketch에 지정해주어야 한다.

1
2
// for SW_SPI
U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);

sketch: SSD1306, HW_SPI

SSD1306을 사용하는 oled의 경우 U8G2_SH1106_~ 부분을 U8G2_SSD1306_~ 으로 수정하여 사용한다. 자세한 옵션은 Constructor Reference for U8g2 library에서 확인할 수 있다.

1
2
// for HW_SPI (VSPI)
U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);

sketch: SSD1306, SW_SPI

SW_SPI를 사용하는 경우, 4~5개의 핀을 sketch에 지정해주어야 한다.

1
2
// for SW_SPI
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);


I2C OLED

pinmap

OLED ESP32
GND GND
VDD 3.3V
SDA 21 (HW_I2C 사용시 GPIO_21로 고정)
SCL 22 (HW_I2C 사용시 GPIO_22으로 고정)

schematic


sketch

sketch: SH1106, HW_I2C

HW_I2C를 사용하는 경우에는 SDA, SCL을 ESP32에 지정된 핀으로 사용하기 때문에, 구동 드라이버만 지정해주면 된다.

1
2
// for SH1106,  I2C, (ESP32: SDA 21, SCL 22)
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

sketch: SSD1306, HW_I2C
1
2
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);


U8g2 라이브러리

ESP32나 Arduino에서 OLED를 사용을 위한 대표적인 라이브러리는 3~4가지 정도이다.

  • U8glib
  • U8g2
  • Adafruit SSD1306
  • UTF8_HanDraw

이들 중에서도 특히 U8glib와 Adafruit SSD1306 라이브러리가 가장 많이 사용되고 있다. 그렇지만 한글 출력이 쉽지 않다는 단점이 있으며, U8glib는 현재 개발이 중단된 상태이기도 하다. U8g2는 U8glib를 이어 새롭게 개발되는 라이브러리이며, U8glib의 기능을 모두 포함하고 있으므로 접근이 용이하다. 그러므로 여기서는 U8g2 라이브러리를 중심으로 ESP32에서 OLED를 사용하는 방법을 알아보고, 이에 더하여 한글 출력이 용이한 UTF8_HanDraw를 사용해보도록 한다.


사용 방법

라이브러리 설치
  • 스케치> 라이브러리 포함하기> 라이브러리 관리> u8g2 검색 후 설치 클릭


U8g2 reference

U8g2 라이브러리 사용에 필요한 명령어들은 u8g2reference · olikraus/u8g2 Wiki · GitHub 를 참고한다.


sketch

  • SH1106 드라이버를 사용하여 HW_SPI로 OLED 출력을 수행할 경우, 아래와 같은 예제를 사용해볼 수 있다.

  • 실제 사용하는 OLED 드라이버와 통신 프로토콜에 따라 sketch를 수정해서 사용해야 한다.

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
32
33
34
35
36
37
#include <Arduino.h>
#include <U8g2lib.h>

// for SPI
#include <SPI.h>

// for using Korean NanumGothicCoding font
//#include "u8g2_font_unifont_t_korean_NanumGothicCoding_16.h"

// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SW_SPI
//U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// for SH1106, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup() {
u8g2.begin();
u8g2.enableUTF8Print(); // enable UTF8 support for the Arduino print() function
}

void loop() {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0,15, "SKS STEAM!");
u8g2.drawTriangle(27,50, 64,32, 100,50);
} while ( u8g2.nextPage() );
delay(1000);
}

기본 사용 선언

void setup() 내에 U8g2 라이브러리를 사용하여 oled를 사용하는 것을 선언 한다.

1
u8g2.begin();

폰트
  • 폰트는 u8g2_font_ncenB14_tr를 사용하고 있는데, fntlist8 · olikraus/u8g2 Wiki · GitHub에서 글자 크기별로 사용 가능한 폰트 목록을 확인할 수 있다.
  • 폰트명에 ncenB14가 포함되어 있으므로 폰트(세로) 픽셀수가 14 라는 것을 알 수 있다.
1
u8g2.setFont(u8g2_font_ncenB14_tr);

drawing sheet 사용하여, 그림 그려보기

128X64 픽셀을 표시한 Drawing Sheet를 사용하여, 원하는 그림을 그려보자.

Drawing Sheet


drawStr
1
u8g2.drawStr(0,15, "SKS STEAM!")
  • drawStr를 통해 문자를 프린트할 수 있다.
  • 예제에서 사용한 0,15는 프린트할 문자열 “SKS STEAM!“ 중 맨 앞글자인 “S“의 왼쪽 아래 시작 픽셀을 의미한다.
  • 폰트사이즈가 14이므로 0,15 픽셀에서 시작하며, 0,1 픽셀까지 출력에 사용한다.
  • 만약 시작 픽셀을 0,14로 바꾸면, 0,0 픽셀까지 출력하므로, 문자를 올바르게 출력할 수 있다.
  • 만약 시작 픽셀을 0,13으로 2픽셀 위로 올리게되면 0,-1 픽셀까지 출력되므로, 글자의 맨 윗부분 1행 전체가 삭제된 상태로 출력된다.
  • 음수(-)를 허용하므로, 이를 적절히 이용하면 가로방향 또는 세로방향 스크롤로 사용할 수 있다.

drawTriangle
1
u8g2.drawTriangle(27,50, 64,32, 100,50);
  • U8g2 라이브러리에서 삼각형을 그릴때 사용하는 명령어이다.
  • 지정된 픽셀(128x64)내에 3쌍의 순서쌍으로 3개의 꼭지점을 지정하면 이에 맞춰 삼각형을 그린다. 여기서는 (27,50), (64,32), (100,50)이 꼭지점이다.

※ 그밖의 도형을 그리거나 글자 모양을 바꾸려면, u8g2reference · olikraus/u8g2 Wiki · GitHub 를 참고한다.


한글 사용하기

U8g2 라이브러리에서 한글을 사용하려면, 한글 폰트를 사용하여야 한다. 기본적으로 제공되는 한글 폰트는 2가지가 있다.

  • u8g2_font_unifont_t_korean1 : 사용가능한 글자가 제한되며, 용량이 17kB 정도

  • u8g2_font_unifont_t_korean2 : korean1폰트보다 사용가능한 글자수가 많으나, 용량이 70kB 정도

    1
    u8g2.setFont(u8g2_font_unifont_t_korean2);
  • 한글 사용을 위해 void setup()에 아래의 코드를 추가하여야 한다.

    1
    u8g2.enableUTF8Print();   // enable UTF8 support for the Arduino print() function

sketch: 한글 사용하기
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <Arduino.h>
#include <U8g2lib.h>

// for SPI
#include <SPI.h>

// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SW_SPI
//U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// for SH1106, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup(void)
{
u8g2.begin();
u8g2.enableUTF8Print();
}

void loop(void)
{
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0, 15, "SKS STEAM!");
u8g2.drawTriangle(27,50, 64,32, 100,50);
} while ( u8g2.nextPage() );
delay(1000);

u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_unifont_t_korean1);
u8g2.setFont(u8g2_font_unifont_t_korean2);
u8g2.setFontDirection(0);
u8g2.clearBuffer();
u8g2.setCursor(0, 16);
u8g2.print("Hello!");
u8g2.setCursor(0, 40);
u8g2.print("안녕하세요.");
u8g2.sendBuffer();
} while ( u8g2.nextPage() );
delay(1000);
}

나눔고딕폰트 사용하기

  • (한글의 모든 글자를 사용해야 할 경우) U8g2 라이브러리에서 사용할 나눔고딕 폰트를 다운로드 (created by xyz37) 한다.

    u8g2_font_unifont_t_korean_NanumGothicCoding_16.h

    주의! 폰트 크기가 1.09MB이므로 메모리가 작은 아두이노에서는 위 폰트를 사용하기 어렵지만, ESP32에서 사용할 수 있다. U8g2에서 제공하는 폰트인 korean1이 17k, korean2는 70k이므로 아두이노를 사용해야하는 경우는, (모든 한글 글자를 표현할 수 없더라도) korean1이나 korean2 폰트를 사용해야 한다.


  • 다운로드 받은 나눔고딕 폰트 파일을 **”u8g2 라이브러리 폴더”**에 넣는다.


  • u8g2 라이브러리 폴더의 위치를 모를 경우,

    아래 그림과 같이 아두이노 IDE에서 파일> 환경설정 에서 스케치북 위치를 확인한 후,

(스케치북 위치)/libraries/U8g2/src/ 폴더를 찾아, 폰트 파일을 넣는다.


  • 위 폰트를 사용하려면 스케치에 다음을 포함해야 한다.

    1
    #include "u8g2_font_unifont_t_korean_NanumGothicCoding_16.h"
    1
    2
    3
    4
    void loop() {
    u8g2.setFont(u8g2_font_unifont_t_korean_NanumGothicCoding_16);

    }

sketch
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <Arduino.h>
#include <U8g2lib.h>

// for SPI
#include <SPI.h>

// for using Korean NanumGothicCoding font
#include "u8g2_font_unifont_t_korean_NanumGothicCoding_16.h"

// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SW_SPI
//U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// for SH1106, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup(void)
{
u8g2.begin();
u8g2.enableUTF8Print();
}

void loop(void)
{
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0, 15, "SKS STEAM!");
u8g2.drawTriangle(27,50, 64,32, 100,50);
} while ( u8g2.nextPage() );
delay(1000);

u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_unifont_t_korean1);
//u8g2.setFont(u8g2_font_unifont_t_korean2);
u8g2.setFont(u8g2_font_unifont_t_korean_NanumGothicCoding_16);
u8g2.setFontDirection(0);
u8g2.clearBuffer();
u8g2.setCursor(0, 16);
u8g2.print("Hello!");
u8g2.setCursor(0, 40);
u8g2.print("안녕하세요.");
u8g2.sendBuffer();
} while ( u8g2.nextPage() );
delay(1000);
}


비트맵 그림/사진 출력하기

OLED의 해상도(128×64)에 맞추어 그림이나 로고를 출력해보자. 윈도우 기준으로 설명하며, Mac의 경우 GIMP에서 비슷한 작업을 수행할 수 있다.


사진 or 로고 그림파일 준비

사진 픽셀수 조정

우선 oled에 출력하려는 작은 사진이나 로고 파일(.jpg or .png 등)을 준비한 뒤, 사용하는 oled의 해상도(예를들어 128×64) 고려하여 사진의 픽셀 수를 줄여야 한다. (※주의할 점: 화면상에 보이는 크기를 축소하는 것이 아니라 실제 픽셀수를 조정해야 함!)


  • 윈도우에서 그림판을 연다. (그림판3D 아님!) : 시작> Windows 보조프로그램> 그림판


  • 그림판 하단에 불러온 그림의 실제 픽셀수가 나타납니다. (픽셀수 오른쪽의 % 수치는 화면상에서 보이는 확대/축소 비율이므로, 여기서는 신경쓰지 않는다.)


  • Ctrl-A를 눌러 전체 그림을 선택하면 그림 겉테두리에 점선이 표시되는데, 이때 (파란색 동그라미 표시된) 사진 오른쪽 상단 모서리의 포인트를 마우스로 클릭하여 픽셀사이즈를 원본 그림의 비율에 맞추어 128x64 이하로 조정한다. 예시에서는 1000x1000px, 즉 1:1의 비율 그림이므로 64x64로 조정하였다. 캔버스 크기를 나타내는 1000x1000px 왼쪽에, 축소되는 그림의 픽셀수가 나타나므로, 이를 확인하면서 픽셀수를 64x64로 조정하면 된다.


  • 64x64px이 표시되어는 부분은, 마우스로 크기를 조정한 뒤에 클릭하고 있는 마우스 버튼에서 손을 떼는 순간 사라진다. (신경쓰지 않아도 됨)

  • 중요! 위 그림처럼 축소된 작은 그림(전체 캔버스가 아님!)의 테두리에 점선이 남아 있는 상태에서 Ctrl-c를 눌러, 축소된 그림만 클립보드에 복사한다.

  • 메뉴에서 파일> 새로 만들기>를 클릭한다.

    이때, 변경내용을 저장하시겠습니까? 팝업이 뜨면 저장 안 함 클릭 (저장을 해도 관계없으나, 원본파일이 사라짐)


  • 메뉴에서 파일> 속성> 을 클릭한 뒤


  • 이미지 속성에서 단위를 픽셀로 지정하고, 너비와 높이를 oled의 전체 픽셀 사이즈(128x64)로 조정한다.


  • 그림판에 128x64 크기의 캔버스가 나타난다. (화면상에 나타나는 크기가 너무 작아 확대/축소 비율을 100%로 조정함)


  • 위 상태에서 Ctrl-v를 클릭하여, 클립보드에 복사한 그림을 붙여넣기 한다.


  • 붙여넣은 그림을 마우스로 클릭하여 위치를 조정한다. 추가로 그림판의 텍스트 기능을 사용하여 글씨를 넣어보았다.


단색 1-bit 비트맵 bmp 파일로 저장하기
  • 이제 작업한 파일을 1-bit 비트맵 bmp 파일로 저장한다. 파일> 다른 이름으로 저장> BMP 그림 을 클릭한 뒤,

    )


  • 저장할 경로와 파일이름을 지정한 뒤, 파일 형식을 *단색 비트맵(.bmp; *dlb)**으로 지정하여 저장한다. (사진 품질 저하에 대한 경고를 확인하면 최종 저장 된다.)



※ Mac에서는 GIMP를 이용하여 단색 비트맵 파일로 저장할 수 있다.

링크 참고 - Creating monochrome bitmap files for LCD / GLIB using GIMP)


단색 bmp 파일을 hex파일로 변경하기

프로그램 설치

이제 아래와 같은 bitmap converter 프로그램을 사용하여, 단색 bmp 파일을 hex 파일로 변경한다. (여기서는 윈도우 기준으로 설명)


bmp → hex
  • LCD Assistant를 실행한 후,

    File> Load image> 를 클릭하여 위에서 제작한 단색 bmp 파일을 연다.


  • Byte orientation을 Horizontal로 변경한다. Size, Pixel/byte, Table name은 자동으로 지정되나, 만일 자동으로 설정되지 않는 경우, 아래와 같이 변경한다.
    • Size: OLED 픽셀수
    • Pixel/byte: 8
    • Table name: 스케치내에서 사용할 이름

  • File> Save output> 를 클릭하여 파일을 저장한다. 이때 파일 형식을 별도로 지정하지 않으며, 사용할 파일이름에 .h 또는 .txt 를 덧붙여 구분하여도 된다. 예시에서는 apeach.h 로 지정하였다.


  • 메모장이나 텍스트 에디터 등을 사용하여 apeach.h 파일을 열어보면, 단색 bmp 파일의 hex 코드를 볼 수 있다. 이제 이를 sketch에 반영하면 된다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
const unsigned char apeach [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0xF0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xC6, 0x01, 0x8C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x21, 0x01, 0x02, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x21, 0x10, 0x84, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x10, 0x84, 0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x00, 0x84, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x00, 0x85, 0xF9, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x43, 0xFF, 0x86, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x01, 0xFF, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x01, 0x55, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x01, 0xD6, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x0F, 0xF0, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x23, 0xC4, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x4C, 0x36, 0x90, 0x00, 0x24, 0x03, 0xD9, 0x9C, 0x0E, 0x07, 0xB0, 0xF3, 0x38, 0x00,
0x00, 0x00, 0xD8, 0x1A, 0x90, 0x00, 0x24, 0x07, 0xF9, 0xFE, 0x1F, 0x8F, 0xF1, 0xF3, 0x7C, 0x00,
0x00, 0x00, 0x93, 0xCB, 0xF0, 0x00, 0x28, 0x0E, 0x39, 0xC7, 0x31, 0x9C, 0x73, 0x83, 0x8C, 0x00,
0x00, 0x00, 0xA6, 0x25, 0x30, 0x00, 0x28, 0x0C, 0x19, 0x83, 0x21, 0x98, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0xA4, 0x35, 0xD0, 0x00, 0x20, 0x0C, 0x19, 0x83, 0x7F, 0x98, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0xA4, 0x96, 0x10, 0x00, 0x20, 0x0C, 0x19, 0x83, 0x3E, 0x18, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0x97, 0x94, 0x10, 0xFC, 0x20, 0x0E, 0x39, 0xC7, 0x30, 0x9C, 0x73, 0x83, 0x0C, 0x00,
0x00, 0x00, 0x90, 0x34, 0x10, 0x84, 0x20, 0x07, 0xF9, 0xFE, 0x3F, 0x8F, 0xF1, 0xF3, 0x0C, 0x00,
0x00, 0x00, 0x48, 0x24, 0x10, 0x84, 0x20, 0x03, 0xD9, 0xBC, 0x0F, 0x07, 0xB0, 0xF3, 0x0C, 0x00,
0x00, 0x00, 0x47, 0xCC, 0x11, 0x04, 0x40, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x18, 0x09, 0x02, 0x40, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1C, 0x30, 0x06, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

스케치에 이미지 hex 코드 넣기

sketch

위에서 만든 어피치로고와 함께 학교로고를 추가로 만들고, 2개의 이미지를 OLED에 3초씩 출력하는 코드이다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include <Arduino.h>
#include <U8g2lib.h>

#include <SPI.h> // for SPI

// #include "u8g2_font_unifont_t_korean_NanumGothicCoding_16.h" // for using Korean NanumGothicCoding font

// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SW_SPI
//U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// for SH1106, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

const unsigned char apeach [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0xF0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xC6, 0x01, 0x8C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x21, 0x01, 0x02, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x21, 0x10, 0x84, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x10, 0x84, 0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x00, 0x84, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x00, 0x85, 0xF9, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x43, 0xFF, 0x86, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x01, 0xFF, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x01, 0x55, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x01, 0xD6, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x0F, 0xF0, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x23, 0xC4, 0x90, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x4C, 0x36, 0x90, 0x00, 0x24, 0x03, 0xD9, 0x9C, 0x0E, 0x07, 0xB0, 0xF3, 0x38, 0x00,
0x00, 0x00, 0xD8, 0x1A, 0x90, 0x00, 0x24, 0x07, 0xF9, 0xFE, 0x1F, 0x8F, 0xF1, 0xF3, 0x7C, 0x00,
0x00, 0x00, 0x93, 0xCB, 0xF0, 0x00, 0x28, 0x0E, 0x39, 0xC7, 0x31, 0x9C, 0x73, 0x83, 0x8C, 0x00,
0x00, 0x00, 0xA6, 0x25, 0x30, 0x00, 0x28, 0x0C, 0x19, 0x83, 0x21, 0x98, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0xA4, 0x35, 0xD0, 0x00, 0x20, 0x0C, 0x19, 0x83, 0x7F, 0x98, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0xA4, 0x96, 0x10, 0x00, 0x20, 0x0C, 0x19, 0x83, 0x3E, 0x18, 0x33, 0x03, 0x0C, 0x00,
0x00, 0x00, 0x97, 0x94, 0x10, 0xFC, 0x20, 0x0E, 0x39, 0xC7, 0x30, 0x9C, 0x73, 0x83, 0x0C, 0x00,
0x00, 0x00, 0x90, 0x34, 0x10, 0x84, 0x20, 0x07, 0xF9, 0xFE, 0x3F, 0x8F, 0xF1, 0xF3, 0x0C, 0x00,
0x00, 0x00, 0x48, 0x24, 0x10, 0x84, 0x20, 0x03, 0xD9, 0xBC, 0x0F, 0x07, 0xB0, 0xF3, 0x0C, 0x00,
0x00, 0x00, 0x47, 0xCC, 0x11, 0x04, 0x40, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x18, 0x09, 0x02, 0x40, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1C, 0x30, 0x06, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char skslogo_1bit_128x64 [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x64, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x76, 0x28, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x06, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xA0, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1E, 0x00, 0x07, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x3C, 0x00, 0x07, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3C, 0x00, 0x07, 0xF8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x7C, 0x00, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0xFC, 0x00, 0x01, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x0F, 0xE0, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xF0, 0x0F, 0xC0, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xFF, 0xF8, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x8D, 0xB1, 0xF8, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xC1, 0xFF, 0xB5, 0x26, 0xFC, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x9C, 0x73, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0xC4, 0x78, 0xFE, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x41, 0xFF, 0x35, 0x26, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xFF, 0x85, 0x90, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x3F, 0xFF, 0x3F, 0xFE, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x0F, 0xFE, 0x3F, 0xFC, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE6, 0x7C, 0x03, 0xF8, 0xFF, 0xF8, 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF1, 0x7C, 0x00, 0x03, 0xFF, 0xF0, 0x1F, 0xA1, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF3, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, 0x3F, 0x37, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7B, 0xDF, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0x97, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7B, 0x67, 0x80, 0x3F, 0xFE, 0x00, 0xFA, 0xD7, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0xE7, 0xC0, 0x07, 0xF0, 0x01, 0xF9, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF8, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0x5C, 0xF8, 0x00, 0x00, 0x0F, 0xFB, 0x7C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x38, 0x7E, 0x00, 0x00, 0x1F, 0xB6, 0xBC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x00, 0xFF, 0x87, 0xB8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0xCF, 0xF8, 0x07, 0xFB, 0x8F, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xED, 0xD3, 0xFF, 0xFF, 0xE3, 0x27, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFA, 0xAB, 0xFF, 0xFF, 0x38, 0x73, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xCE, 0x3F, 0xFE, 0xB9, 0xE7, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x15, 0x30, 0xFE, 0x37, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0E, 0x31, 0xFE, 0xDE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xCD, 0x30, 0xEF, 0xDC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFB, 0xB0, 0xEF, 0x9F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0xE1, 0xD7, 0x6F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF9, 0xDA, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xBB, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void setup(void)
{
u8g2.begin();
u8g2.enableUTF8Print();
}

void loop(void)
{
u8g2.firstPage();
do {
u8g2.drawBitmap( 0, 0, 16, 64, apeach);
} while(u8g2.nextPage());
delay(3000);

u8g2.firstPage();
do {
u8g2.drawBitmap( 0, 0, 16, 64, skslogo_1bit_128x64);
} while(u8g2.nextPage());
delay(3000);
}



센서값 출력하기

DS18b20 온도센서

pinmap
OLED ESP32 (HW_SPI로 사용시) (VSPI) DS18b20 4.7kΩ
GND GND
VDD 3.3V
D0 18 (HW_SPI 사용시 GPIO_18로 고정) SCK/CLK/CLOCK
D1 23 (HW_SPI 사용시 GPIO_23으로 고정) DATA/DIN/MOSI
RES 15 RST/RESET
DC 14 Data Command
CS 5 Chip Select
GND GND (Black)
5V VCC (Red) O
13 Signal (Yellow) O

schematic


library

Sketch> Include Library> Manage Libraries>

  • OneWire (by Jim Studt etc.)
  • DallasTemperature (by Miles Burton)

sketch
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include <Arduino.h>
#include <U8g2lib.h>

// for SPI
#include <SPI.h>

// for using Korean NanumGothicCoding font
// #include "u8g2_font_unifont_t_korean_NanumGothicCoding_16.h"

// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

// for HW_SPI (VSPI)
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SW_SPI
//U8G2_SH1106_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 26, /* data=*/ 25, /* cs=*/ 5, /* dc=*/ 14, /* reset=*/ 15);
// for SSD1306, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// for SH1106, I2C, (ESP32: SDA 21, SCL 22)
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

#include <OneWire.h>
#include <DallasTemperature.h>

#define TEMP_PIN 13

OneWire oneWire(TEMP_PIN);
DallasTemperature sensors(&oneWire);

unsigned long startTime = 0;

const unsigned char skslogo_1bit_128x64 [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x64, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x76, 0x28, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x06, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xA0, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1E, 0x00, 0x07, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x3C, 0x00, 0x07, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3C, 0x00, 0x07, 0xF8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x7C, 0x00, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0xFC, 0x00, 0x01, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x0F, 0xE0, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xF0, 0x0F, 0xC0, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xFF, 0xF8, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x8D, 0xB1, 0xF8, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xC1, 0xFF, 0xB5, 0x26, 0xFC, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x9C, 0x73, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0xC4, 0x78, 0xFE, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x41, 0xFF, 0x35, 0x26, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xFF, 0x85, 0x90, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x3F, 0xFF, 0x3F, 0xFE, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x0F, 0xFE, 0x3F, 0xFC, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE6, 0x7C, 0x03, 0xF8, 0xFF, 0xF8, 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF1, 0x7C, 0x00, 0x03, 0xFF, 0xF0, 0x1F, 0xA1, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF3, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, 0x3F, 0x37, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7B, 0xDF, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0x97, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7B, 0x67, 0x80, 0x3F, 0xFE, 0x00, 0xFA, 0xD7, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0xE7, 0xC0, 0x07, 0xF0, 0x01, 0xF9, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF8, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0x5C, 0xF8, 0x00, 0x00, 0x0F, 0xFB, 0x7C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x38, 0x7E, 0x00, 0x00, 0x1F, 0xB6, 0xBC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x00, 0xFF, 0x87, 0xB8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0xCF, 0xF8, 0x07, 0xFB, 0x8F, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xED, 0xD3, 0xFF, 0xFF, 0xE3, 0x27, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFA, 0xAB, 0xFF, 0xFF, 0x38, 0x73, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xCE, 0x3F, 0xFE, 0xB9, 0xE7, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x15, 0x30, 0xFE, 0x37, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0E, 0x31, 0xFE, 0xDE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xCD, 0x30, 0xEF, 0xDC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFB, 0xB0, 0xEF, 0x9F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0xE1, 0xD7, 0x6F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF9, 0xDA, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xBB, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void setup(void)
{
u8g2.begin();
u8g2.enableUTF8Print();

u8g2.firstPage();
do {
u8g2.drawBitmap( 0, 0, 16, 64, skslogo_1bit_128x64);
} while(u8g2.nextPage());
delay(3000);

u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0, 15, "SKS STEAM!");
u8g2.drawTriangle(27,50, 64,32, 100,50);
} while ( u8g2.nextPage() );
delay(3000);

startTime = millis();

// Start up the DS18b20 library
sensors.begin();
}

void loop(void)
{
unsigned long ttime = millis();

sensors.requestTemperatures();
float temperatureC = sensors.getTempCByIndex(0);
float temperatureF = sensors.getTempFByIndex(0);

u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_unifont_t_korean1);
u8g2.setFont(u8g2_font_unifont_t_korean2);
//u8g2.setFont(u8g2_font_unifont_t_korean_NanumGothicCoding_16);

// Title
u8g2.setCursor(8, 14);
u8g2.print("[[ 온도측정 ]]");

// actual time measured. (1초 마다 출력되지는 않음)
u8g2.setCursor(0, 31);
u8g2.print("시간: ");
u8g2.setCursor(63, 31);
u8g2.print(getTime(ttime));

// Celcius temperature
u8g2.setFont(u8g2_font_unifont_t_korean2);
u8g2.setCursor(0, 47);
u8g2.print("섭씨: ");
u8g2.setFont(u8g2_font_unifont_tf);
if(temperatureC<100) {
u8g2.setCursor(71, 47);
}
else {
u8g2.setCursor(63, 47);
}
u8g2.print(temperatureC, 2);
u8g2.drawGlyph(111, 47, 0x00b0); // degree
u8g2.drawStr(119, 47, "C");

// Fahrenheit temperature
u8g2.setFont(u8g2_font_unifont_t_korean2);
u8g2.setCursor(0, 63);
u8g2.print("화씨: ");
u8g2.setFont(u8g2_font_unifont_tf);
if(temperatureF<100) {
u8g2.setCursor(71, 63);
}
else {
u8g2.setCursor(63, 63);
}
u8g2.print(temperatureF, 2);
u8g2.drawGlyph(111, 63, 0x00b0); // degree
u8g2.drawStr(119, 63, "F");

} while ( u8g2.nextPage() );
delay(1000);
}

String getTime(unsigned long ttime) {
int sec = ttime / 1000; int min = sec / 60; int hr = min / 60;
String ts = "";

// 시간
if (hr < 10) ts += "0";
ts += hr;
ts += ":";

// 분
if ((min % 60) < 10) ts += "0";
ts += min % 60;
ts += ":";

// 초
if ((sec % 60) < 10) ts += "0";
ts += sec % 60;

return (ts);
}

result



UTF8_HanDraw 라이브러리 한글 출력하기

ESP32 SPIFFS 영역에 폰트 업로드

  • SPIFFS란?

    • **SPIFFS(Serial Peripheral Interface Flash File System)**는 ESP32와 ESP8266의 모듈에 내부 탑재 되어 있는 메모리를 저장공간으로 사용하기 위한 파일 시스템이다.
    • ESP8266과 ESP32에는 보통 4MByte의 메모리가 탑재되어 있으며 이 메모리의 일부를 플래쉬 메모리로 사용하고 나머지를 파일 저장공간으로 사용할 수가 있다.
    • 작은 용량의 SD카드가 내장이 되었다고 생각하면 이해하기 쉽다.
  • 아두이노에서의 SPIFFS의 이용

    • 아두이노 IDE에는 이 메모리의 분리를 설정할 수 있는 메뉴가 있다. 아두이노IDE에서 아래의 항목을 설정하는 것 만으로 스케치를 업로드하면 영역이 분할되어 스케치가 업로드 된다.

      > Partition Scheme> Default 4MB with spiffs (1.2MB APPS / 1.5MB SPIFFS)

    • 기본설정은 위와 같지만 SPIFFS 용량이 부족하다면, **위 메뉴에서 **

      **No OTA(1MB APP/3MB SPIFFS)**와 같은 다른 설정을 선택하여, 코딩이 들어가는 플래쉬 메모리를 1MByte를 사용하고, 나머지 3MByte의 SPIFFS영역에는 코딩에서 필요한 자료(예를 들면 그림파일이나 텍스트 파일등)을 업로드 하고 코딩에서 그 파일을 사용하면 됩니다.

    • 아두이노 우노의 플래쉬메모리가 32KByte인 것을 고려하면 ESP32에 플래쉬 메모리를 1MByte만 할당 하는 것도 적지 않은 할당이지만, ESP32의 고급기능(WIFI, 블루투스)등을 사용하면 이 메모리도 모자라게 되는 경우도 있다.


  • 아두이노 IDE에 SPIFFS 설정하기

    • 아두이노 IDE에서 ESP32의 SPIFFS영역에 파일을 올리기 위해서는 별도의 툴을 설치하여야 한다. 아래의 링크를 클릭하여 ESP32FS-1.0.zip 파일을 다운로드 받은 뒤,

      https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/

    • 압축을 풀어 나오는 ESP32FS폴더를 아두이노 설치 폴더 내의 tools폴더 아래에 복사한다.

      예를들면, c:\Program Files (x86)\Arduino\tools\ESP32FS\tool\esp32fs.jar

      같은 형태로 파일 경로가 만들어져야 한다.

    • 아두이노IDE를 종료하였다가 다시 실행시키면

      > ESP32 Sketch Data Upload 항목이 만들어진다.


  • SPIFFS에 (폰트) 파일 업로드하기
    • 기본적으로 ESP32의 SPIFFS에 파일을 업로드 하기 위해서는, 업로드하려는 스케치(.ino) 파일이 위치한 폴더에 data폴더를 생성한 뒤, 업로드하려는 파일을 복사해 넣으면 된다.
    • 그러므로 이번 과정에서 SPIFFS에 (폰트)파일을 업로드하려면, (폰트) 파일을 업로드하려는 스케치(UTF8_HanDraw.ino)가 저장되어 있는 폴더 안에, (UTF8_HanDraw.zip 파일의 압축을 풀었을때 만들어진) data폴더를 복사해 넣으면 된다.
    • 아두이노 IDE에 스케치를 열고, 메뉴에 추가된 ESP32 Sketch Data Upload를 클릭하면, 스케치 파일은 컴파일 후 APP영역에 저장되고, Data폴더안의 (폰트) 파일은 SPIFFS영역에 분리되어 저장된다.

schematic

OLED (I2C) ESP32 if, Arduino UNO
GND GND GND
VCC 3V3 3V3
SCL 22 A5
SDA 21 A4


sketch

출처: [OLED에 한글을 출력하자 by Andy Kim (andy-power.blogspot.com)](Andy Kim: OLED에 한글을 출력하자. (andy-power.blogspot.com))

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <Arduino.h>
#include "Display_SSD1306.h"
#include "HanDraw.h"

#define OLED_RESET -1 // for S/W 리셋
Display_SSD1306 Oled(OLED_RESET);

/************************* Global variables *********************************/
unsigned long startTime = 0;
uint32_t loopcnt = 0;
char fpsbuf[128] = "FPS:";
bool invert = true; // 화면을 역상으로 표시

void setup() {
Serial.begin(115200);
delay(50);

// Oled를 I2C 방식으로 연결하고, 그 주소는 0x3c
Oled.begin(SSD1306_SWITCHCAPVCC, 0x3C, false);

// Callback 함수를 설정해 주면 필요시 호출 하여 사용함.
HanDraw.begin(12,
// 화면 지우는 콜백함수
[](void) { Oled.clearDisplay(); },
// 1개 픽셀을 그리는 콜백 함수
[](int16_t x, int16_t y, uint16_t color) { Oled.drawPixel(x, y, color); },
// 메모리에서 Display 표시 버퍼까지 보내는 함수
[](void) { Oled.display(); }
);

HanDraw.display(); // 사실상 Oled.display() 와 동일....
delay(2000);

HanDraw.clear();
HanDraw.setFontSize(12);
// 특수한 문자 몇개는... 아래와 같이 주면 출력된다. (통용되는 코드가 아니라 변칙 코드임)
// 12px:한글 21℃℉‰μ° 라고 출력
HanDraw.drawString(1, 0, "12px:한글 21\x10\x0f\x11\x12\x13");
HanDraw.setFontSize(14);
// 14:아래첨자 A₁₂₃₄ 라고 출력
HanDraw.drawString(1, 13, "14:아래첨자 A\x0b\x0c\x0d\x0e");
HanDraw.setFontSize(16);
// 16:위첨자 M¹²³⁴ 라고 출력
HanDraw.drawString(1, 28, "16:위첨자 M\x15\x16\x17\x18");
HanDraw.display();
delay(3000);

HanDraw.setFontSize(12);
startTime = millis();
delay(1);
Serial.println("Setup All done");
}

String getTime(unsigned long ttime) {
int sec = ttime / 1000; int min = sec / 60; int hr = min / 60;
String ts = "";
if (hr < 10) ts += "0";
ts += hr; ts += ":";
if ((min % 60) < 10) ts += "0";
ts += min % 60; ts += ":";
if ((sec % 60) < 10) ts += "0";
ts += sec % 60;
return (ts);
}

void loop() {
unsigned long ttime = millis();
dtostrf(loopcnt * 1000.0 / (ttime - startTime), 5, 2, fpsbuf + 4);

HanDraw.clear();
HanDraw.drawString(15, 2, "[[ 화면 정보 ]]");
HanDraw.drawString(2, 14, "--------------------");
HanDraw.drawString(2, 24, "한글 출력 테스트");
HanDraw.drawString(2, 38, fpsbuf);
HanDraw.drawString(2, 51, getTime(ttime));
HanDraw.display();

loopcnt++;
if (loopcnt % 100 == 0) {
Oled.invertDisplay(invert);
invert = !invert;
}
}


OLED에 DS18b20 온도 출력하기

schematic

OLED (I2C) ESP32 DS18b20 4.7kΩ
GND GND BLK
VCC 3V3
SCL 33
SDA 32
5V RED O
GPIO13 YELLOW O

library

Sketch> Include Library> Manage Libraries>

  • OneWire (by Jim Studt etc.)
  • DallasTemperature (by Miles Burton)

sketch

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <Arduino.h>

#include <OneWire.h>
#include <DallasTemperature.h>

#define TEMP_PIN 13

OneWire oneWire(TEMP_PIN);
DallasTemperature sensors(&oneWire);

#include "Display_SSD1306.h"
#include "HanDraw.h"

#define OLED_RESET -1 // for S/W 리셋
Display_SSD1306 Oled(OLED_RESET);

/************************* Global variables *********************************/
unsigned long startTime = 0;
uint32_t loopcnt = 0;
char fpsbuf[128] = "FPS:";
char tC[128];
char tF[128];
bool invert = true; // 화면을 역상으로 표시


void setup() {
Serial.begin(115200);
delay(50);

// Oled를 I2C 방식으로 연결하고, 그 주소는 0x3c
Oled.begin(SSD1306_SWITCHCAPVCC, 0x3C, false);

// Callback 함수를 설정해 주면 필요시 호출 하여 사용함.
HanDraw.begin(12,
// 화면 지우는 콜백함수
[](void) { Oled.clearDisplay(); },
// 1개 픽셀을 그리는 콜백 함수
[](int16_t x, int16_t y, uint16_t color) { Oled.drawPixel(x, y, color); },
// 메모리에서 Display 표시 버퍼까지 보내는 함수
[](void) { Oled.display(); }
);

startTime = millis();
delay(1);

// Start up the DS18b20 library
sensors.begin();
}

void loop() {
unsigned long ttime = millis();
dtostrf(loopcnt * 1000.0 / (ttime - startTime), 5, 2, fpsbuf + 4);

sensors.requestTemperatures();
float temperatureC = sensors.getTempCByIndex(0);
float temperatureF = sensors.getTempFByIndex(0);
dtostrf(temperatureC, 6, 2, tC);
String tempC = "섭씨온도 * " + (String)tC + "\x10";
dtostrf(temperatureF, 6, 2, tF);
String tempF = "화씨온도 * " + (String)tF + "\x0f";

HanDraw.display();

HanDraw.clear();
HanDraw.setFontSize(12);
HanDraw.drawString(15, 2, "[[ 온도 측정 ]]");
HanDraw.drawString(2, 14, "--------------------");
HanDraw.drawString(2, 24, getTime(ttime));
HanDraw.drawString(2, 38, tempC);
HanDraw.drawString(2, 51, tempF);
HanDraw.display();
}

String getTime(unsigned long ttime) {
int sec = ttime / 1000; int min = sec / 60; int hr = min / 60;
//String ts = "";
String ts = "측정시간 * ";
if (hr < 10) ts += "0";
ts += hr; ts += ":";
if ((min % 60) < 10) ts += "0";
ts += min % 60; ts += ":";
if ((sec % 60) < 10) ts += "0";
ts += sec % 60;
return (ts);
}

ESP32, 스텝모터

스텝 모터, 28BYJ-48

Specification

  • 정격전압: 5VDC

  • 기어비: 1/64
    $$
    \frac {32}{9} \times \frac {22}{11} \times \frac {26}{9} \times \frac {31}{10} = 63.68395 \fallingdotseq 64
    $$

  • 스트라이드 각도
    • 스펙상 5.625º / 64 = 0.087890625
      • 그러므로, 360º / 0.087890625 = 4096 스텝
    • 실제로는 11.25º / 64 = 0.17578125
      • 그러므로, 360º / 0.17578125 = 2048 스텝으로 1회전
  • Frequency: 100Hz
  • 토크: 34.3mN,m

모터드라이브 ULN2003



스텝 모터의 구동

schematic


Pin Map

외부전원 모터드라이브 ESP32
INT1 19
INT2 18
INT3 5
INT4 17
(+) 5V GND
(-) VCC

※ 모터드라이브에 연결하는 전원(VCC)는 외부전원 5V를 사용한다. (ESP32의 5V는, 전류가 부족하여 ESP32와 연결한 노트북에 영향을 줄 수 있으므로, 사용하지 않는 것이 좋다.)


스텝업 모듈

  • 입력전압: 0.9~5V
  • 출력전압: 5V
  • 출력전류: 500~600mA
  • 최대효율: 96%

sketch

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
#include <Stepper.h>

const int stepsPerRevolution = 2048; // change this to fit the number of steps per revolution

// ULN2003 Motor Driver Pins
#define IN1 19
#define IN2 18
#define IN3 5
#define IN4 17

// initialize the stepper library
Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);

void setup() {
// set the speed at 5 rpm
myStepper.setSpeed(5);
// initialize the serial port
Serial.begin(115200);
}

void loop() {
// step one revolution in one direction:
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
delay(1000);

// step one revolution in the other direction:
Serial.println("counterclockwise");
myStepper.step(-stepsPerRevolution);
delay(1000);
}
  • setSpeed(5);

    • 외부 전원의 전력에 따라 최대값이 달라짐.

    • 5V 전원 사용시 16이 최대값인 경우가 있었고. 그 이상은 안정적으로 회전이 안되며, 17을 넣으면 한바퀴 돌고 정지. 18을 넣으면 돌지 않음.

    • 7.4V 전원 사용시 22가 최대값인 경우가 있었고, 그 이상은 안정적으로 회전이 안됨. (23을 넣으면 시계방향 회전 후, 반시계방향 회전이 안됨. 24를 넣으면 돌지 않음)

    • 전압에 따라 수치가 고정된 것은 아닌 듯 하며, 외부 전원의 상태에 따라 달라질 수 있으므로 테스트가 필요함.

ESP32, 온도측정(DS18b20), PLX_DAQ

DS18b20 온도센서

DS18b20 온도 센서를 이용하여 온도를 측정한다.

  • one-wire 버스 통신
  • 공급전압: 3.0V ~ 5.5V
  • 작동온도: -55ºC ~ +125ºC
  • 오차: +/-0.5 ºC (-10ºC ~ 85ºC 범위에서)

Pinout


schematic

DS18b20 센서는 두 가지 연결방법을 제공하는데, 하나는 VCC를 5V에 연결하는 Normal Mode이고 다른 하나는 VCC를 GND에 연결하는 Parasite Mode이다. 두가지 방법 모두 지원되지만 (경험상) Normal를 추천하며 (원인은 잘 모르겠지만) Parasite Mode에서는 온도센서가 작동이 되지 않는 경우도 가끔 있었다. 회로 구성을 위해 4.7㏀ 저항 1개가 필요하며 Normal Mode 구성을 위해 다음 그림과 같이 연결한다.

  • 3가닥의 선에 브레드보드에 연결할 수 있는 핀을 납땜하고, 노란색선과 빨간색선의 연결부위에 4.7㏀ 저항을 추가로 납땜하여 연결한다.

  • 수축튜브를 사용하여 연결부위를 절연한 뒤,

  • 구경이 조금 더 큰 수축튜브를 사용하여 3가닥을 하나로 감싸 마무리한다.


Normal Mode



라이브러리 준비하기

Sketch> Include Library> Manage Libraries>

  • OneWire (by Jim Studt etc.)
  • DallasTemperature (by Miles Burton)

sketch

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
#include <OneWire.h>
#include <DallasTemperature.h>

#define TEMP_PIN 4

OneWire oneWire(TEMP_PIN);
DallasTemperature sensors(&oneWire);

void setup() {
Serial.begin(115200);
Serial.println("DallasTemperature IC Control");

// Start up the library
sensors.begin();
}

void loop() {
sensors.requestTemperatures();
float temperatureC = sensors.getTempCByIndex(0);
float temperatureF = sensors.getTempFByIndex(0);
Serial.print(temperatureC);
Serial.println("*C");
Serial.print(temperatureF);
Serial.println("*F");
delay(2000);
}


PLX-DAQ

ESP32를 사용하여 센서데이터를 Excel로 보내고 실시간 그래프를 그려주는 Excel 매크로 파일이다. Parallax에서 만든 것이지만, 현재 공식적인 업데이트는 중지되어 있다. 이런 이유로 Parallax 홈페이지에서 다운로드 받을 수 있는 PLX-DAQ는 최신 버전의 Excel에서 활용이 불가능하며, ESP32에서의 활용도 불가능하다. 현재는 아두이노 포럼 등에서 최신 버전의 Excel과 ESP32 등에서 활용 가능하도록 업데이트를 진행하고 있으므로 Parallax 홈페이지가 아닌, 아두이노 포럼에서 다운로드 받도록 한다.


download

PLX-DAQ version 2 - now with 64 bit support! (and further new features) - Using Arduino / Interfacing w/ Software on the Computer - Arduino Forum)


sketch

  • void setup()

    • Excel Sheet Clear
    1
    Serial.println("CLEARDATA");
    • Label 만들기 (콤마(,)로 구분)
    1
    Serial.println("LABEL,Date,Time,Millis,SensorValue");
  • void loop()

    • Data 출력
    1
    2
    Serial.println( (String) "DATA,DATE,TIME," + millis() + "," + SensorValue );
    delay(200);

    (혹은, 아래와 같이 라인을 구분하는 것도 좋은 방법이다.

    1
    2
    3
    4
    5
    6
    Serial.print("DATA,DATE,TIME,");
    Serial.print(millis());
    Serial.print(",");
    Serial.print(i);
    Serial.print(",");
    Serial.println(SensorValue);
    • DATA 출력시작은 Serial.print(“DATA,”); 로 하며, DATA 출력종료는 println을 통해 개행한다.
    • delay를 500이상 주어야 안정적으로 데이터를 수신한다.
  • 전체 sketch

    GPIO4 데이터를 엑셀 파일에 기록한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int i = 0;
    const int SensorPin = 4;
    float SensorValue;

    void setup() {
    Serial.begin(115200);
    Serial.println("CLEARDATA");
    Serial.println("LABEL,Date,Time,Millis,Times,SensorValue");
    }

    void loop() {
    SensorValue = analogRead(SensorPin)/100.00;
    Serial.print("DATA,DATE,TIME,");
    Serial.print(millis());
    Serial.print(",");
    Serial.print(i);
    Serial.print(",");
    Serial.println(SensorValue);
    i++;
    delay(5000); // 5초마다 1번씩 측정
    }


온도센서의 온도값 기록하기

sketch

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
32
33
34
35
#include <OneWire.h>
#include <DallasTemperature.h>

int i = 0;
const int SensorPin = 4;
float temperatureC;
//float temperatureF;

OneWire oneWire(SensorPin);
DallasTemperature sensors(&oneWire);

void setup() {
Serial.begin(115200);
Serial.println("DallasTemperature IC Control");

// Start up the library
sensors.begin();
Serial.println("CLEARDATA");
Serial.println("LABEL,Date,Time,Millis,Times,SensorValue");
}

void loop() {
sensors.requestTemperatures();
temperatureC = sensors.getTempCByIndex(0);
// temperatureF = sensors.getTempFByIndex(0);

Serial.print("DATA,DATE,TIME,");
Serial.print(millis());
Serial.print(",");
Serial.print(i);
Serial.print(",");
Serial.println(temperatureC);
i++;
delay(5000); // 5초마다 1번씩 측정
}

PLX-DAQ 엑셀파일 실행

  • 다운로드 받은 PLX-DAQ 엑셀 매크로 파일을 더블 클릭하여 불러들인다.

  • Settings에서 ESP32가 연결된 Com port와 Baud rate 설정

    • Port: 6 (장치 관리자> 포트> 에서 확인)
    • Baud: 115200 (ESP32의 경우 115200, 아두이노는 9600으로 셋팅)
  • 스케치를 업로드 한 뒤, Connect를 누르면 실시간으로 데이터를 받아서 기록함

  • 기본적으로 셋팅되어 있는 차트가 실시간으로 그려진다. 단, 차트가 그려지지 않거나 잘못된 형태로 표현되는 경우, 엑셀 차트의 데이터 범위를 재설정하여야 한다.



Microsoft Data Streamer

PLX-DAQ와 비슷한 역할을 하는 Microsoft Data Streamer가 있으나, 현재 아두이노만 지원하며 ESP32에서는 데이터를 전송받지 못하는 문제가 있음.