'pwm'에 해당되는 글 10건

  1. 2020.08.14 Hardware | ZE08-CH2O Formaldehyde 센서 사용해보기 4
  2. 2020.04.21 Hardware | ESP32 NTP Server 이용한 시간 맞추기
  3. 2020.04.18 Hardware | ESP32 Deep sleep 알아보기
  4. 2020.04.16 Hardware | ESP32 Cryptographic HW 가속 확인해 보기 2
  5. 2020.04.11 Hardware | EPS32 PWM 기능 확인해 보기
  6. 2020.04.09 Hardware | ESP32 의 internal sensor 확인해 보기 2
  7. 2020.03.18 Hardware | CO2 센서인 MH-Z14A 를 활용해 보자 2
  8. 2018.12.03 Hardware | 8x8 LED matrix 와 Colorduino 이용해 보기
  9. 2018.05.16 Hardware | ATtiny85 를 사용해 보자 - 2
  10. 2018.05.16 Hardware | ATtiny85 를 사용해 보자 - 1 2

Hardware | ZE08-CH2O Formaldehyde 센서 사용해보기

|

1. 환경 호르몬


사람은 호르몬으로 살아간다고 해도 과언이 아닙니다.

컨디션, 감정, 치유, 성장, 성향, 행동 등, 인간의 몸안에서 일어나는 거의 모든 화학작용과 관련이 되어 있으며, 몸을 컨트롤 합니다.


다만 아쉽게도, 현대 사회로 진입하면서 생활은 편해졌지만, 화학 물질 등으로 인하여 몸 안의 호르몬들이 교란을 잃으키고 있습니다.

더 무서운 것은 이 "환경 호르몬" 은 거의 모든 곳에 도사리고 있다는 것이지오.

특히 환경 호르몬 중에서는 "포름알데히드" 가 그 주범 물질 중 하나 입니다.


* Formaldehyde

https://en.wikipedia.org/wiki/Formaldehyde



* 포름알데하이드

https://namu.wiki/w/%ED%8F%AC%EB%A6%84%EC%95%8C%EB%8D%B0%ED%95%98%EC%9D%B4%EB%93%9C


메탄올을 잘못 마셨을 때, 실명이나 사망을 일으키는 것도 이 포름알데히드 때문이다. 메탄올이 신체 내부로 유입되면 간에서 포름알데히드 및 포름산이라는 물질로 변환되는데, 특히 포름알데히드는 시신경을 손상시키고 단백질 조직을 변성시켜 굳혀버리는 효과를 갖고 있기 때문에 이런 위험한 상황이 발생하게 되는 것이다.


가구, 특히 MDF를 사용한 가구에서는 본드와 페인트에 의해 포름알데히드가 공기 중으로 방출된다. 소위 새집증후군, 아토피의 원인으로 지목되고 있으며, 새 가구를 샀을 때 매캐한 냄새, 눈이나 목의 따가움을 느꼈다면 이것 때문이다. 포름알데히드는 성인은 물론 특히 어린이에게 매우 유해하기 때문에 실내가구의 방출량은 각국에서 규제하고 있다. 다만 포름알데히드 측정에 대한 국제 표준이 없기 때문에 국가별로 측정방법 및 규정이 다른 상태다.


다이X 같은 곳에 가면, 온갖 화학물질이 공기 중에 떠다니는 것을 대번에 느낄 수 있습니다. 이게 환경호르몬 = 포름알데히드 입니다.


값싸게 제품을 만들다 보니, 출처가 불분명한 재료와, 후처리 되지 않은 채로 공장에서 나와 유통되기 때문이죠.

특히 중국산 물건에서 많이 느낄 수 있습니다. 손이 쥐여지는 것을 입으로 쉽게 가져가는 애기들을 생각하면 소름 돋는 장소라고 생각합니다.




2. 포름알데히드 센서


그럼, 포름알데히드를 측정할 수 있는 센서는 없을까, 하고 찾아 봤습니다. 있네요.

구매를 작년 9월쯤 했을 때에는 위의 가격이었는데, 요즘은 조금 저렴해 졌습니다.


* Formaldehyde sensor ZE08-CH2O serial output formaldehyde concentration measurement with cable

https://ko.aliexpress.com/item/32842350486.html



이 센서에 사용된 "ZE08-CH2O" 는 그리 널리 사용되지 않지만, 아두이노 센서 breakout 보드를 생산하는 DFRobot - Gravity 회사에서도 출시 했을 만큼 완전 무명도 아닙니다. (이 글의 후반부에서 그림과 함께 조금 설명 해놨습니다.)


* ZE08-CH2O formaldehyde gas sensor module

https://www.winsen-sensor.com/sensors/ch2o-gas-sensor/ze08-ch2o.html

ZE08-CH2O_V1.0.pdf




3. 도착


도착샷은 예의 입니다.



센서는 PCB 보드 윗쪽에 얹혀져 있습니다.



Breakout 보드 밑부분은 센서값 처리를 위한 회로 및 IC chip 들로 빼빽하게 차 있습니다.

센서 종류로 14 USD 나 하는, 비싼 값을 하는 이유가 있네요.





4. Specification


메인 chip 에 붙어 있는 IC20 이라는 스티커를 제거하면, chip 명칭을 알 수 있습니다.



사진으로는 흐릿하게 나와서, 제품 설명 그림을 가져와 봤습니다.

STMicroelectronics 사의 32-bit ARM Cortex-M 프로세서라는 것을 알 수 있습니다.


stm32f030f4.pdf



STM32F030F4 네요. 아래 장표를 보면 32-bit ARM Cortex-M 에서 Mainstream 에 해당하는 chip 입니다.



동일 계열의 칩 중에서는 USB 인터페이스가 생략되어 있고, 메모리가 가장 적은 버전이군요.



ZE08-CH2O 의 인터페이스는 다음과 같습니다.



인터페이스 선들을 살펴 보면, 신호를 받는 방식이 PWM, UART, 그리고 DAC 세 가지 임을 알 수 있습니다.

아래는 default 연결인 Active Upload type 방식이라고 하는데, 기본적으로 TX/RX 를 사용하는 UART 방식을 표시합니다.





5. Arduino 용 Active Upload 소스


Default 방식인 Active Upload (UART) 소스 입니다.


* Serial Communication CH2O sensor
    - https://forum.arduino.cc/index.php?topic=547952.0


Sketch 는 다음과 같습니다.


#include "arduino.h"
#include "SoftwareSerial.h"

#define MAXLENGTH 9
#define VREF 5.0 // voltage on AREF pin

long tenMinutes = 10 * 60 * 1000L; // on time of heater
SoftwareSerial mySerial(10, 11);

byte receivedCommandStack[MAXLENGTH];
byte checkSum(byte array[], byte length);
boolean receivedFlag;

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

void loop() {
	ze08_PPM();
}

byte checkSum(byte array[], byte length) {
	byte sum = 0;
	for (int i = 1; i < length - 1; i ++) {
		sum += array[i];
	}
	sum = (~sum) + 1;
	return sum;
}

boolean available1() { //new data was recevied
	while (mySerial.available()) {
		for (byte index = 0; index < MAXLENGTH - 1; index++) {
			receivedCommandStack[index] = receivedCommandStack[index + 1];
		}
		receivedCommandStack[MAXLENGTH - 1] = mySerial.read();
		
		byte sumNum = checkSum(receivedCommandStack, MAXLENGTH);
		if ( (receivedCommandStack[0] == 0xFF) && (receivedCommandStack[1] == 0x17) && (receivedCommandStack[2] == 0x04) && (receivedCommandStack[MAXLENGTH - 1] == sumNum) ) { //head bit and sum are all right
			receivedFlag = 1; //new data received
			return receivedFlag;
		} else {
			receivedFlag = 0; //data loss or error
			return receivedFlag;
		}
	}
	return receivedFlag;
}

float ze08_PPM() {
	if (available1() == 1) {
		receivedFlag = 0;
		
		float ppb = (unsigned int) (receivedCommandStack[4] * 256) + receivedCommandStack[5]; // bit 4: ppm high 8-bit; bit 5: ppm low 8-bit
		float ppm = ppb / 1000; // 1ppb = 1000ppm
		delay (1000);
		Serial.print("Formalin ppm == ");
		Serial.println(ppm);
		return ppm;
	}
}

float analogReadPPM() {
	float analogVoltage = analogRead(A0) / 1024.0 * VREF;
	float ppm = 3.125 * analogVoltage - 1.25; //linear relationship (0.4V for 0 ppm and 2V for 5ppm)
	
	if( ppm < 0 ) {
		ppm = 0;
	} else if( ppm > 5 ) {
		ppm = 5;
	}
	delay (1000);
	return ppm;
}


Arduino 와의 Pin 연결은 다음과 같습니다.


 ZE08-CH2O | Arduino Nano
--------------------------
   6 (TX)  |      D10
   5 (RX)  |      D11
--------------------------
           |     POWER
--------------------------
    VCC    |      3.3V
    GND    |      GND
--------------------------


회로 diagram 도 그려 봤습니다.


실재로 연결할 선들은 CO2 센서인 MH-Z14A 와 갯수와 크기가 동일하여, 만들어 놨던 선을 사용 했습니다.


* Hardware | CO2 센서인 MH-Z14A 를 활용해 보자

https://chocoball.tistory.com/entry/Hardware-CO2-sensor-MH-Z14A


사용하지 않는 선들은 빵판 고정용으로 사용. :-)



아래와 같이 값이 표시됩니다. Calibration 이 적용되지 않아서, 이 값이 정확한 것인지는 모르겠습니다.

센서 주변 공기가 바뀌면, 그에 따라서 센서값도 달라집니다.





6. DFRobot 용 소스 - DAC


UARTDAC 를 스위치 하나로 변경하면서 사용할 수 있는 breakout 보드를 DFRobot 에서 출시한 제품도 있습니다.


* Gravity: Formaldehyde (HCHO) Sensor
    - https://www.dfrobot.com/product-1574.html



깔끔하게 잘 만들었네요. 저는 비싸서 구입하지 않았습니다.



Breakout 보드도 Pin 별로 이미 구분되어 있어서, 조금 아는 사람이면 굳이 DFRobot 제품을 구매할 필요는 없을 듯 하다.


DFRobot / DFRobotHCHOSensor - A library for DFRobot Gravity HCHO Sensor, Arduino Compatible.

https://github.com/DFRobot/DFRobotHCHOSensor

DFRobotHCHOSensor.zip


* Gravity HCHO WiKi
    - https://wiki.dfrobot.com/Gravity__HCHO_Sensor_SKU__SEN0231


DFRobot 도 동일한 센서를 사용했으므로, DAC 소스를 가져다 사용해 봅시다.


/***************************************************
 DFRobot Gravity: HCHO Sensor
 "https://www.dfrobot.com/wiki/index.php/Gravity:_HCHO_Sensor_SKU:_SEN0231"

 ***************************************************
 This example reads the concentration of HCHO in air by DAC mode.

 Created 2016-12-15
 By Jason "jason.ling@dfrobot.com@dfrobot.com"

 GNU Lesser General Public License.
 See "http://www.gnu.org/licenses/" for details.
 All above must be included in any redistribution
 ****************************************************/

 /***********Notice and Trouble shooting***************
 1. This code is tested on Arduino Uno with Arduino IDE 1.0.5 r2.
 2. In order to protect the sensor, do not touch the white sensor film on the sensor module,
 and high concentration of Hydrogen sulfide, hydrogen, methanol, ethanol, carbon monoxide should be avoided.
 3. Please do not use the modules in systems which related to human being’s safety.
 ****************************************************/

#define SensorAnalogPin A2 // this pin read the analog voltage from the HCHO sensor
#define VREF 5.0 // voltage on AREF pin

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

void loop() {
	Serial.print(analogReadPPM());
	Serial.println("ppm");
	delay(1000);
}

float analogReadPPM() {
	float analogVoltage = analogRead(SensorAnalogPin) / 1024.0 * VREF;
	float ppm = 3.125 * analogVoltage - 1.25;    //linear relationship(0.4V for 0 ppm and 2V for 5ppm)
	
	if( ppm < 0) {
		ppm = 0;
	} else if( ppm > 5) {
		ppm = 5;
	}
	return ppm;
}


Arduino 와 연결되는 Pin 정보는 다음과 같습니다.


 ZE08-CH2O | Arduino Nano
--------------------------
  2 (DAC)  |      A2
--------------------------
           |     POWER
--------------------------
    VCC    |      3.3V
    GND    |      GND
--------------------------


Layout 구성도 입니다.



실재로 구현한 사진 :-)



Default 연결 방식인 Active Upload 방식과는 값의 차이가 많이 날 뿐더러, 일관적인 값을 보여주지 않습니다.





7. DFRobot 용 소스 - UART


이번에는 DFRobot 에서 나온 UART 방식의 소스를 이용해 봅니다.


/***************************************************
 DFRobot Gravity: HCHO Sensor
 "https://www.dfrobot.com/wiki/index.php/Gravity:_HCHO_Sensor_SKU:_SEN0231"

 ***************************************************
 This example reads the concentration of HCHO in air by UART mode.

 Created 2016-12-15
 By Jason "jason.ling@dfrobot.com@dfrobot.com"

 GNU Lesser General Public License.
 See "http://www.gnu.org/licenses/" for details.
 All above must be included in any redistribution
 ****************************************************/

 /***********Notice and Trouble shooting***************
 1. This code is tested on Arduino Uno with Arduino IDE 1.0.5 r2.
 2. In order to protect the sensor, do not touch the white sensor film on the sensor module,
 and high concentration of Hydrogen sulfide, hydrogen, methanol, ethanol, carbon monoxide should be avoided.
 3. Please do not use the modules in systems which related to human being’s safety.
 ****************************************************/

#include "DFRobotHCHOSensor.h"
#include "SoftwareSerial.h"

#define SensorSerialPin 10 // this pin read the uart signal from the HCHO sensor

SoftwareSerial sensorSerial(SensorSerialPin, SensorSerialPin);
DFRobotHCHOSensor hchoSensor(&sensorSerial);

void setup() {
	sensorSerial.begin(9600); // the baudrate of HCHO is 9600
	sensorSerial.listen();
	Serial.begin(115200);
}

void loop() {
	if(hchoSensor.available() > 0) {
		Serial.print(hchoSensor.uartReadPPM());
		Serial.println("ppm");
	}
}


Arduino 와의 Pin 연결은 다음과 같습니다.


 ZE08-CH2O | Arduino Nano
--------------------------
   6 (TX)  |      D10
--------------------------
           |     POWER
--------------------------
    VCC    |      3.3V
    GND    |      GND
--------------------------


Layout 그림도 그려 봤습니다.



실재 구성 모습 입니다.



처음 시도한 UART 소스의 결과값과 거의 동일하게 나옵니다.
DFRobot 라이브러리에서 거의 모든 처리가 이루어 지지만, 처음 시도한 UART 방식과 동일한 듯 합니다.




8. ESP8266 용 소스


인터넷 바다를 떠돌다가, ESP8266 을 이용한 소스를 발견하게 됩니다.


* rsalinas/ze08-ch2o-arduino

https://github.com/rsalinas/ze08-ch2o-arduino


Remember that this sensor requires 5V in Vcc but does NOT tolerate 5V in its RX input. If you just want to use the default, active mode, you don't even need to connect this pin, so you can connect directly 5V, GND and Arduino's RX.


ZE08-CH2O.zip


궁극적으로는 WiFi > internet 을 통하여 sensor data 를 올리고, 모니터링 방식이 좋으므로, 잘 되었습니다.

ESP8266 에서 돌아가는 소스라면, WiFi 연결 코드만 추가하면 추가로 arduino 필요 없이 바로 구현이 가능 하겠습니다.


지금까지는, arduino 와 sensor 를 연결하고, ESP8266 은 오로지 WiFi 용으로만 사용하는 구성이었습니다.

ESP8266 에서 sensor 값 감지와 WiFi 가 동시에 되면, arduino 를 사용할 필요가 없어 효율이 좋겠네요.


ZIP 파일을 그대로 Library 에 추가 합니다.


Sketch > Include Library



Libraries 폴더에 보면, 새롭게 올라가 있는 것을 확인할 수 있습니다.



소스를 보면 Basic 하나만 등록되어 있습니다.


File > Examples > ze08-ch2o > Basic



Sketch 는 다음과 같습니다. SoftwareSerial 에서 조금 손을 봤습니다.


#include "ze08_ch2o.h"
#include "SoftwareSerial.h"

// Instantiate a serial port, whatever Stream you have
// SoftwareSerial ch2oSerial(4, SW_SERIAL_UNUSED_PIN); // RX, TX
SoftwareSerial ch2oSerial(14, 14); // RX, TX

// Instantiate a sensor connected to the previous port
Ze08CH2O ch2o{&ch2oSerial};

void setup() {
	ch2oSerial.begin(9600);
	ch2oSerial.listen();
	Serial.begin(115200); // Serial Monitor
}

void loop() {
	Ze08CH2O::concentration_t reading;
	
	if (ch2o.read(reading)) {
		Serial.print("New value: ");
		Serial.println(reading);
	}
}


여기서 한가지 문제가 있습니다.

ESP8266 에는 RX pin 이 하나만 있어, Serial Monitor 를 이용하면서 "수신" 을 받을 수 있는 pin 이 없다는 것이죠.

즉, sensor 를 연결할 수 있는 Pin 이 없습니다.



남아있는 GPIO2 는 TX 용도이고, 부팅 후에는 HIGH 로, 3V 전압이 걸려 있습니다.

그럼 도대체 이 소스 제작자는 어떻게 확인한 것일 까요?


사실은 ESP2866 이라는 것은 ESP-01 만 뜻하는 것이 아니라, ESP8266EX 을 사용한 WiFi module 의 총칭이었던 것입니다.

저는 지금까지 ESP8266 = ESP-01 인줄 알고, SoftwareSerial 부분에서 더 이상 진행을 못하고 있었습니다.



위의 도식처럼 ESP8266EX 에는, 더 많은 GPIO 를 지원하고 있었습니다.

단순히, ESP-01 의 pin out 갯수가 적었던 것이였죠. 더 많은 연결을 위해 ESP-01 도 pin out 을 처음부터 늘려 줬으면 어떠했을까 합니다.

어떤 사람이 "it's a shame to have such a small number of GPIOs at ESP-01" 라고 쓴 글을 본것 같습니다.


저도 chip diagram 을 보고, 납땜을 시도 했습니다.... 만 실패 했습니다. 너무 조밀합니다.



굴하지 않고, ESP8266EX chip 을 사용하면서 Pinout 이 확장된 breakout 모델인 ESP-03 을 구입해서 연결 했습니다!

연결할 수 있는 Pinout - GPIO 가 많아서 행복합니다.



Pin 연결도는 다음과 같습니다.


 ZE08-CH2O |  ESP-03
----------------------
   6 (TX)  | GPIO 14
----------------------
           |  POWER
----------------------
    VCC    |   3.3V
    GND    |   GND
----------------------


실재 구성도는 다음과 같습니다.



Serial Monitor 까지 연결한 모습이 다음과 같습니다. ESP-03 의 GPIO 14 에 ZE08-CH2O 의 TX 와 연결되어 있습니다.




기본 소스에 IoT 솔루션인 Blynk 소스를 입혀 봤습니다. 자세한 내용은 아래 포스트에 올려 놨습니다.


* Software | Blynk 사용해 보기

https://chocoball.tistory.com/entry/Software-Blynk-howto


이렇게 하므로써, WiFi 연결까지 소스에 한방에 녹여 놓을 수 있습니다.


#include "ze08_ch2o.h"
#include "SoftwareSerial.h"
 
SoftwareSerial ch2oSerial(14, 14); // RX, TX
Ze08CH2O ch2o{&ch2oSerial};
 
int sensorData;
 
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
 
#include "ESP8266WiFi.h"
#include "BlynkSimpleEsp8266.h"
 
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "XXXXXXXXXXXX";
char pass[] = "YYYYYYYYYYYYYYYYYYY";
 
BlynkTimer timer;
 
// This function sends Arduino's up time every second to Virtual Pin (5).
// In the app, Widget's reading frequency should be set to PUSH. This means
// that you define how often to send data to Blynk App.
void myTimerEvent() {
    // You can send any value at any time.
    // Please don't send more that 10 values per second.
     
    Ze08CH2O::concentration_t reading;
    if (ch2o.read(reading)) {
        Serial.print("ZE08-CH2O : ");
        Serial.println(reading);
         
        sensorData = reading;
    }
    Blynk.virtualWrite(V5, sensorData);
}
 
void setup() {
    // Debug console
    Serial.begin(115200);
     
    ch2oSerial.begin(9600);
    ch2oSerial.listen();
     
    Blynk.begin(auth, ssid, pass);
    // You can also specify server:
    //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 80);
    //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
     
    // Setup a function to be called every second
    timer.setInterval(5000L, myTimerEvent);
}
 
void loop() {
    Blynk.run();
    timer.run(); // Initiates BlynkTimer
}


아래와 같이 값들이 표시됩니다.

값의 범위가 50 ~ 150 정도여서, 기준을 모르겠어나, 값의 변동이 기민하게 발생하는 것을 보니, 문제 없을 듯 합니다.

나누기 100 을 하면, UART 방식의 값과 거의 비슷해 집니다.



문제 없이 Blynk 어플에서 값들을 확인할 수 있습니다.





FIN


ZE08-CH2O 의 연결 방식인 DAC / UART 는 시험해 봤으나, PWM 은 정보가 없어서 시도해 보지 못했네요.

나중에 알게 되면 추가하도록 하겠습니다.


Formaldehyde 센서 확인도 끝났으니, 자 다음 센서요~.


And

Hardware | ESP32 NTP Server 이용한 시간 맞추기

|

지금까지 ESP32 에 관한 글은 아래를 참고해 주세요.


* Hardware | ESP32 Deep sleep 알아보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Deep-sleep

* Hardware | ESP32 Cryptographic HW 가속 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Cryptographic-HW-acceleration

* Hardware | EPS32 PWM 기능 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-EPS32-PWM

* Hardware | ESP32 의 internal sensor 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-internal-sensors

* Hardware | ESP32 의 Dual core 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Dual-core

* Hardware | ESP32 스펙 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check

* Hardware | ESP32 간단 사용기
    - https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32


이 글을 마지막으로 ESP32 에 대해 대략적인 내용은 얼추 확인해 본 것 같습니다.

이후에는 WiFi 이용한 활용시에는 가능한 ESP32 를 사용해 보려 합니다.




1. NTP


본 포스트는 아래 글을 참조 하였습니다.


* Getting Date & Time From NTP Server With ESP32

- https://lastminuteengineers.com/esp32-ntp-server-date-time-tutorial/



NTP 서버란 인터넷에서 시간을 동기화 시켜주는 서버를 말합니다.
인증, GPS 위치와 연관된 시간 정보, 동기화와 관련된 timestamp 등, 인터넷 서비스의 거의 모든 기능들이 동일한 "시간" 정보가 필요합니다.


동일한 시간 기준으로 동작해야 하는 서비스를 위해, 인터넷에서는 NTP 라는 서비스가 지원되고 있습니다.


* Network Time Protocol

- https://en.wikipedia.org/wiki/Network_Time_Protocol


이를태면, 정확한 시간 정보를 가져올 수 있는 서버들이 존재한다는 것이죠.




2. WiFi



"인터넷" 을 통해 시간 정보를 가져와야 하므로, WiFi 등의 인터넷 연결이 필수 입니다.

ESP32 는, WiFi 연결을 위해 "WiFi.h" 라이브러리를 지원합니다. 이를 통해 쉽게 WiFi 연결을 실현해 줍니다.

지금까지 Arduino + ESP8266 에서는 AT command 를 이용하여, 하나하나 명령어를 정의해야 했었는데, 그럴 수고를 덜어줍니다.


const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";


인터넷 접속을 위한 WiFI SSID 및 비번 정의를 하면 끝 입니다. 정말로 이걸로 끝입니다.




3. NTP


NTP 서버를 통해 시간을 가져오는 소스는 다음과 같습니다.


#include "WiFi.h"
#include "time.h"

const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

void printLocalTime() {
	struct tm timeinfo;
	if(!getLocalTime(&timeinfo)) {
		Serial.println("Failed to obtain time");
		return;
	}
	Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

void setup() {
	Serial.begin(115200);
	
	// connect to WiFi
	Serial.printf("Connecting to %s ", ssid);
	WiFi.begin(ssid, password);
	while (WiFi.status() != WL_CONNECTED) {
		delay(500);
		Serial.print(".");
	}
	Serial.println(" CONNECTED");
	
	// init and get the time
	configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
	printLocalTime();
	
	// disconnect WiFi as it's no longer needed
	WiFi.disconnect(true);
	WiFi.mode(WIFI_OFF);
}

void loop() {
	delay(1000);
	printLocalTime();
}


위의 소스를 각 로컬 상황에 맞게 설정해줘야 합니다.


const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;


우선, NTP 서버는 "pool.ntp.org" 로 정의 합니다. 이 FQDN 을 통해 NTP 서버를 할당 받습니다.


"gmOffset_sec" 는 GMT 기준으로 얼마나 차이나는지를 확인합니다.

한국은 그리니치 천문대 기준 9시간 추가된 시간대인, "GMT+9" 이므로 "3600 * 9 = 32400" 만큼 더해주면 됩니다.


또한, "daylightOffset_sec" 은, 서머타임 적용 지역이면, 한시간인 3600 을 적용하면 됩니다.

우리나라는 서머타임 적용은 80년대에 일시적으로 적용하고, 그 이후 사용되지 않으므로 "0" 으로 정의합니다. (옛날 사람...)


위의 내용을 적용하고 실행하면 다음과 같이 됩니다.



한국 상황에 맞게, 요일까지 정확히 표시할 수 있는 것을 확인했습니다.



FIN

ESP32 의 WiFi 구현이 얼마나 간단한지를 확인해 보기 위해 NTP 서비스를 활용해 봤습니다.
앞으로는 ESP32 를 통하여 다양한 project 를 해봐야 겠네요.

끝.


And

Hardware | ESP32 Deep sleep 알아보기

|

ESP32 는 WiFi 및 Bluetooth 에 추가하여 Dual-core CPU 에 sensor 등, 작은 사이즈에 많은 기능을 내포하고 있습니다.

본격적인 IoT 생활을 위해 Arduino 보드에서 ESP32 로 넘어가는 중이라 ESP32 에 대해 공부하고 있습니다.


지금까지 작성된 ESP32 에 관한 글들은 아래 포스트들을 참고해 주세요.


* Hardware | ESP32 Cryptographic HW 가속 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Cryptographic-HW-acceleration

* Hardware | EPS32 PWM 기능 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-EPS32-PWM

* Hardware | ESP32 의 internal sensor 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-internal-sensors

* Hardware | ESP32 의 Dual core 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Dual-core

* Hardware | ESP32 스펙 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check

* Hardware | ESP32 간단 사용기
    - https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32



1. Power Modes


ESP32 는 사용 전력량을 알맞게 활용할 수 있도록 5가지 Power 모드를 제공하고 있습니다. Off-Grid 에서의 활용에서는 필수겠죠.



아래 테이블은, 각 mode 들에 따른 ESP32 부위별 active / inactive 와 사용 전력 정보 입니다.


* Insight Into ESP32 Sleep Modes & Their Power Consumption
    - https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/


--------------------------------------------------------------------------------
| Power Mode  | Active                 | Inactive               | Power        |
|             |                        |                        | Consumption  |
|------------------------------------------------------------------------------|
| Active      | WiFi, Bluetooth, Radio |                        | 160 ~ 260 mA |
|             | ESP32 Core             |                        |              |
|             | ULP Co-processor       |                        |              |
|             | Peripherals, RTC       |                        |              |
|------------------------------------------------------------------------------|
| Modem Sleep | ESP32 Core             | WiFi, Bluetooth, Radio |   3 ~ 20 mA  |
|             | ULP Co-processor, RTC  | Peripherals            |              |
|------------------------------------------------------------------------------|
| Light Sleep | ULP Co-processor, RTC  | WiFi, Bluetooth, Radio |     0.8 mA   |
|             | ESP32 Core (Paused)    | Peripherals            |              |
|------------------------------------------------------------------------------|
| Deep Sleep  | ULP Co-processor, RTC  | WiFi, Bluetooth, Radio |     10 uA    |
|             |                        | ESP32 Core, Peripherals|              |
|------------------------------------------------------------------------------|
| Hibernation | RTC                    | ESP32 Core             |     2.5 uA   |
|             |                        | ULP Co-processor       |              |
|             |                        | WiFi, Bluetooth, Radio |              |
|             |                        | Peripherals            |              |
--------------------------------------------------------------------------------


사용하지 않는 부분을 죽이고 최대한 사용 전력을 아끼는 방법입니다.

사양서에는, 좀더 자세한 power mode 별 소비 전력이 안내되어 있습니다.



이번 포스트에서 집중적으로 확인해 볼 Deep sleep 에서는 CPU 를 사용하지 않고, 소전력으로 돌아가는 ULP processor 를 활용합니다.



참고고, ESP32 에서 사용되는 전력의 많은 부분은 역시 WiFi/Bluetooth 이군요.





2. RTC_IO / Touch


Deep sleep 시에 ESP32 를 깨우거나 입력을 받아들이는 pin 은 RTC_IO / Touch 핀들 입니다.

즉, 이 pin 들을 통하여 ULP co-processor 에 자극을 줘서 deep sleep 에서 깨어날 수 있도록 open 된 핀들이라고 할 수 있겠네요.

참고로 모든 Touch 핀들은 RTC_IO 핀들에 포함되어 있습니다.





3. 깨우기 - timer


마침 Examples 에 정갈하게 소스가 올라와 있으니, 이걸 이용해서 확인해 봅니다.

File > Examples > ESP32 > DeepSleep > TimerWakeup


원본 소스는 다음과 같습니다. 5초마다 깼다가 바로 잠드는 시퀀스로 짜여져 있습니다.

wakeup_reason 을 통해, timer 외에 touch 나 external pin 에 의한 확인도 가능하게 만들어져 있네요.


/*
Simple Deep Sleep with Timer Wake Up
=====================================
ESP32 offers a deep sleep mode for effective power
saving as power is an important factor for IoT
applications. In this mode CPUs, most of the RAM,
and all the digital peripherals which are clocked
from APB_CLK are powered off. The only parts of
the chip which can still be powered on are:
RTC controller, RTC peripherals ,and RTC memories

This code displays the most basic deep sleep with
a timer to wake it up and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli - cherukupallip@gmail.com
*/

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up every 5 seconds
  */
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  /*
  Next we decide what all peripherals to shut down/keep on
  By default, ESP32 will automatically power down the peripherals
  not needed by the wakeup source, but if you want to be a poweruser
  this is for you. Read in detail at the API docs
  http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
  Left the line commented as an example of how to configure peripherals.
  The line below turns off all RTC peripherals in deep sleep.
  */
  //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
  //Serial.println("Configured all RTC Peripherals to be powered down in sleep");

  /*
  Now that we have setup a wake cause and if needed setup the
  peripherals state in deep sleep, we can now start going to
  deep sleep.
  In the case that no wake up sources were provided but deep
  sleep was started, it will sleep forever unless hardware
  reset occurs.
  */
  Serial.println("Going to sleep now");
  Serial.flush(); 
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}


Serial Monitor 를 통해, 5초마다 깨어나는 상황을 확인 할 수 있습니다.





4. 깨우기 - touch pin


터치센서를 통하여 잠에서 깨우는 소스 입니다. 이것도 마찬가지로 기본 제공되는 Example 을 이용하여 확인해 봤습니다.


File > Examples > ESP32 > DeepSleep > TouchWakeup

/*
Deep Sleep with Touch Wake Up
=====================================
This code displays how to use deep sleep with
a touch as a wake up source and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli - cherukupallip@gmail.com
*/

#define Threshold 40 /* Greater the value, more the sensitivity */

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
  touchPin = esp_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

void callback(){
  //placeholder callback function
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32 and touchpad too
  print_wakeup_reason();
  print_wakeup_touchpad();

  //Setup interrupt on Touch Pad 3 (GPIO15)
  touchAttachInterrupt(T3, callback, Threshold);

  //Configure Touchpad as wakeup source
  esp_sleep_enable_touchpad_wakeup();

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This will never be reached
}


GPIO15 번을 손으로 터치하면 깨어납니다.



Serial Monitor 에서도 touch pin 에서의 감지를 알려 줍니다.





5. 깨우기 - EXT(0) external wake ups


Pin 의 HIGH/LOW 입력을 통하여 깨우는 방법 입니다.


File > Examples > ESP32 > DeepSleep > ExternalWakeup


/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli -cherukupallip@gmail.com
*/

//#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
#define BUTTON_PIN_BITMASK 0x16 // 2^4 in hex

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
//  esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_4,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}


원본 소스는 GPIO_33 번을 활용하게 되어 있으나, 핀 배열상 GPIO_4 로 바꾸도록 수정 해 봤습니다.

Linux 의 파일 시스템에서, 쓰기/읽기 정의에 사용되는 bit mask 방법을 사용하는 군요.


2^(pin 번호) 를 16진수로 표현하여 정의합니다. GPIO_4 이므로, 우선 2 의 4승 계산은 다음과 같습니다.


2^4 = 16



위의 결과를 16진수로 변경해야 합니다. 아래 사이트의 converter 를 이용하면 편합니다.


* Decimal to Hexadecimal converter
    - https://www.rapidtables.com/convert/number/decimal-to-hex.html



최종적으로 2^4 = 16 > 10 (Hexadecimal) 가 됩니다.


...

//#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
#define BUTTON_PIN_BITMASK 0x10 // 2^4 in hex

...

//  esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_4,1); //1 = High, 0 = Low

...


BITMASK 값과 ext0GPIO_NUM_4 로 바꾸어 주면 정의가 완료 됩니다.
회로를 아래처럼 꾸며, 스위치를 누르면 깨어나는 방법입니다.


실제 사진은 다음과 같습니다.



정상 작동 되는군요.





6. 깨우기 - EXT(1) external wake ups


외부 입력을 통한 방법은 위의 EXT(0) 와 같으나, 이번에는 두 개의 스위치를 이용하는 방법 입니다.


/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots
 
This code is under Public Domain License.
 
Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor
 
NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.
 
Author:
Pranav Cherukupalli - cherukupallip@gmail.com
*/
 
#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15
 
RTC_DATA_ATTR int bootCount = 0;
 
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;
 
  wakeup_reason = esp_sleep_get_wakeup_cause();
 
  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}
 
/*
Method to print the GPIO that triggered the wakeup
*/
void print_GPIO_wake_up(){
  int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
  Serial.print("GPIO that triggered the wake up: GPIO ");
  Serial.println((log(GPIO_reason))/log(2), 0);
}
   
void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor
 
  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));
 
  //Print the wakeup reason for ESP32
  print_wakeup_reason();
 
  //Print the GPIO used to wake up
  print_GPIO_wake_up();
 
  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  //esp_deep_sleep_enable_ext0_wakeup(GPIO_NUM_15,1); //1 = High, 0 = Low
 
  //If you were to use ext1, you would use it like
  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
 
  //Go to sleep now
  Serial.println("Going to sleep now");
  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}
 
void loop(){
  //This is not going to be called
}


여기서의 포인트는, 입력을 받는 복수의 GPIO 번호를 더해서 BITMASK 를 구하는 것 입니다.

사용된 GPIO 정보는 2번과 15번 입니다.


GPIO_2 + GPIO_15 > 2^2 + 2^15 = 32772



32772 의 16진수는 8004 입니다.



원본 소스에서 아래처럼 GPIO 의 BITMASK 변경과, 어떤 GPIO 가 눌렀는지의 표시, 그리고 EXT1 을 정의해 줍니다.


...

#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15
...

/*
Method to print the GPIO that triggered the wakeup
*/
void print_GPIO_wake_up(){
  int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
  Serial.print("GPIO that triggered the wake up: GPIO ");
  Serial.println((log(GPIO_reason))/log(2), 0);
}
  
...

  //If you were to use ext1, you would use it like
  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

...


회로는 다음과 같이 스위치를 연결 했습니다. EXT0 와 다른 것은 복수 (두 개) 의 스위치를 입력받게 하는 것 입니다.



Serial Monitor 의 결과는 다음과 같아요. 어느 GPIO 핀에서 입력 받았는지를 표시해 줍니다.





7. 전류 확인 - Deep sleep


실재로 전류 변화가 있는지 확인해 봤습니다.



음... 그리 많이 차이나지 않군요.

Examples 에서 제공되는 기본 소스는 Wake up 만 확인하는 소스이지, 계산을 시키거나 하는 것이 아니라서 그런 것 같습니다.





FIN


배터리를 가지고 동작하는 장비들은 필수로 가지고 있어야 할 Power mode 들, 특히 Deep sleep 에 대해 알아 봤습니다.

전력 소비가 가장 심한 WiFi/Bluetooth 부분을 어떻게 활용하면서 운용해야 하는지를 많이 고민해야 할 것 같네요.


- WiFi/Bluetooth 연결 정보는 연결 실패 exception 이나 스케줄에 따라 실시

- 가능한 정보는 memory 에 저장하여 꺼내 쓰는 방식으로

- 배터리 방전 threshold 값을 모니터링 하고, 일정 값 이하로 내려가면 지속적인 alert 발생

- WiFi/Bluetooth 에 사용되는 전류량을 컨트롤 하여, 근거리 통신 -> 중거리 -> 장거리 통신으로 연결하게끔 구성

- DTIM (Delivery Traffic Indication Message) beacon mechanism 등을 활용

- 등등...



And

Hardware | ESP32 Cryptographic HW 가속 확인해 보기

|

ESP32 에 관한 글들은 아래 링크들을 참고해 주세요.


* Hardware | EPS32 PWM 기능 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-EPS32-PWM

* Hardware | ESP32 의 internal sensor 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-internal-sensors

* Hardware | ESP32 의 Dual core 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Dual-core

* Hardware | ESP32 스펙 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check

* Hardware | ESP32 간단 사용기
    - https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32



1. Cryptographic Hardware Acceleration


한 13여년 전에 웹 서비스 구축 시, HTTPS (SSL) 처리에 CPU 연산을 너무 많이 사용해서 골치가 아팠던 경험이 있습니다.

당시에는 NIC 에 붙어있는 Intel 칩에서 SSL 가속 처리를 못해줘, OS 에서 처리하다 보니 CPU 들이 죽어 나갔죠.


막 HW 가속기 (PCI daughter card 형식) 들이 등장하기도 했습니다만, 어디까지나 실험적인 제품들이었고, OS 와 HW 특성을 많이 타다 보니 PoC 단계에서도 그닥 실효를 거두지 못했었습니다.


암호 연산에 대해서는 요즘 NIC 나 CPU 자체적으로 전용 명령어 set을 가지고 지원하는 시대이다 보니, 예전같은 걱정은 말끔히 사라졌네요.


근래에 출시된 ESP32 에도, 이 암호 연산용 HW 가속 기능이 내장되어 있습니다!

다이어그램 상, SHA / RSA / AES / RNG 등이 있네요.



사양서에도 이 HW Accelerator 에 대한 안내가 되어 있습니다.



Cryptographic hardware acceleration

- AES, SHA-2, RSA, elliptic curve cryptography (ECC), random number generator (RNG)


이번 글은 위의 HW Accelerator 의 몇 가지 기능 중, ESP32 의 hardware AES 에 대해 알아보고자 합니다.




2. AES


미국 정부가 1990년 후반까지 사용하고 있던 DES 암호화 기법이, 약 30대 정도의 PC 를 가지고 뚫리면서, 새로운 암호화 기법을 찾게 됩니다. 공모 결과 AES 가 채택되면서 유명해진 암호화 기법이에요.


AES 는 "Advanced Encryption Standard" 의 약자로 cryptographic symmetric cipher algorithm 을 기반으로 encryption 과 decryption 양쪽에 사용될 수 있는 장점을 가지고 있습니다.

참고로 아래 두 가지의 parameter 를 필요로 합니다.


IV (Initial Vector)

맨 처음 block 을 XOR 처리를 할 때, 덮어 씌우는 data block 이 존재하지 않습니다. 이를 보충해 주기 위한 인위적인 블럭이 IV 입니다.


Encryption Key

암호화 / 복호화에 사용되는 고유의 키 입니다.



너무 자세한 설명에 들어가면, 저의 지식이 바닦 치는 것이 보이기에 여기서 그만 합니다.

인터넷에 관련된 문서 및 동영상들이 어마어마 하니, 자세히 공부해 보고 싶으신 분을 넓은 인터넷의 세계로...




3. Software AES - ESP32


HW 가속을 시험해 보기에 앞서, AES 를 소프트웨어적으루 구현해본 분이 계셔서 따라 해봤습니다.


* AES Encryption/Decryption using Arduino Uno
    - https://www.arduinolab.net/aes-encryptiondecryption-using-arduino-uno/


우선 필요한 것은, Spaniakos 라는 분이 만드신 AES 라이브러리를 설치해 줍니다. 아래 Github 에서 라이브러리를 다운 받습니다.


* AES for microcontrollers (Arduino & Raspberry pi)
    - https://github.com/spaniakos/AES



그리고, Arduino libraries 폴더에 심어 놓으면 됩니다.



기본 준비가 되었으니, ESP32 에서 AES-CBC 방식의 암호화/복호화를 실행 해봅니다.


/*------------------------------------------------------------------------------ 
Program:      aesEncDec 
 
Description:  Basic setup to test AES CBC encryption/decryption using different 
              key lengths.
 
Hardware:     Arduino Uno R3 
 
Software:     Developed using Arduino 1.8.2 IDE
 
Libraries:    
              - AES Encryption Library for Arduino and Raspberry Pi: 
                https://spaniakos.github.io/AES/index.html
 
References: 
              - Advanced Encryption Standard by Example: 
              http://www.adamberent.com/wp-content/uploads/2019/02/AESbyExample.pdf
              - AES Class Reference: https://spaniakos.github.io/AES/classAES.html
 
Date:         July 9, 2017
 
Author:       G. Gainaru, https://www.arduinolab.net
              (based on AES library documentation and examples)
------------------------------------------------------------------------------*/
#include "AES.h"

AES aes ;

unsigned int keyLength [3] = {128, 192, 256}; // key length: 128b, 192b or 256b

byte *key = (unsigned char*)"01234567890123456789012345678901"; // encryption key
byte plain[] = "https://chocoball.tistory.com/entry/Hardware-ESP32-Cryptographic-hardware-acceleration"; // plaintext to encrypt

unsigned long long int myIv = 36753562; // CBC initialization vector; real iv = iv x2 ex: 01234567 = 0123456701234567

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

void loop () {
	for (int i=0; i < 3; i++) {
		Serial.print("- key length [b]: ");
		Serial.println(keyLength [i]);
		aesTest (keyLength[i]);
		delay(2000);
	}
}

void aesTest (int bits) {
	aes.iv_inc();
	
	byte iv [N_BLOCK];
	int plainPaddedLength = sizeof(plain) + (N_BLOCK - ((sizeof(plain)-1) % 16)); // length of padded plaintext [B]
	byte cipher [plainPaddedLength]; // ciphertext (encrypted plaintext)
	byte check [plainPaddedLength]; // decrypted plaintext
	
	aes.set_IV(myIv);
	aes.get_IV(iv);
	
	Serial.print("- encryption time [us]: ");
	unsigned long ms = micros ();
	aes.do_aes_encrypt(plain, sizeof(plain), cipher, key, bits, iv);
	Serial.println(micros() - ms);
	
	aes.set_IV(myIv);
	aes.get_IV(iv);
	
	Serial.print("- decryption time [us]: ");
	ms = micros ();
	aes.do_aes_decrypt(cipher,aes.get_size(),check,key,bits,iv); 
	Serial.println(micros() - ms);
	
	Serial.print("- plain:   ");
	aes.printArray(plain,(bool)true); //print plain with no padding
	
	Serial.print("- cipher:  ");
	aes.printArray(cipher,(bool)false); //print cipher with padding
	
	Serial.print("- check:   ");
	aes.printArray(check,(bool)true); //print decrypted plain with no padding
	
	Serial.print("- iv:      ");
	aes.printArray(iv,16); //print iv
	printf("\n-----------------------------------------------------------------------------------\n");
}


암호화를 걸 평문은, 이 포스트의 URL 을 사용했습니다. :-)



암호화/복호화 잘 됩니다. 속도도 좋네요.




4. Software AES - ATmega328


비교 대상으로 궁금하여, ATmega328 을 탑재한 Arduino nano 로 동일한 계산을 시켜보기로 합니다.

다만, "AES.h" 라이브러리를 include 한다고 제대로 실행되진 않는군요.


	aes.printArray(plain,(bool)true); //print plain with no padding


이유는, ATmega328 의 라이브러리에는 위의 printArray 에서 사용하는 printf_P 함수가 없기 때문입니다. (AES.cpp 에서 정의됨)

ESP32 의 FreeRTOS 에는 C 라이브러리 기본 탑재로 문제 없이 동작하지만, Arduino nano 에서는 동작하지 않습니다.


그리하여, 이를 대신할 function 을 만들어 봤는데, 징그럽게도 동작하지 않더군요.

수많은 삽질을 통해, 배열을 다른 함수의 인자로 전달하려면 배열의 pointer 와 그 배열의 크기를 명시해야 하는 것을 알게 되었습니다.

결국 아래처럼 변경하여 Arduino nano 에서도 동작을 성공 시켰습니다.


...

	showArray(iv, array_size_iv, 1);

...

void showArray (byte *result, int array_length, int hex_conv) {
	for (int i=0; i < array_length; i++) {
		if (hex_conv) {
			Serial.print(result[i], HEX);
		} else {
			Serial.print((char)result[i]);
		}
	}
	Serial.println();
}


최종 소스는 다음과 같습니다.


#include "AES.h"

AES aes ;

unsigned int keyLength [3] = {128, 192, 256}; // key length: 128b, 192b or 256b

byte *key = (unsigned char*)"01234567890123456789012345678901"; // encryption key
byte plain[] = "https://chocoball.tistory.com/entry/Hardware-ESP32-Cryptographic-hardware-acceleration"; // plaintext to encrypt

unsigned long long int myIv = 36753562; // CBC initialization vector; real iv = iv x2 ex: 01234567 = 0123456701234567

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

void loop () {
	for (int i=0; i < 3; i++) {
		Serial.print("- key length [b]: ");
		Serial.println(keyLength [i]);
		aesTest (keyLength[i]);
		delay(2000);
	}
}

void aesTest (int bits) {
	aes.iv_inc();
	
	byte iv [N_BLOCK];
	int plainPaddedLength = sizeof(plain) + (N_BLOCK - ((sizeof(plain)-1) % 16)); // length of padded plaintext [B]
	byte cipher [plainPaddedLength]; // ciphertext (encrypted plaintext)
	byte check [plainPaddedLength]; // decrypted plaintext
	
	aes.set_IV(myIv);
	aes.get_IV(iv);
	
	Serial.print("- encryption time [us]: ");
	unsigned long ms = micros ();
	aes.do_aes_encrypt(plain, sizeof(plain), cipher, key, bits, iv);
	Serial.println(micros() - ms);
	
	aes.set_IV(myIv);
	aes.get_IV(iv);
	
	Serial.print("- decryption time [us]: ");
	ms = micros ();
	aes.do_aes_decrypt(cipher,aes.get_size(),check,key,bits,iv);
	Serial.println(micros() - ms);
	
	Serial.print("- plain:   ");
	//aes.printArray(plain,(bool)true); //print plain with no padding
	int array_size_p = sizeof(plain);
	showArray(plain, array_size_p, 0);
	
	Serial.print("- cipher:  ");
	//aes.printArray(cipher,(bool)false); //print cipher with padding
	int array_size_ci = sizeof(cipher);
	showArray(cipher, array_size_ci, 0);
	
	Serial.print("- check:   ");
	//aes.printArray(check,(bool)true); //print decrypted plain with no padding
	int array_size_ch = sizeof(check);
	showArray(check, array_size_ch, 0);
	
	Serial.print("- iv:      ");
	//aes.printArray(iv,16); //print iv
	int array_size_iv = sizeof(iv);
	showArray(iv, array_size_iv, 1);
	Serial.println("-----------------------------------------------------------------------------------");
}

void showArray (byte *result, int array_length, int hex_conv) {
	for (int i=0; i < array_length; i++) {
		if (hex_conv) {
			Serial.print(result[i], HEX);
		} else {
			Serial.print((char)result[i]);
		}
	}
	Serial.println();
}


아래는 Arduino nano 에서 실행시킨 결과 입니다.

ESP32 에서 Software 로 돌린 AES 결과와 비교시, 걸린 시간 빼곤 완벽히 동일합니다.



ESP32 vs. ATmega328 의 CPU 차에 의한 software AES 계산은 encryption = 27 배, decryption = 20 배 정도 차이 났습니다.


----------------------------------------------
| bits | ESP32 | ATmega328 | diff. (multiply)|
|--------------------------------------------|
| 128  |  159  |   4396    |       27.6      |
|      |  267  |   5388    |       20.1      |
|--------------------------------------------|
| 192  |  189  |   5156    |       27.2      | 
|      |  321  |   6392    |       19.9      |
|--------------------------------------------|
| 256  |  220  |   5964    |       27.1      |
|      |  376  |   7432    |       19.7      |
----------------------------------------------


ESP32 를 찬양하라!





5. Hardware AES - library


마지막으로 ESP32 의 HW AES 를 걸어볼 차례 입니다.

HW accelerator 의 Native library 는 아래 글에서 설명이 잘 되어 있습니다.


* AES-CBC encryption or decryption operation
    - https://tls.mbed.org/api/aes_8h.html#a321834eafbf0dacb36dac343bfd6b35d


요는 mbedtls 함수를 이용하면, HW accelerator 를 사용할 수 있게 되는군요. 키 포인트는 "mbedtls_aes_crypt_cbc" 함수가 되겠습니다.

int mbedtls_aes_crypt_cbc ( mbedtls_aes_context *	ctx,
							int						mode,
							size_t					length,
							unsigned char			iv[16],
							const unsigned char *	input,
							unsigned char *			output
)


각 변수들의 정의 입니다.


---------------------------------------------------------------------------------------------------
| Parameters | Meaning                                                                            |
---------------------------------------------------------------------------------------------------
|    ctx     | The AES context to use for encryption or decryption.                               |
|            | It must be initialized and bound to a key.                                         |
---------------------------------------------------------------------------------------------------
|    mode    | The AES operation: MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT.                     |
---------------------------------------------------------------------------------------------------
|   length   | The length of the input data in Bytes.                                             |
|            | This must be a multiple of the block size (16 Bytes).                              |
---------------------------------------------------------------------------------------------------
|    iv      | Initialization vector (updated after use).                                         |
|            | It must be a readable and writeable buffer of 16 Bytes.                            |
---------------------------------------------------------------------------------------------------
|   input    | The buffer holding the input data. It must be readable and of size length Bytes.   |
---------------------------------------------------------------------------------------------------
|   output   | The buffer holding the output data. It must be writeable and of size length Bytes. |
---------------------------------------------------------------------------------------------------


아래는 실재 구현에 도움이 될 만한 사이트들 입니다. 예제들이 설명되어 있어요.


* ESP32 Arduino: Encryption using AES-128 in ECB mode
    - https://techtutorialsx.com/2018/04/18/esp32-arduino-encryption-using-aes-128-in-ecb-mode/

* ESP32 Arduino Tutorial: Encryption AES128 in ECB mode
    - https://everythingesp.com/esp32-arduino-tutorial-encryption-aes128-in-ecb-mode/

* How to encrypt data with AES-CBC mode
    - https://tls.mbed.org/kb/how-to/encrypt-with-aes-cbc

재미 있는 것은, "mbedtls/aes.h" 의 library 도 동작하지만,

#include "mbedtls/aes.h"


"hwcrypto/aes.h" 라이브러리도 동일한 parameter 와 동작을 보여줍니다. 함수명에 mbedtls 가 붙느냐, esp가 붙느냐의 차이 뿐.


#include "hwcrypto/aes.h"


위의 두 가지 library 는 따로 설치하지 않아도 되는걸 보면, native library 이면서 서로가 copy 버전이 아닐까 하네요.




6. Hardware AES - 확인


위의 Software AES 를 Hardware AES 용으로 변환하면 되겠지만, 아직 지식이 짧은 관계로 아래 소스를 가지고 확인해 봤습니다.

* Example of using hardware AES 256 Crypto in CBC mode on the ESP32 using ESP-IDF
* cnlohr/esp32_aes_example.c

    - https://gist.github.com/cnlohr/96128ef4126bcc878b1b5a7586c624ef

#include "string.h"
#include "stdio.h"
#include "hwcrypto/aes.h"

/*
For Encryption time: 1802.40us (9.09 MB/s) at 16kB blocks.
*/

static inline int32_t _getCycleCount(void) {
	int32_t ccount;
	asm volatile("rsr %0,ccount":"=a" (ccount));
	return ccount;
}

char plaintext[16384];
char encrypted[16384];

void encodetest() {
	uint8_t key[32];
	uint8_t iv[16];
	
	//If you have cryptographically random data in the start of your payload, you do not need
	//an IV. If you start a plaintext payload, you will need an IV.
	memset( iv, 0, sizeof( iv ) );
	
	//Right now, I am using a key of all zeroes. This should change. You should fill the key
	//out with actual data.
	memset( key, 0, sizeof( key ) );
	
	memset( plaintext, 0, sizeof( plaintext ) );
	strcpy( plaintext, "https://chocoball.tistory.com/entry/Hardware-ESP32-Cryptographic-hardware-acceleration" );
	
	//Just FYI - you must be encrypting/decrypting data that is in BLOCKSIZE chunks!!!
	
	esp_aes_context ctx;
	esp_aes_init( &ctx );
	esp_aes_setkey( &ctx, key, 256 );
	int32_t start = _getCycleCount();
	esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), iv, (uint8_t*)plaintext, (uint8_t*)encrypted );
	int32_t end = _getCycleCount();
	
	float enctime = (end-start)/240.0;
	Serial.printf( "Encryption time: %.2fus (%f MB/s)\n", enctime, (sizeof(plaintext)*1.0)/enctime );
	//See encrypted payload, and wipe out plaintext.
	
	memset( plaintext, 0, sizeof( plaintext ) );
	
	int i;
	for( i = 0; i < 128; i++ ) {
		Serial.printf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
	}
	Serial.printf( "\n" );
	
	//Must reset IV.
	//XXX TODO: Research further: I found out if you don't reset the IV, the first block will fail
	//but subsequent blocks will pass. Is there some strange cryptoalgebra going on that permits this?
	Serial.printf( "IV: %02x %02x\n", iv[0], iv[1] );
	memset( iv, 0, sizeof( iv ) );
	
	//Use the ESP32 to decrypt the CBC block.
	Serial.print("- decryption time [us]: ");
	unsigned long ms = micros ();
	esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), iv, (uint8_t*)encrypted, (uint8_t*)plaintext );
	Serial.println(micros() - ms);
	
	//Verify output
	for( i = 0; i < 128; i++ ) {
		Serial.printf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
	}
	Serial.printf( "\n" );
	
	esp_aes_free( &ctx );
}

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

void loop() {
	// put your main code here, to run repeatedly:
}


결과는 다음과 같습니다.

위의 Software AES 와 비슷하게 결과를 내도록 소스를 만들면 확연히 비교할 수 있겠으나, 공부를 더 해야 함.


다만, 최종적인 처리 속도는 결코 아래 결과에서 변하지 않는다는 것을 여러 삽질을 통해 발견했으니 이걸로 만족.



Hardware AES 를 서포트하는 전용 명령어 set 이 최적화가 되지 않아, 이런 결과가 나온 것인지 모르겠네요.

지금으로써는 전용 accelerator 를 사용하지 않고, Software AES 를 구현하는 것이 더 속도적인 이득이 있는 듯 보입니다.


단, 여러가지 일을 동시에 처리해야 할 경우, 암호화/복호화 처리 부분만 따로 분리하여 HW accelerator 를 이용한다면, CPU 부하를 분산시켜 효율적인 활용은 가능할 것 같네요.




FIN


혹시, 위의 Hardware AES 결과가 잘못된 방법으로 검증된 것이라면 댓글로 알려주세요.


esp32_technical_reference_manual_en.pdf



Clock 사이클을 바탕으로 이론적인 계산을 해보면 68ns 레벨이라고, 아래 블로그에서 봤는데, 실측과는 많이 다르군요.


* Pwn the ESP32 crypto-core
    - https://limitedresults.com/2019/08/pwn-the-esp32-crypto-core/


Doing simple math, the HW AES-128 encryption process is 68.75ns (@160MHz).


그렇다고 합니다.


And

Hardware | EPS32 PWM 기능 확인해 보기

|

ESP32 에 관한 포스트는 아래를 참고해 주세요.


* Hardware | ESP32 의 internal sensor 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-internal-sensors

* Hardware | ESP32 의 Dual core 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Dual-core

* Hardware | ESP32 스펙 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check

* Hardware | ESP32 간단 사용기
    - https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32




1. PWM


ESP32 에는 PWM (Pulse Width Modulation) 기능이 들어가 있습니다.

LED 의 dimming (밝기) 조절이나, LCD panel 의 색조절 등에 사용되는 기법입니다.


일전에 8x8 LED matrix 를 다양한 색을 표현하기 위해, PWM 을 구현해 주는 전용 보드를 사용해서 확인해 본 적이 있습니다.


* Hardware | 8x8 LED matrix 와 Colorduino 이용해 보기
    - https://chocoball.tistory.com/entry/Hardware-8x8-LED-matrix-Colorduino


이반적인 Arduino 에는 이 기능이 기본 내장이 아닌지라, PWM 을 구현하려면 대응 보드를 사용해야 하는 번거로움이 존재합니다.

그치만, ESP32 는 기본 내장이에요!



PWM 을 통한 활용은 LED 뿐 만 아니라, step motor 난 piezo speaker 등에서도 활용할 수 있습니다.



기본 동작은 높은 주파수를 통해, on/off 를 해주는 시간 (duty) 를 변경하면서 조절하는 방법입니다.

당연히 duty 가 길면 길수록 밝기가 세다거나, 지속적인 동작을 보여주는 원리 입니다.

LED, Step motor, 그리고 Piezo 스피커를 작동시키는 동작원리와 완벽히 같습니다.




2. 코딩


지금까지 몇 번 봐왔던 원리인지라, 바로 코딩에 들어가 봅니다. 참조한 사이트는 아래 입니다.


* ESP32 PWM Example
    - https://circuits4you.com/2018/12/31/esp32-pwm-example/


PWM 을 구현하기 위한 명령어는 라이브러리에 준비되어 있으니, 그냥 사용해 줍니다.


* ledcSetup(PWM_Channel_Number, Frequency, resolution)

- Pass three arguments as an input to this function, channel number, frequency and the resolution of PWM channel at inside the setup function only.


* ledcAttachPin(GPIO_PIN , CHANNEL)

- Two arguments. One is the GPIO pin on which we want to get the OUTPUT of signal and second argument is the channel which we produce the signal.


* ledcWrite(CHANNEL, DUTY_CYCLE)

- ledcWrite function is used to generate the signal with a duty cycle value.


ledcSetupledcAttachPin 은 setup() 에서, 실제 구동은 loop() 에서 ledcWrite 를 사용합니다. digitalWrite 와 비슷하죠.

최종적으로 ESP32 보드에 실장되어 있는 LED 를 가지고 PWM 을 확인한 코드가 아래 입니다.


// Generates PWM on Internal LED Pin GPIO 2 of ESP32
#define LED 2 //On Board LED

int brightness = 0; // how bright the LED is
int fadeAmount = 5; // how many points to fade the LED by

// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 10; // Resolution 8, 10, 12, 15

void setup() {
	Serial.begin(115200);
	pinMode(LED, OUTPUT);
	
	// configure LED PWM functionalitites
	ledcSetup(ledChannel, freq, resolution);
	
	// attach the channel to the GPIO2 to be controlled
	ledcAttachPin(LED, ledChannel);
}

void loop() {
	// PWM Value varries from 0 to 1023
	Serial.println("10 % PWM");
	ledcWrite(ledChannel, 102);
	delay(2000);
	
	Serial.println("20 % PWM");
	ledcWrite(ledChannel,205);
	delay(2000);
	
	Serial.println("40 % PWM");
	ledcWrite(ledChannel,410);
	delay(2000);
	
	Serial.println("70 % PWM");
	ledcWrite(ledChannel,714);
	delay(2000);
	
	Serial.println("100 % PWM");
	ledcWrite(ledChannel,1024);
	delay(2000);
	
	// Continuous Fading
	Serial.println("Fadding Started");
	while(1) {
		// set the brightness of pin 2:
		ledcWrite(ledChannel, brightness);
		
		// change the brightness for next time through the loop:
		brightness = brightness + fadeAmount;
		
		// reverse the direction of the fading at the ends of the fade:
		if (brightness <= 0 || brightness >= 1023) {
			fadeAmount = -fadeAmount;
		}
		
		// wait for 30 milliseconds to see the dimming effect
		delay(10);
	}
}




3. LED_BUILTIN


위의 코드를 실행하면 Serial Monitor 에서는 다음과 같이 표시됩니다.


소스에서도 알 수 있듯이, 10% > 20% > 40% > 70% > 100% 으로 LED 를 키고, 그 다음부터는 5/1024 씩 증감하면서 dimming 을 표현해 줍니다.

실제 동작 동영상은 다음과 같습니다.




4. 오실로스코프 확인


여기서 끝내면 심심하니, 실제 파형을 확인해 보려 합니다. 참고한 사이트는 다음과 같습니다.


* ESP32 PWM with Arduino IDE (Analog Output)
    - https://randomnerdtutorials.com/esp32-pwm-arduino-ide/


소스 코드는 다음과 같습니다.


// the number of the LED pin
const int ledPin = 16;  // 16 corresponds to GPIO16
const int ledPin2 = 17; // 17 corresponds to GPIO17
const int ledPin3 = 5;  // 5 corresponds to GPIO5

// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
 
void setup() {
	// configure LED PWM functionalitites
	ledcSetup(ledChannel, freq, resolution);
	
	// attach the channel to the GPIO to be controlled
	ledcAttachPin(ledPin, ledChannel);
	ledcAttachPin(ledPin2, ledChannel);
	ledcAttachPin(ledPin3, ledChannel);
}

void loop() {
	// increase the LED brightness
	for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
		// changing the LED brightness with PWM
		ledcWrite(ledChannel, dutyCycle);
		delay(15);
	}
	
	// decrease the LED brightness
	for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
		// changing the LED brightness with PWM
		ledcWrite(ledChannel, dutyCycle);
		delay(15);
	}
}


3개의 LED 에 PWM 을 거는 소스 입니다.

2개 pin 에는 실제로 LED 를 연결해서 dimming 을 확인하고, 나머지 하나의 pin 에는 오실로스코프를 연결하여 파형을 확인해 봅니다.



이론과 실험이 만나는 동영상 입니다.



당연히 이렇게 될 것이라 알지만, 너무 이쁘게 그래프가 나와서 조금 놀랬습니다. Frequency 와 Duty 값이 정확하게 측정되는 군요.

ESP32 의 PWM 기능은 완벽하군요.




5. Piezo 연결


원리는 같으나, 눈으로 보면 다르게 느껴지는 Piezo 스피커도 확인해 보았습니다. 아래 링크를 참조하였습니다.


* ESP32 Arduino: Controlling a buzzer with PWM
    - https://techtutorialsx.com/2017/07/01/esp32-arduino-controlling-a-buzzer-with-pwm/


PWM 동작하는 GPIO 하나 잡아서 연결하면 됩니다.



저는 piezo 를 D4 = GPIO4 에 연결했습니다.


int freq = 2000;
int channel = 0;
int resolution = 8;
  
void setup() {
	Serial.begin(115200);
	ledcSetup(channel, freq, resolution);
	ledcAttachPin(4, channel);
}
  
void loop() {
	ledcWriteTone(channel, 2000);
	for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle = dutyCycle + 10) {
		Serial.println(dutyCycle);
		ledcWrite(channel, dutyCycle);
		delay(1000);
	}
	
	ledcWrite(channel, 125);
	for (int freq = 255; freq < 10000; freq = freq + 250){
		Serial.println(freq);
		ledcWriteTone(channel, freq);
		delay(1000);
	}
}


처음에는 2000Hz 에서 duty 사이클을 10씩 증가하는 소리이고,

그 다음은 duty 를 125로 고정한 다음, 주파수만 바꿔가면서 소리를 내는 소스 입니다.





FIN


지금까지 ESP32 의 내부 기능들을 확인해 봤습니다.

확인하지 않고 남아있는 기능들이 Deep Sleep 과 Hardware Acceleration (Cryptographic) 정도가 남았네요.


And

Hardware | ESP32 의 internal sensor 확인해 보기

|

지금까지 ESP32 에 대한 글은 아래 링크들에서 확인해 보세요.


* Hardware | ESP32 의 Dual core 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-Dual-core

* Hardware | ESP32 스펙 확인해 보기
    - https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check

* Hardware | ESP32 간단 사용기
    - https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32

이번 글은, ESP32 에 내장된 센서들에 관한 이야기 입니다.




1. 온도 센서


첫 번째로 온도센서에 대해 확인해 봅니다.

내장 센서이다 보니, 회로를 구성할 필요 없이, internal 값만 찾아서 확인해 보면 됩니다.


아래는 소스 입니다.


/* 
 *  ESP32 Internal Temperature Sensor Example
 */
 
 #ifdef __cplusplus
  extern "C" {
 #endif
 
  uint8_t temprature_sens_read();
 
#ifdef __cplusplus
}
#endif
 
uint8_t temprature_sens_read();
//====================================================
//         Setup
//====================================================
void setup() {
  Serial.begin(115200);
}
 
//====================================================
//         Loop
//====================================================
void loop() {
  Serial.print("Temperature: ");
  
  // Convert raw temperature in F to Celsius degrees
  Serial.print((temprature_sens_read() - 32) / 1.8);
  Serial.println(" C");
  delay(1000);
}


내부 함수 "temprature_sens_read()" 를 사용하여 내부 온도 센서 값을 읽어오는 루틴 입니다.



음? 뭔가 이상하군요. 계속 53.33 도씨만을 리턴합니다. 구글링 해봅니다.


* ESP32 internal temperature sensor not working #2422

https://github.com/espressif/arduino-esp32/issues/2422


아... CPU 버전업 되면서, 온도센서가 없어졌네요. -_-;



어쩐지, 예전 CPU diagram 에는 온도센서가 존재하지만,



최신 spec. 문서에는 Temperature sensor 가 없어지고, Embedded Flash 가 대신 들어가 있습니다.



그리하여, Internal temperature sensor 의 값을 불러오면, 정해진 값 - 128 - 만 리턴하게 만들어 진 것을 확인할 수 있었습니다.



2. Hall 센서


자력을 측정하는 센서 입니다. 보통 전류 센서에도 붙어 있죠.



Examples > ESP32 > HallSensor



소스를 보면, "hallRead()" 를 통해서 자력 값을 읽을 수 있습니다.


//Simple sketch to access the internal hall effect detector on the esp32.
//values can be quite low. 
//Brian Degger / @sctv  

int val = 0;
void setup() {
	Serial.begin(115200);
}

void loop() {
	// put your main code here, to run repeatedly:
	val = hallRead();
	// print the results to the serial monitor:
	// Serial.print("sensor = ");
	Serial.println(val); // to graph
	
	delay(50);
}


마침 가지고 있던 네오디뮴 자석으로 확인해 봅니다.



양의 값 (+) 와 음의 값 (-) 이 잘 읽힙니다.

개인적으로는 이 센서가 ESP32 에 필요한 이유는 잘 모르겠습니다. 누가 아시는 분은 댓글로 남겨 주세요.



동영상도 올려 봅니다.





3. 터치 센서


ESP32 에는 터치 센싱을 하는 pin 이 10 개 달려 있습니다.



마찬가지로, 왜 이리 touch sensor 를 많이 가지고 있는지 의문이지만, 스펙상으로는 10개 입니다.

스펙 문서에는 10개로 되어 있지만, Pinheader 로는 9개만 보이네요.



아래처럼 터치를 읽어들이는 소스가 기본 제공됩니다.


Examples > ESP32 > Touch > TouchRead



이미 "touchRead()" 라는 함수가 라이브러리에 준비되어 있군요.


// ESP32 Touch Test
// Just test touch pin - Touch0 is T0 which is on GPIO 4.

void setup() {
	Serial.begin(115200);
	delay(1000); // give me time to bring up serial monitor
	Serial.println("ESP32 Touch Test");
}

void loop() {
	Serial.println(touchRead(T0)); // get value using T0
	delay(1000);
}


결과는 다음과 같이 표시 됩니다. 터치되지 않은 상태는 95 정도이고, 해당 pin 을 손으로 만지면 10 이하로 내려갑니다.



해당 pin 을 터치했을 때, 내장 LED 의 불을 켜주는 소스 입니다. touchRead 의 기준 값은 50 으로 설정되어 있습니다.


// ESP32 Touch Test
// Just test touch pin - Touch0 is T0 which is on GPIO 4.

int LED_BUILTIN = 2;

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  
  Serial.begin(115200);
  delay(1000); // give me time to bring up serial monitor
  Serial.println("ESP32 Touch Test");
}

void loop() {
  int x = 0;
  Serial.println( x = touchRead(T0) ); // get value using T0
  //delay(1000);
  
  if( x < 50 ) {
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println("Turn on LED");
    delay(500);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Turn off LED");
    delay(500);
  }
}


주의할 것은, LED_BUILTIN 값은 보통 arduino 들은 자동으로 잡아 주지만, IDE 에서 board 선택을 ESP32 Dev Module 로 하면 제대로 동작하지 않습니다. 아마 DOIT ESP32 DEVKIT V1 은 LED_BUILTIN 이 정의되어 있지만, ESP32 Dev Module 은 해당 정의가 없는 듯 합니다.


ESP32 의 내장 LED 는 digital pin 2 이므로, "int LED_BUILTIN = 2" 를 선언해 주면 됩니다.



동영상도 올려 봅니다.





FIN


Arduino 와 비교하여 CPU 도 넘사벽이지만, 내장 센서도 다채롭게 구비되어 있는 ESP32 테스트 해 봤습니다.

다음 글에서는 ESP32 의 PWM 기능에 대해 확인해 보도록 하겠습니다.


And

Hardware | CO2 센서인 MH-Z14A 를 활용해 보자

|

1. 이산화탄소


이산화탄소는 지구로 들어 왔다가 빠져나가는 태양광 복사열을 차폐하여 온실효과를 내는 주범 입니다.

매년 기온이 상승하고 있습니다. 기온 상승으로 인하여 지구에서는 지금까지 겪지 못했던 일들이 일어나고 있죠.


전 지구적으로 본다면, 과거로부터 CO2 의 농도 변화는 일정한 주기를 가져 왔습니다.



하지만, 현재의 CO2 농도는 과거의 주기적인 범위에서 한참을 벗어나 있습니다.

측정 데이터를 가지고 본다면, 산업혁명 이후 꾸준히 증가 중 이라는 것을 알 수 있습니다.



산업혁명 이전은 280 ppm 이하였고, 그 이후 약 300년 사이에 140 ppm 정도 늘었습니다.

옛날과 비교하면 150% 가 되어있는 셈 입니다. 수백만년동안 일정한 주기를 가지던 패턴이 300년 동안 완전히 붕괴된 것이죠.


예전 개그 프로에서 봤던, 공기좋은 알프스에서 채집한 공기를 깡통에 넣어 팔아도 되는 시대가 올지도 모르겠습니다.



참고로, 현재 우리가 살고 있는 "요즈음" 은, 410 ppm 정도가 일반적인 수치임을 위의 그래프를 통해 알 수 있습니다.




2. MH-Z14A


생활 공간의 쾌적한 조성은, 삶에 있어서 행복감을 줄 수 있는 요소 중 하나 입니다.


이를 위해, 산소 발생기를 만들어 볼 생각이 났습니다.

다만, 산소 발생기를 만들더라도, 현재의 상황 - 농도 - 를 알고 있어야 조정이 가능하니, CO2 측정 방법을 찾아 봅니다.


CO2 센서로는 몇 가지가 존재하나, MH-Z14A 라는 것이 심심치 않게 사용되고 있네요.

거의 2만원이 넘는 가격이지만, 구입합니다. 기체 포집 센서들은 꽤나 가격이 높게 형성되어 있습니다.


* Free shipping NDIR CO2 SENSOR MH-Z14A infrared carbon dioxide sensor module,serial port, PWM, analog output with cable MH-Z14

https://www.aliexpress.com/item/Free-shipping-NDIR-CO2-SENSOR-MH-Z14A-infrared-carbon-dioxide-sensor-module-serial-port-PWM-analog/32617820781.html



센서의 스펙은 다음과 같습니다.


Product Name: MH-Z14A infrared type, carbon dioxide detection sensor
1. the working voltage: DC 4.5-5.5V
2. Working current: Mean < 60mA; peak 150mA
3. the detection range: 0-5000ppm
4. the detection accuracy: ± (50ppm + 3% reading value)
5. Warm-up time: 3min
6. the output signal:
   1) analog output voltage: (D1 port 0V-2.5V) (D2 port 0.4-2V) linear output
   2) serial port (UART) (TTL level)
   3) PWM
7. response time: T90 < 120s
8. the working temperature: 0-50C
9. Humidity: 0-95% RH
10. life: 5 years
11. size: 57mm X 35mm X 15mm
12. weight size: 17g

Package Including: 1pcs X CO2 sensors



3. 도착


도착샷은 예의.



평범하게 배달.



리본 케이블이 딸려 있습니다만, pin hole 로도 연결이 가능합니다.



뒷면은 레귤레이터와 신호 처리 chip 이 달려 있습니다. 그리고 방수 코팅도 되어 있네요.





4. 통신 과 연결 방법


메뉴얼과 스펙 문서를 첨부합니다.


mh-z14a_co2-manual-v1_01.pdf

mh-z14_co2.pdf


문서를 보니, 이 센서와 통신할 수 있는 방법은 3가지가 됩니다. 각각의 사용법은 밑에서 다뤄 보겠습니다.


Analog

PWM

UART (RX/TX)


Pin header 정보 입니다.



리본 케이블을 사용할 경우, 각 선의 의미는 아래 그림과 같습니다.



새로 납땜해야 하는 pin header 말고, 리본 케이블을 사용하여 깔끔하게 연결해 보도록 하겠습니다.



5. 리본 케이블용 커넥터


리본 케이블을 이용하여 예쁘게 연결하고 싶으니, 리본 케이블 커넥터나 연장을 생각해 봅니다.

일단 측정해 봅니다. 대략 1mm 정도 되겠네요. 아래 규격일 듯 합니다.


* JST SH 1.0mm

- http://www.jst-mfg.com/product/detail_e.php?series=231



알리에서 검색해 보니, 아래 제품이 맞을 듯.

* 10 sets 1.0mm 1.25mm 1.5mm 2.0 2.54mm 2PIN /3/4/5/6/12P Pin Male & Female PCB Connector SH JST ZH PH XH 2 Pin
    - https://www.aliexpress.com/item/32733307616.html



잘 도착 했습니다.



커넥터의 female / male 이 짝으로 도착했습니다.



도착한 커넥터와 센서에 딸려 나온 커넥터를 비교해 보니... 덴장.

기존 커넥터의 pin 피치를 비교해 보면, 좀더 조밀합니다.



빵판이나 일반적인 연결 용도로 사용되는 (2.54mm) Pin connector 도 주문 했더랬습니다.

빵판에 prototype 회로를 만드려면, pin 이 필요하니까요.


* 100PCS 2.54mm Dupont Jumper Wire Cable Housing Female/Male Pin Connector Terminal Kit
    - https://www.aliexpress.com/item/32908083223.html



암수 모양 한세트를 주문 했습니다.



이걸 제대로 사용하기 위해선는 찝는 툴이 필요하다는 것을, 물건 받아보고 나서야 깨닫습니다.



한가지 아쉬운건, 캐스팅 된 핀이 아니라, 프레스된 철판을 구부려서 만든 모양 입니다.



프레스로 된 pin 은 잘 구부러질 뿐만 아니라, 빵판 안에서 부러져 버리면 꺼낼 수가 없어, 죽은 소켓이 되어버리기 때문입니다.
(그래 뽰자 큰 영향은 없지만...)




6. 연결은 결국...


결국 빵판에 연결 방법으로는, 선 끝을 자르고, 기존 pin 을 이식하는 것으로 정했습니다.



Female 소켓에서 한 땀 한 땀 분리합니다.



혹시? 하고 이것 그대로 직접 연결하면 어떨까 하여 연결해 보니, 진동에 의한 결선 이탈이 쉽게 일어나므로 포기.



원래 생각했던 대로, pin 달린 jumper 에서 pin 만을 잘라 이식합니다.



선을 서로 꼬아준 다음, 납땜해 주고, 수축튜브 이용하여 마무리 했습니다. (완벽)



드디어 arduino 와 연결하여 측정이 가능하게 되었습니다.




7. UART


우선 아래 blog 를 많이 참고했습니다.


* Dr. Monk's DIY Electronics Blog

- http://www.doctormonk.com/2018/03/review-and-test-of-mh-z14a-ndir-co2.html


다만, 위의 링크에서 제시한 UART (Software Serial) 포트를 Arduino 의 digital pin 로 측정하는 것은 잘못된 방법입니다.

Analog pin 에 RX/TX 를 접속 시켜야 하며, PWM / Analog 입력을 동시에 받으면, 모든 값이 뒤틀립니다.

그래서 UART 따로, PWM / Analog 를 따로 측정해 봤습니다. (많은 삽질의 결과)


우선 UART. 센서에 3.3V 를 먹이고, TX/RX 를 analog pin 에 연결하여 데이터를 받습니다.



처음으로 센서를 동작시켜본 기념으로 동영상을 올립니다.

센서 가동중에는 네 귀퉁이에 어렴풋이 불이 켜졌다가 꺼지기를 반복합니다.


참고로, TX / RX 에 접속시킨 채로 sketch 를 upload 하면 error 가 나는군요.


#include "SoftwareSerial.h"

const long samplePeriod = 10000L;

SoftwareSerial sensor(A2, A3); // RX, TX
const byte requestReading[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte result[9];
long lastSampleTime = 0;

void setup() {
  Serial.begin(9600);
  sensor.begin(9600);
}

void loop() {
  long now = millis();
  if (now > lastSampleTime + samplePeriod) {
    lastSampleTime = now;
    int ppmS = readPPMSerial();
    Serial.println(ppmS);
  }
}

int readPPMSerial() {
  sensor.flush();
  for (int i = 0; i < 9; i++) {
    sensor.write(requestReading[i]); 
  }
  
  while (sensor.available() < 9) {}; // wait for response
  for (int i = 0; i < 9; i++) {
    result[i] = sensor.read(); 
  }
  int high = result[2];
  int low = result[3];
  return high * 256 + low;
}


결과 입니다. UART 는 값 보정이 필요해 보입니다.


보정을 위해서는 기준값을 알아야 하는데, 기준값을 도출하기 위해서는 정확히 조성된 환경에서 보정작업이 이루어져야 합니다.

저는 그런 환경이나 챔버가 없으므로, calibration 은 무시.





8. Analog / PWM


그나마 현실적인 값을 도출하는 Analog 와 PWM 값 확인 입니다.


#include "SoftwareSerial.h"

const int analogPin = A0; // analog pin
const int pwmPin = 6; // digital pin

const long samplePeriod = 10000L;

long lastSampleTime = 0;

void setup() {
  Serial.begin(9600);
  pinMode(pwmPin, INPUT_PULLUP);
}

void loop() {
  long now = millis();
  if (now > lastSampleTime + samplePeriod) {
    lastSampleTime = now;
    int ppmV = readPPMV();
    int ppmPWM = readPPMPWM();
    Serial.print(ppmV); 
    Serial.print("\t"); 
    Serial.println(ppmPWM); 
    }
}

int readPPMV() {
  float v = analogRead(analogPin) * 5.0 / 1023.0;
  int ppm = int((v - 0.4) * 3125.0);
  return ppm;
}

int readPPMPWM() {
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high
  long t0 = millis();
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  long t1 = millis();
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high again
  long t2 = millis();
  long th = t1-t0;
  long tl = t2-t1;
  long ppm = 5000L * (th - 2) / (th + tl - 4);
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  delay(10); // allow output to settle.
  return int(ppm);
}


결과값은 다음과 같습니다. 왼쪽이 Analog 값, 오른쪽이 PWM 입니다.



위에서 알 수 있듯, 값의 변화나 지구의 CO2 농도를 참고했을 때, PWM 이 좀더 현실적인 값이 아닌가 합니다.




9. WiFi 연결


일반적으로 센서를 가지고 변화 추이를 확인하려면, 상당히 긴 시간동안의 데이터를 수집해야 합니다.

지금까지는 PC를 켜 놓고 Arduino IDE 의 Serial Monitor 를 사용하여 측정 했었습니다.


Cloud 시대인 만큼, 이번에는 WiFi 를 이용하여 ThingSpeak 에 측정 데이터를 보내주기로 합니다.

ThingSpeak 등록 및 기본 사용법은 아래 포스트에서 다뤘습니다.


* Software | ThingSpeak 등록하여 IoT 데이터 펼처보기
    - https://chocoball.tistory.com/entry/Software-ThingSpeak-IoT-monitoring


힘들었던 것은, ESP-01 의 WiFi command 를 이용하여, 필요한 command 를 하나씩 확인하는 작업이었습니다.



위 스샷은 "AT+CIPMUX" 를 통하여 single channel / multi channel 통신을 정의하는 것 입니다.

값에 "0" 을 정의하면 single 이고, 1~4 숫자면 multi channel 입니다.


참조한 블로그 처럼, Multi Channel 을 이용하면 좋을 듯 하지만,

다른 명령어에서 channel 번호를 명시해야 하는 등 번거로워서 Single Channel 설정으로 "AT+CIPMUX=0" 이용.


이외 명령어들은, 연결할 호스트 정의 및 HTTP data 전송에 관련한 부분입니다.


AT+CIPMUX=0
AT+CIPSTART="TCP","api.thingspeak.com",80
AT+CIPSEND=49
GET /update?api_key=XXXXXXXXXXXXXXXX?field1=351
AT+CIPCLOSE


- AT+CIPMUX=0 > Single Channel 로 통신 시작

- AT+CIPSTART="TCP","api.thingspeak.com",80 > 연결할 host 명과 port 정의
- AT+CIPSEND=49 > 전송할 데이터 사이즈를 미리 정의
- GET /update?api_key=XXXXXXXXXXXXXXX?field1=351 > HTTP GET request
- AT+CIPCLOSE > session close



위는 FTDI 를 이용하여, 직접 Serial Monitor 를 이용하여 WiFi 통신"만" 테스트해 보는 스샷입니다.

FTDI 를 이용한 자세한 활용 방법은 아래 글에서 다뤘습니다.


* Hardware | ESP-01 or ESP8266 사용기 - 2
    - https://chocoball.tistory.com/entry/Hardware-ESP01-or-ESP8266-using-2


참고한 blog 의 소스에는 ">" 캐릭터가 나오면 send 명령어를 실행하게끔 되어 있습니다만, 실패가 계속 나더군요.

테스트 해본 결과, 제가 가지고 있는 ESP-01 모듈은 ">" 이 나오기 전, "OK" 가 먼저 뜨므로, 기준을 "OK" 문자로 해야 합니다.


이렇듯, 소스를 하나하나 검증하면서 제대로 동작하는 command 들을 끼워 맞추기까지 오래 걸렸습니다.

없는 시간 쪼개어 가며 테스트하고 삽질하였더니만 2개월 정도 걸린 듯 합니다.




10. 최종 버전


아래는 arduino / MH-Z14A / ESP-01 간의 pin 연결표 입니다.


 MH-Z14A | Arduino Nano
------------------------
   PWM   |     D6
   GND   |     GND
   VCC   |     5V
------------------------


  ESP-01 | Arduino Nano
------------------------
   TX    |     D10
   RX    |     D11
   VCC   |     3.3V
   GND   |     GND
  CHPD   |     3.3V
------------------------


아래는 layout 입니다. 추가 전원을 위해 MB102 도 사용했습니다.



아래는 실제로 연결한 arduino nano / MH-Z14A / ESP-01 / MB102 입니다.



지금까지 확인한 내용이 모두 담긴 소스 입니다.


#include "SoftwareSerial.h"

// HM-Z14A
const int pwmPin = 6; // digital pin

// ESP-01
#define RX 10
#define TX 11
SoftwareSerial AT(RX, TX);

// WiFi
String ssid = "XXXXXXXXX"; //Wifi SSID
String password = "XXXXXXXXX"; //WiFi Pass
String apiKeyIn = "XXXXXXXXX"; // API Key
const unsigned int writeInterval = 25000; // write interval (in ms)

// ThingSpeak
String host = "api.thingspeak.com"; // API host name
String port = "80"; // port

int AT_cmd_time;
boolean AT_cmd_result = false; 

void setup() {
  Serial.begin(9600);
  pinMode(pwmPin, INPUT_PULLUP);
  
  // WiFi status
  Serial.println("---------- Program Start");
  AT.begin(115200);
  Serial.println("Initiate AT commands with ESP8266 ");
  sendATcmd("AT",5,"OK");
  sendATcmd("AT+CWMODE=1",5,"OK");
  Serial.print("Connecting to WiFi:");
  Serial.println(ssid);
  sendATcmd("AT+CWJAP=\""+ ssid +"\",\""+ password +"\"",20,"OK");
}

void loop() {
  // get CO2 data
  int ppmPWM = readPPMPWM();
  
  // Create the URL for the request
  String url = "GET /update?api_key=";
  url += apiKeyIn;
  url += "&field1=";
  url += ppmPWM;
  url += "\r\n";
  Serial.println("---------- Open TCP connection");
  sendATcmd("AT+CIPMUX=0", 10, "OK");
  sendATcmd("AT+CIPSTART=\"TCP\",\"" + host +"\"," + port, 20, "OK");
  sendATcmd("AT+CIPSEND=" + String(url.length()), 10, "OK");
  
  Serial.print("---------- requesting URL: ");
  Serial.println(url);
  AT.println(url);
  delay(2000);
  sendATcmd("AT+CIPCLOSE", 10, "OK");
  
  Serial.println("---------- Close TCP Connection ");
  Serial.println("");
  
  delay(writeInterval); // delay
}

// PWM function
int readPPMPWM() {
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high
  long t0 = millis();
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  long t1 = millis();
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high again
  long t2 = millis();
  long th = t1-t0;
  long tl = t2-t1;
  long ppm = 5000L * (th - 2) / (th + tl - 4);
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  delay(10); // allow output to settle
  return int(ppm);
}

// sendATcmd
void sendATcmd(String AT_cmd, int AT_cmd_maxTime, char readReplay[]) {
  Serial.print("AT command:");
  Serial.println(AT_cmd);
  
  while(AT_cmd_time < (AT_cmd_maxTime)) {
    AT.println(AT_cmd);
    if(AT.find(readReplay)) {
      AT_cmd_result = true;
      break;
    }
    
    AT_cmd_time++;
  }
  
  Serial.print("...Result:");
  if(AT_cmd_result == true) {
    Serial.println("DONE");
    AT_cmd_time = 0;
  }
  
  if(AT_cmd_result == false) {
    Serial.println("FAILED");
    AT_cmd_time = 0;
  }
  
  AT_cmd_result = false;
}


참조한 blog 에서는, URL 을 만들 때, 단순히 4 bytes 를 추가한 size 를 CIPSEND 하라고 했지만, 제대로 동작하지 않습니다.

Line feed / carriage return 을 GET method 뒤에 추가되어야 정상 동작 합니다. (아래 소스의 제일 마지막 줄)


...
  String url = "GET /update?api_key=";
  url += apiKeyIn;
  url += "&field1=";
  url += ppmPWM;
  url += "\r\n";
...

성공한 결과물을 Serial Monitor 로 확인해 보면 다음과 같습니다.

WiFi 연결 및 HTTP Get method 로 REST API 동작을 확인 할 수 있어요.





11. 결과


ThingSpeak 사이트에서 확인한 결과 입니다.



거실에 설치 후, 외출하면서 CO2 농도가 떨어짐.

외출에서 귀가하면서 농도가 한번 급등하고, 그 후에 지속적으로 오름.

취침시간을 기점으로 점점 떨어지다가 기상과 더불어 다시 올라가는 그래프를 보여 줬습니다.


우리 집은 CO2 농도가 꽤나 높은 것으로 나오네요.




12. Update - 20200328


약 10일간 측정한 데이터 입니다.


feeds.csv



잘못 들어간 쓰레기 값들을 조금 조정했습니다. 전원 문제도 있고, 빵판의 접점 문제 등으로 가끔 쓰레기값이 나오는 듯 해요.



5일 그래프를 겹쳐 봤습니다.

1000 ppm 이상의 값에 대해 비정상임을 의심해 봤으나, 전체적으로 보면 정상 수치임을 알 수 있습니다.

저녁에 가족 4명이 거실에 있으면, 대략 1300대의 값을 보여 줬습니다. 모두 잠든 새벽에는 400 언저리 수치를 보여줘, 전 지구의 값과 동일하다는 것을 확인 할 수 있었어요.


요일별로 늦게 일어나는 주말에는 점심 언저리부터 값이 증가하고, 외출해 있을 때에는 거의 값의 변화가 없었으며, 저녁 12시 취침시간을 기점으로 아침 기상까지 수치가 떨어지는 그래프를 보여 줬습니다.


산소 발생기와 연동한다면, 새벽 외에는 하루 종일 틀어놔야 겠군요. 물론, 400 수치로 돌아오면 멈추는 루틴이 필요하겠지만.



And

Hardware | 8x8 LED matrix 와 Colorduino 이용해 보기

|

1. 8x8 LED Matrix


한동안 LED bar 나 LED 전구, 74HC595 등을 사용하다가,

"FULL COLOR 8x8 LED Dot Matrix" 라는 문구를 보게 됩니다.


때는 바야흐로 2017년 5월 24일...

아래 제품을 구입하게 됩니다.


* Full Color 8x8 8*8 Mini Dot Matrix LED Display Red Green Bule RGB Common Anode Digital Tube Screen For Diy 60mmx60mmx5mm

https://www.aliexpress.com/item/5mm-8x8-8-8-Full-Colour-RGB-LED-Dot-Matrix-Display-Module-Common-Anode/32452391556.html



정말 이쁘게 생겼죠?




2. 도착


큰 무리 없이 잘 도착 했습니다.



dot 의 한개씩 자세히 보면, 조그마한 3가지 LED가 하나의 dot 를 이룹니다.



우리가 흔히 알고 있는 3색 - 빨강, 파랑, 녹색이 모든 색을 표현하는 원리를 이용하는 구조로 생각할 수 있습니다.



핀이 많은 것을 보면, full color 임은 확실해 보입니다.

단색일 경우는 아래 보이는 pin 수보다 훨씬 적습니다.



자... 그럼 arduino 와 어떻게 연동될까요.

인터넷 바다에서 검색에 검색을 거듭합니다.





3. 구현 방법


RGB 를 섞어 색을 만들며, 색의 변화를 컨트롤 하는 주된 기능은 "Pulse Width Modulation" 이라고 합니다.

한국에서는 "펄스 폭 변조" 라는군요. (그냥 직역이지 않소...)


* Pulse-width modulation

https://en.wikipedia.org/wiki/Pulse-width_modulation


SparkFun 에서도 관련한 설명을 해 놓은 web page 가 있어서 여기에 링크를 걸어 놓습니다.


* Pulse Width Modulation

https://learn.sparkfun.com/tutorials/pulse-width-modulation/all


간단히 이야기 하면, 펄스의 "" 만을 조정하여, 빛의 강약이나 모터의 구동 속도를 조절하는 것입니다.

눈으로 보기에는 자연스러운 흐름이지만, 주파수적으로는 끊어서 조정하는 방법이라고 합니다.



"난, arduino 를 가지고 놀려고 했는데, 공부를 해야 하는군" 이라는 생각을 다시금 깨우쳐 주는 대목입니다.


자, 그래서 8x8 led dot matrix 를 구동하려면 어떤 선례들이 있는지 찾아보니, 잘 정리된 글들이 대략 다음과 같군요.

결론부터 이야기 하면, 필자도 이런 이야기를 합니다.


"Pulse width modu - WHAT ?"


읽어보면, 결국 74HC595 + ATmega328 등을 이용하여, 전용 breakout 보드를 만들어서 컨트롤 하고 있었습니다.
이게 단순한게 아니었구나...

* 8×8 RGB LED Matrix

http://blog.spitzenpfeil.org/wordpress/projects/8x8-rgb-led-matrix/


* 64 Pixel RGB LED Display - Another Arduino Clone

- https://www.instructables.com/id/64-pixel-RGB-LED-Display-Another-Arduino-Clone/


* How to Build a 8×8 RGB LED Matrix with PWM using an Arduino

http://francisshanahan.com/index.php/2009/how-to-build-a-8x8x3-led-matrix-with-pwm-using-an-arduino/


* nrj/LEDMatrixControl

https://github.com/nrj/LEDMatrixControl


여기까지 하려면 시간이 많이많이많이 걸리겠는걸... 라고 생각 후, 일단 덮고 다른걸로 한동안 시간을 보내게 됩니다.





4. Colorduino


시간이 흘러 흘러 1년...



우연히 Colorduino 라는 제품의 존재를 알게 됩니다.


알게 된지는 꽤 되었지만, 직접 breakout 보드를 만들어 보고자 무시해 왔지만, 너무 일이 커지는듯 하여 포기하고,

1년이 훌쩍 지난 2018년 11월, 이 구동 driver 격인 breakout 보드 구입을 위해 조사하게 됩니다.


제조사는 ITead 라는 회사군요.


* ITEAD Intelligent Systems Co.Ltd.

https://www.itead.cc/


현재 Colorduino 는 version 1.4 까지 나와있는 듯 합니다.


* Colorduino V1.4 Color Rainbow Matrix RGB LED Driver Shield For Arduino

https://www.itead.cc/colorduino-v1-4.html


아래 스샷들은 제품 website 에서 가져온 내용인데,

지금까지 고민한 것들이 모두 구현되어 있는 모습을 보여주고 있습니다.



PWM 을 위해서 전용 chip이 채용되었군요.



컨트롤을 위해서 arduino 에서 사용하는 ATmega328 이 채용되었습니다.


그래서 Arduino IDE 와 FTDI 를 통해서 연결 시,

보드를 ATmega328 을 채용한 보드 - Uno, Duemilanove, Nano - 를 선택하면 문제가 없습니다.


관련된 library 및 example 소스는 WIKI 형식으로 정리가 되어 있습니다.


* Colorduino V1.3 (WIKI)

https://www.itead.cc/wiki/Colorduino_V1.3


* Colorduino V1.4 (WIKI)

https://www.itead.cc/wiki/Colorduino_V1.4


사용된 각 chip 의 datasheet 는 아래와 같이 이 post 에 첨부해 놓습니다.


* Datasheet

- Colorduino : DS_IM120410004_Colorduino.pdf

DM163 : DS_DM163.pdf

M54564FP : DS_M54564FP.pdf


* Fritzing Parts

Colorduino.fzpz





5. Colorduino / Funduino 구입


AliExpress 에서 검색하면, Colorduino 의 clone 제품인 "Funduino" 가 판매되고 있습니다.

잘 보면, Colorduino V1.3 버전을 기준으로 만든 제품입니다.


* Free shipping ! Full color 8 * 8 LED RGB matrix screen driver board Colorduino for arduino

https://www.aliexpress.com/item/Free-shipping-Full-color-8-8-LED-RGB-matrix-screen-driver-board-Colorduino-for-arduino/2045397138.html



위의 ITead 사이트의 V1.3 과 비교해 보면, 완벽히 동일하다는 것을 알 수 있습니다.





6. Funduino 도착


가격이 좀 있다 보니, 2주만에 도착했습니다.



뽁뽁이로 잘 쌓여서 도착했습니다. 믿음직 스러운 배송입니다.



상면샷 입니다. 깔끔하게 만들어져 있네요.



다시금 Colorduino 와 동일함을 느끼게 됩니다.



ATmega328 도 보이며, PWM 을 위한 DM163 도 보입니다.



뒷면에는 당당하게(?) Funduino v1.A 라고 마킹되어 있습니다.






7. 장착 및 FTDI 연결


우선 8x8 LED matrix 의 1번 pin (not 어뢰) 를 Funduino 의 1번 소캣에 맞추어 끼웁니다.



Dot matrix 에 딱 가려지는 크기 입니다. 잘 만들었네요.



FTDI 와 pin 연결은 다음과 같습니다.


  FTDI | Funduino
------------------
  DTR  |   DTR
  RX   |   TXD
  TX   |   RXD
  VCC  |   VDD
  GND  |   GND
------------------



실제로 FTDI 와 연결된 모양은 다음과 같습니다.

(한데 묶여있는 선 다발로 조금 지저분해 보이지만, 그건 오해입니다.)



일단, PC USB --> FTDI --> Funduino 를 연결하면, 미리 구워진 프로그램으로 구동됩니다.



동영상으로 찍어 봤습니다.



이쁘네요.





8. Arduino IDE 설정 및 Library 설치


Colorduino 는 기본으로 ATmega328 을 가지고 있으므로,

IDE 에서는 ATmega328 을 실장하고 있는 Arduino Nano / Uno / Duemilanove 어느것을 선택해도 됩니다.



최종적으로 소스 프로그램이 ATmega328 용으로 컴파일 되면 문제가 없으니까요.


Colorduino 의 Library 를 다운로드하여 등록합니다.



그러면 아래와 같이 example 을 로드할 수 있습니다.



아래는 Colorduino 의 Plasma 와, 문자를 스크롤 하는 Library 링크 입니다.

혹시 모르니, 실제 파일도 첨부해 놓습니다.


* Colorduino Library

https://github.com/Electromondo-Coding/Colorduino

Colorduino-master.zip


* Colorduino Scroller Library

https://github.com/Electromondo-Coding/ColorduinoScrollerLibrary

ColorduinoScrollerLibrary-master.zip


위의 Scroller 는 위의 Colorduino Library 와 서로 의존성을 갖습니다.


또다른 버전의 Colorduino Library 도 존재하는데, 그게 아래 링크 및 파일입니다.

위의 Library 와 비슷하지만, 좀더 PWM 이 부드럽게 동작하는 듯 합니다.


그래서, 아래 Colorduino Library 와 위의 Scroller Library 를 혼합하여 설치하면,

Scroller 가 동작하지 않으니 주의가 필요합니다.


* Colorduino Library

https://github.com/lincomatic/Colorduino/

Colorduino-master.zip





9. Plasma 와 Scroller


위의 두 example 을 구동시킨 동영상을 첨부합니다.

우선 Plasma 동영상 입니다.



Scroller 에서는 아래 처럼 text 를 수정하여, 원하는 text 를 뿌려줄 수 있습니다.



Scroller 의 동영상 입니다.






FIN


거의 1년 6개월 걸린, 8x8 LED Dot Matrix 의 동작확인이 이제야 끝났습니다.

뭔가 생산적으로 coding 을 해보고 싶었으나, example 소스를 보고 바로 접었습니다.


꼭 coding 을 해야 할 때가 되면 그때 하려구요.


And

Hardware | ATtiny85 를 사용해 보자 - 2

|

1. 준비물


ATtiny85 에 구동 program 를 업로드 하기 전에, 구성품 준비가 필요합니다.

관련해서는 다음에 링크된 이전글을 참고해 주세요.


* Hardware | ATtiny85 를 사용해 보자 - 1

http://chocoball.tistory.com/entry/Hardware-ATtiny85-1



참고로 ATtiny85 의 datasheet 는 다음과 같습니다.

Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf





2. 참조 사이트


ATtiny85 에 프로그램 업로드를 위해서는 몇가지 방법이 있지만,

저는 다음 사이트를 기준으로 참고하였습니다.


* Breathing life into the DigiSpark clone with ATtiny MCU

https://makbit.com/web/firmware/breathing-life-into-digispark-clone-with-attiny-mcu/




3. Arduino - ArduinoISP


자, 이제 준비가 완료 되었으니 이제 시작을 해 볼까요?


ATtiny85 에 프로그램을 심으려면,

바로 PC 와 USB를 통해 연결하면 안되고 "PC -- USB -- Arduino -- ATtiny85" 식으로 연결하여 프로그래밍 할 수 있습니다.


중간에 위치한 Arduino 를 브릿지 형식으로 사용하는 것입니다. 이름하야  In-circuit Serial Programmer (ISP) 라는군요.

이를 위해, Arduino 가 ISP 로 동작할 수 있도록 Arduino 에 ISP 프로그램을 업로드 해줍니다.


File > Examples > 11.ArduinoISP > ArduinoISP 



이걸로 Arduino 쪽의 준비는 끝났습니다.




4. Arduino IDE - Perferences


다음으로 IDE의 Preferences 메뉴로 이동하여 ATtiny85 보드를 Arduino 에서 인식시킬 수 있도록 관련된 library 를 인스톨 합니다.

이를 위하여, 다음 URL 중 하나를 Preferences 의 Boards Manager URLs 에 다음 중 하나를 선택하여 입력해 줍니다.


* ATtinyCore

- http://drazzy.com/package_drazzy.com_index.json

* Digispark

- http://digistump.com/package_digistump_index.json


File > Preferences > Additional Boards Manager URLs



보통은 Digispark 용을 사용해도 되나,

Spence Konde 라는 사람이 만든 ATTinyCore 는 좀더 정밀하게 설정할 수 있고, 문제 없이 동작한다고 합니다.


* Spence Konde

https://github.com/SpenceKonde/ATTinyCore


저는 Spence Konde 를 선택했습니다.




5. Arduino IDE - Board Manager


다음으로 ATtinyCore 라이브러리를 Board Manager 를 통해 설치합니다.


Tools > Board > Board Manager > search "ATtinyCore"



위의 과정까지 거치면, 아래처럼 Board 에서 ATtiny85 를 선택할 수 있습니다.





6. Arduino IDE - Programmer


이제 마지막으로 운용할 Programmer 를 선택합니다.

이미 우리는 Arduino 를 ISP 로 사용할 것을 알기에, "Arduino as ISP" 를 선택하면 됩니다.


ATtinyCore 라이브러리가 설치되어 있으므로, "Arduino as ISP (ATtinyCore)" 를 선택해도 됩니다만,

왠지 default 로 제공되는 것을 사용하고 싶어서 "Arduino as ISP" 를 선택했습니다.


어느쪽을 선택해도 문제없을것 같아요.



이제 Arduino IDE 에서의 준비는 끝났습니다.




7. Layout


ATtiny85 의 Pin 배열은 다음과 같습니다.



위의 그림을 참조하여 Arduino 와 ATtiny85 를 다음과 같이 연결합니다.


  ATtiny85  | Arduino Nano
---------------------------
    VCC     |     5V
    GND     |     GND
    Reset   |     D10
    Pin 0   |     D11
    Pin 1   |     D12
    Pin 2   |     D13
---------------------------


추가로 Arduino 의 GND/RESET 에 10uF 캐패시터를 연결해 줍니다.

이유는 Arduino 가 ATtiny85 에 업로드 할 때, auto reset 을 방지하기 위함이라 합니다.



실제 모양은 다음과 같아요.

그간 고생해서 얻은 "DIP to SOIC converter" 가 빛을 발하는 순간입니다.



이제 물리적인 구성은 완료 되었습니다.




8. sketch


소스는 아래 link 를 참조하였습니다.

깜빡이 간격이 좀 짧은것 같아 increment / decrement 를 5에서 1로 변경했습니다.


* Attiny85 blink fade for loop 3 LED

https://codebender.cc/sketch:354605


해당 소스는 단순히 LED 를 깜빡이게 하는것 외에도,

PWM (Pulse With Modulation) 기법을 이용하여 LED 점등을 fade 효과를 낸 것입니다.


/*
This code will allow you to mix fading and blinking on 3 LEDs
Jill Dawson
*/

int Pin0 = 0;	// LED connected to pwm pin 0, which can blink or fade
int Pin1 = 1;	// LED connected to pwm pin 1, which can blink or fade
int Pin2 = 2;	// LED connected to digital pin 2, which can only blink

void setup() {
	// nothing happens in setup
	// declares pins as outputs
	
	pinMode (0, OUTPUT);
	pinMode (1, OUTPUT);
	pinMode (2, OUTPUT);
} 

//Loop repeats
void loop() {
	// fades pin 0 in from min to max in increments of 5 points
	for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=1) {
		// sets the value (range from 0 to 255):
		analogWrite(Pin0, fadeValue);
		// wait for 30 milliseconds to see the dimming effect
		delay(30);
	}
	
	// fades pin 0 out from max to min in increments of 5 points
	for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
		// sets the value (range from 0 to 255):
		analogWrite(Pin0, fadeValue);
		// wait for 30 milliseconds to see the dimming effect
		delay(30);
	}
	
	// blinks LED 1 three times
	{
		digitalWrite (1, HIGH);	//turns pin 1 on
		delay (1000);			//waits for 1 second
		digitalWrite (1, LOW);	//turns pin 1 off
		delay (1000);			//waits for 1 second
		digitalWrite (1, HIGH);	//turns pin 1 on
		delay (1000);			//waits for 1 second
		digitalWrite (1, LOW);	//turns pin 1 off
		delay (1000);			//waits for 1 second
		digitalWrite (1, HIGH);	//turns pin 1 on
		delay (1000);			//waits for 1 second
		digitalWrite (1, LOW);	//turns pin 1 off
	}
	
	// blinks LED 2 three times
	{
		digitalWrite (2, HIGH);	//turns pin 2 on
		delay (1000);			//wait for 1 second
		digitalWrite (2, LOW);	//turns pin 2 off
		delay (1000);			//waits for 1 second
		digitalWrite (2, HIGH);	//turns pin 2 on
		delay (1000);			//wait for 1 second
		digitalWrite (2, LOW);	//turns pin 2 off
		delay (1000);			//waits for 1 second
		digitalWrite (2, HIGH);	//turns pin 2 on
		delay (1000);			//wait for 1 second
		digitalWrite (2, LOW);	//turns pin 2 off
		delay (1000);			//waits for 1 second
	}
	
	// fades pin 1 out from max to min in increments of 5 points
	for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=1) {
		// sets the value (range from 0 to 255):
		analogWrite(Pin1, fadeValue);
		// wait for 30 milliseconds to see the dimming effect
		delay(30);
	}
	
	// fades out from max to min in increments of 5 points:
	for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
		// sets the value (range from 0 to 255):
		analogWrite(Pin1, fadeValue);
		// wait for 30 milliseconds to see the dimming effect
		delay(30);
	}
}


Arduino IDE 에서 업로드 합니다.



AVR Part 항목을 보면 ATtiny85 로 정확히 인식합니다.



ATtiny85 에 FLUSH 가 성공하면 위와 같이 문제없이 완료가 됩니다.




9. 결과


짜잔~!





FIN


이제 ATtiny85 에 프로그램을 업로드 하여 standalone 으로 동작하는 것까지 확인할 수 있었습니다.


"ATtiny85 + DIP to SOIC adapter + USB board" 를 USB 에 직접 연결하여,

Arduino ISP 없이도 Digispark 처럼 프로그램을 업로드 할 수 있는게 목표므로, 다음 글에서 구성해 보도록 하겠습니다.


And

Hardware | ATtiny85 를 사용해 보자 - 1

|

1. 시작


Arduino Nano 를 사용하고 있지만, 실제 프로젝트 구성시 이렇게 많은 핀들이 필요할지 의문이 드는 때가 있습니다.

인터넷을 돌아다니다 보면, Arduino 본체는 아니지만 소스코드는 거의 동일하지만, chip 은 조그마한 것이 사용된 것을 볼 수 있습니다.


그 이름하야 ATtiny85.


Arduino Nano 에 들어가는 chip 이 Atmel 사의 MEGA328P 인데,

이 ATtiny85 의 생산회사도 Atmel 사 입니다.



이 ATtiny85 는 두가지 package 가 존재합니다.

하나는, 아래 보이느 DIP8 형태 (다리가 8개 벌레같은 칩) 의 ATtiny85.


'

다른 하나는, SOIC 형태 (납착한 보드 납땝식) 의 ATtiny85 입니다.



다리는 8개밖에 없는지라, power / ground 빼면 6개 이고, chip control 하는 1번을 빼면,

실제 가용할 수 있는 pin 수는 5개가 되겠군요.



실제 project 상에서 사용되는 Pin 수는 그리 많지 않기때문에,

간단한 sensor 를 이용하여 기능 구현할 때에는 Arduino 보단, 이런 ATtiny85 를 사용하는 것이 훨씬 경제적으로 보입니다.


- 장소 차지가 적다

- Power가 더 적게 먹을 것이다

- 기판 적층시 component 처럼 실장할 수 있다




2. 최종 모양


ATtiny85 에 프로그램을 upload 하기 위해서는, FTDI 나 Arduino ISP 모드를 이용하여 8k 자체 메모리에 업로드 해야 합니다.


위의 방법이 일반적이지만, 아래 그림의 처럼

직접 Arduino IDE 에서 프로그래밍 하여 USB를 통해 upload 할 수 있는, Digispark 모양으로 만들고 싶었습니다.


* Digispark

http://digistump.com/products/1



클라우드펀딩인 Kickstarter 에서도 큰 방향을 일으킨 제품입니다.


* Digispark - The tiny, Arduino enabled, usb dev board!

https://www.kickstarter.com/projects/digistump/digispark-the-tiny-arduino-enabled-usb-dev-board



지금까지 내용을 정리하면 최종 모습은 아래와 같은 모양이 됩니다.


- SOIC 형태

- Digispark 처럼 USB 를 통한 개발 보드 형태


알리에서 검색해 보면, DIP 소켓 을 사용하여 Digispark 처럼 사용할 수 있게 해주는 보드가 있습니다.

그럼 위의 DIP 소켓을 SOIC 로 변환해주는 adapter 를 이용하면, 최종적으로 SOIC를 사용할 수 있겠군요.



이제 구상이 끝났으니 AliExpress 에서 구입을 진행해 봅니다.




2. 구입 - ATtiny85


먼저 SOIC 타입의 ATtiny85 를 구매합니다.


* Free Shipping 5PCS Original Integrated circuit parts ATTINY85 ATTINY85-20SU

https://www.aliexpress.com/item/Free-Shipping-5PCS-Original-Integrated-circuit-parts-ATTINY85-ATTINY85-20SU/32318325945.html



5개에 1만원 정도이니, 여타 다른 chip 들에 비하면 비싼 편 입니다.



릴 package 를 끊어서 보내줬군요.



확대해서 보면 Atmel 사의 TINY85 라고 쓰여 있습니다.



여기서 잠깐, 구매한 ATtiny85 는 20SU 라는 코드가 달려 있습니다.

제품사양을 보면 20SH 는 좀더 비싸고, 20SU 는 조금 더 쌉니다. 기호의 의미는 다음과 같다고 하네요.


– H: NiPdAu lead finish

– U: matte tin


U는 주석이고, H 는 NiPdAu (Nickel-Palladium-Gold) 로 납땜시, 더 확고하고 쉽게 접합된다고 합니다.



이렇게까지 전문적으로 아직 필요가 없으므로, 무난하게 20SU 버전으로 구입합니다.




3. 구입 - USB board


ATtiny85 chip 을 엊고 USB 를 연결하게 할 수 있는 보드를 구매합니다.


물론 ATtiny85 가 DIP 소켓에 꽂혀 있는 일체형이나 SOIC chip 이 납땜되어 있는 버전도 팔지만,

SOIC 를 바꿔 끼울 수 있도록 adapter 를 사용할 것이기에 깡통 USB 보드를 선택합니다.


* ATtiny13A/ATtiny25 /ATtiny45/ATtiny85 Pluggable Development Programming Bare Board

https://www.aliexpress.com/item/Free-Shipping-ATtiny13A-ATtiny25-ATtiny45-ATtiny85-Pluggable-Development-Programming-Bare-Board/32706943203.html



배송 포함 1천원정도니 부담없습니다.



잘 도착해서 보니, 기판 찌꺼기가 같이 붙어 있네요.

뭐 이 가격이니 이해할 수 있습니다.



뒷면입니다.



기판 찌꺼기는 펜치로 잡고 구부리면 쉽게 제거됩니다.



제거되면 기분이 뭔가 좋아집니다. 이런 즐거움도 선사해 주는군요.



나중에 adapter 가 도착하여 ATtiny85 와 채결할 때, 호기심으로 1번 핀을 거꾸로 채결해 봤더니 연기가 나더군요.

(나중에 안 사실이지만, 이 연기는 솔더링 잘되라고 도포된 약품이 레귤레이터 열로 증발한 것)


USB 연결시에 PC 에서 인식 못하는 문제가, 이 태워먹은 결과라고 잘못 생각하고 바로 2개를 추가로 주문했습니다.

(USB 를 연결하여 Digispark 처럼 사용하려면, 필요한 과정이 더 있슴)



총 3개가 되었군요.




4. 구입 - DIP to SOIC converter


USB 의 DIP 소켓을 SOIC 를 엊을 수 있게 하는 adapter / converter 를 주문합니다.


* SOIC8 SOP8 to DIP8 EZ Socket Converter Module Programmer Output Power Adapter With 150mil Connector SOIC 8 SOP 8 To DIP 8

https://www.aliexpress.com/item/SOIC8-SOP8-to-DIP8-EZ-Socket-Converter-Module-Programmer-Output-Power-Adapter-With-150mil-Connector-SOIC/32613388551.html



잘 도착했습니다.



포장도 잘 되어 있네요.



정전기 방지 포장까지 되어 있습니다.



위에서 누르면 가운데 금속 다리가 벌어져 SOIC chip 을 끼울 수 있는 구조 입니다.


음?

그렇습니다. USB 의 DIP socket 과 맞지 않습니다. (이런 고추장....)
또한 SOIC chip 을 끼우는 가운데 공간도 좁아, ATtiny85 도 들어가지 않습니다.

알고보니 150mil 규격이 아니라 200mil 을 구매해야 했던 것이였습니다.
ATtiny85 의 스펙을 살펴보면, chip 몸채가 5.18 mm 인걸 알수 있습니다. (아래그림 E1)


이것에 대응하는 것이 200mil SOIC 소켓 입니다.


Chip 규격이 다양한 만큼 알아야 할것도 많네요.

AliExpress 의 무료배송 마약때문에, 새로 구입한 converter 가 도착하려면 최소 2주 이상을 또 기다려야 합니다.

이거 하나 하는데 도대체 얼마나 기다려야 하는겨.


가끔 AliExpress 를 통한 놀이는 기다림이 대부분인것 같습니다.

기다리다가 열정이 식어버려, 다시 끌어올리는데 시간이 많이 걸리는 경우가 있어요.




5. 구입 - 또다른 DIP to SOIC converter


이제 정확한 제품 규격을 알게 되었으니 제대로 구입합니다.

제품명에 "Universal" 이라는 문구가 왠지 신뢰갑니다.


* Universal Adapter Sockets SOP8 SOP 8 to DIP8 DIP 8 for all Programmer 200 208 mil

https://www.aliexpress.com/item/Universal-Adapter-Sockets-SOP8-SOP-8-to-DIP8-DIP-8-for-all-Programmer-200-208-mil/32680997921.html



드.디.어! 정확한 converter 가 도착 했습니다.



대충 포장되어 있더라도 상관 없습니다.



아아아아아! 이거 얼마나 걸린거야.




6. 최종 모습


겨우 최종 결과 모습이 되었습니다.

별것도 아닌것 같은데 너무 오래 걸렸네요.



USB 보드 + DIP to SOIC adapter + ATtiny85 구성 입니다.



모두 합체하면 이렇게 되죠.




FIN


여기까지 오는데 너무 힘을 빼서, 실제 Arduino IDE 를 이용한 program uploading 은 다음 편에서 다루도록 하겠습니다.


And
prev | 1 | next