'WiFi'에 해당되는 글 13건

  1. 2020.10.07 Hardware | Fuji Xerox CP116w 수리 성공기 48
  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.04.08 Hardware | ESP32 의 Dual core 확인해 보기
  8. 2020.03.23 Hardware | ESP32 스펙 확인해 보기
  9. 2020.03.21 Hardware | ESP32 간단 사용기
  10. 2020.03.18 Hardware | CO2 센서인 MH-Z14A 를 활용해 보자 2

Hardware | Fuji Xerox CP116w 수리 성공기

|

후지제록스 DocuPrint CP116w 를 잘 쓰고 있었습니다.

리필토너도 저렴하고, 리필 칩도 쉽게 구할 수 있었거든요. 레이저 프린터인 만큼 최고의 가성비를 보여 줬었습니다.


토너 리필에 대해서는 아래 글들을 참조.


* Hardware | Fuji Xerox 의 CP116w 토너 교환기

https://chocoball.tistory.com/entry/Hardware-Fuji-Xerox-CP116w-toner-replacement


* Hardware | 재생토너 chip 교환기 - 1

https://chocoball.tistory.com/entry/Hardware-refill-toner-chip-replacement-1


* Hardware | 재생토너 chip 교환기 - 2

https://chocoball.tistory.com/entry/Hardware-refill-toner-chip-replacement-2


글을 세 개나 올렸었네요. 그 만큼 이 프린터를 사랑하고 있었습니다.





1. 에러


어느 시점을 시작으로 자주 느낌표시가 떴습니다.



보통 092-651 에러라고 하는데, 주로 뒷면에 있는 CTD (Color Toner Density) 센서를 닦아주면 된다고 했습니다.



메뉴얼 대로 뒷판을 열고 닦아주니 다시 돌아가더군요.



다만, 문제는 딱 한 번만 해결 되었고, 두 번째부터는 해결되지 않았습니다.

이후, 절대 되돌아 오지 않았습니다. 여기서부터 긴 여정의 시작입니다.




2. 서비스 메뉴얼


수리 과정 중에서 알게된 것이지만, 일반 사용자에게 공개된 메뉴얼 외에,

수리 기사들이 사용하는 "서비스 메뉴얼" 이 따로 있다는 것을 알게 되었습니다.


* 일반 메뉴얼

docuprint_cp116_w.pdf


* 서비스 메뉴얼

WC6015_Service_Manual.zip.001

WC6015_Service_Manual.zip.002

WC6015_Service_Manual.zip.003

WC6015_Service_Manual.zip.004


일반 메뉴얼로는 도저히 알 수 없는 내용들이 서비스 메뉴얼에는 자세하게 나와 있어,

이 후 내용은 서비스 메뉴얼을 기반으로 도전한 내용들 입니다.




3. 꼬질대로 헤드 클리닝


노란색 꼬질대가 토너 삽입부인 옆면에 있습니다.

꺼내서 각 토너의 헤드를 크리닝 해 봅니다.



전혀 효과가 없군요. 실패.




4. 벨트 클리닝


인쇄되는 과정을 보면, 토너를 실어 나르는 벨트가 있고, 고열의 롤러를 통해서 인쇄되는 과정이 포인트 입니다.

그 과정 중에서 아래 그림의 8번 항목처럼, Cleaning 이라는 부분이 있습니다.



고열의 드럼을 통해서 인쇄되지만, 여분의 토너 가루 + 먼지들은, 벨트를 청소해 주는 "Cleaning Blade" 에 모인다고 하네요.



뒷 뚜껑을 열면 투명한 책받침 같은 것이 열전사 드럼 윗쪽에 붙어서 먼지 들을 걸러내 주고 있었습니다.

살짝만 봐도 먼지가 엄청 쌓여 있군요. 핀셋과 진공 청소기를 이용해서 깨끗하게 먼지를 제거합니다.



전혀 효과가 없군요. 실패.




5. 공장 초기화


기계는 모름지기 공장 초기화. 시도해 봅니다.

메뉴얼 문구대로, 뒷 커버를 연 상태로 전면의 Start / Stop 버튼을 누르면서 전원 ON. 그리고 뒷 뚜껑을 닫으면 공장 초기화가 됩니다.



이 글의 스크롤 바가 아직 한참 남은 것이 상황을 말해 주듯, 전혀 효과가 없군요.

실패.




6. 토너칩 교환


쓰고 있던 토너칩이 아직 쌩쌩하지만, 혹시나 해서 교체해 봅니다.

지금까지 구입했던 토너칩 중에서 가장 괜찮은 제품, 아껴 두었던 마무리가 깔끔한 칩을 꺼냈습니다.



이걸 구하려고 해도, 이제는 판매되지 않은 제품 입니다.



요즈음 구매한 토너칩과 비교해 봐면, 보호 수지 질이나, 프린터 센서와 접촉 면적이 훨씬 넓어, 인식문제가 잘 일어나지 않는 제품 입니다.



전혀 효과가 없군요. 실패.



7. 폐토너


서비스 메뉴얼을 정독하고 있자니, 폐토너에 대한 내용이 나옵니다.



드럼이 돌면서 여분의 토너 가루들은 토너 통으로 다시 돌아오는 구조네요?!!!
토너 통의 구분된 부분으로, 한 사이클을 돌고 나온 폐토너 가루들이 들어가 쌓이는 구조로 설계되어 있다는 것을 발견했습니다.

지금까지 토너가루 보충만 했지, Waste Toner Bottle 이라는 부분을 버려본 적이 없었네요. (아무도 알려주지 않았슴)
인터넷 검색을 하니, 어떤 재생토너 업자가 올린 사진을 발견했습니다. 폐토너 통을 비울 수도 있었군요!


Waste Bin 과 연결된 부분을 얇은 드라이버같은 것으로 누르면, 촤르륵 하며 지금까지 쌓여온 페토너 가루들이 쏟아집니다.



신기하게 자성을 띄는 성질을 가지고 있군요. 이 말인 즉슨, 토너는 분말 금속이 섞여 있는 듯 합니다.

신체에 노출이 되면 중금속 중독도 일으키는 원인이 될 수 있다고 봅니다.



지금까지 쌓여 있었던 폐토너 가루들이 한 무더기 나왔습니다.

폐토너들이 더 이상 청소되지 못해여, 프린터 내부에 이게 꽉꽉 쌓여 있으니, 고장의 원인이 되지 않았나라고 생각 했습니다.



그렇지만, 이 시도는 안타깝게도 효과가 없었습니다.
다만, 앞으로 토너 충전시에는 꼭 폐토너도 확인하여 버려야 한다는 것을 새롭게 알게 되었습니다. 일단 실패.



8. 토너 모터


느낌표 에러는 CTD 센서 뿐만 아니라, 정확히 파악되지 않는, 토너 관련 전반적인 에러라는 것을 알게 되었습니다.



토너가 공급되는 방식은 Toner Motor 를 통해서 이루어 진다고 합니다.



토너를 벨트와 고열 드럼으로 보내주기 위해서, 맨 처음 토너 통에서 이루어지는 작업이네요.



문서의 여러 군데에서 Toner Motor 와 연관된 부분들이 언급되어 있습니다.



Toner Motor 는 두 개가 있으며, 기어박스를 통하여 돌아가는 방향에 따라 토너 통을 선택하여 벨트로 보내 줍니다.



두 개의 모터만으로 4개의 토너 통을 컨트롤 하고 있습니다.



이 모터를 제어하는 부분은 MCU Board 의 P/J20 핀에서 제어하고 있었습니다.



아래는 나중에 분해한 후의 사진이지만, P/J20 커넥터 부분입니다.


Toner Motor 는 4개의 신호로 움직이는 Step Motor 이고, 강제적으로 이걸 돌려 주면,

토너 피딩이 끊어진 통에 토너가루를 밀어 넣어주지 않을까 하여 직접 제어해 보기로 합니다.



이 Step Motor 를 arduino 와 연동하여 강제적으로 돌려주기 위해, A4988 / DRV8825 센서를 구입 합니다.


* 3D Printer Parts A4988 DRV8825 Stepper Motor Driver With Heat sink For SKR V1.3 1.4 GTR V1.0 RAMPS 1.4 1.6 MKS GEN V1.4 board

https://www.aliexpress.com/item/32965199683.html



시도해 보고 싶었지만, 다른 방법으로 성공해버려 직접 적용해 보지 않았습니다.

다른 기회에 구동 테스트는 해보는 것으로...




9. 프린터 분해


보다 근본 원인을 파악하기 위해 일단 분해해 봤습니다.

일단 앞부분의 종이 받침을 제거해 줍니다. 연질의 플라스틱이라 살짝 구부려서 양쪽 힌지를 빼면, 쉽게 제거 됩니다.



숨어있는 나사 두 개가 보입니다.



드라이버로 제거해 줍니다.



나머지 하나도 제거해 주구요.



나사를 제거 후, 옆에 있는 틈 사이로 헤라를 넣고 살찍 비틀어 주면, 두두둑 하면서 앞면 전체가 분리됩니다. 



요런 모양으로 됩니다. 앞면만 살짝 노출 되었을 뿐 전혀 구조가 보이지 않네요.



윗면의 투명 커버도 혹시 모르니 분리해 줍니다. 연질의 플라스틱이니 살짝 휘어주면 분리가 됩니다.

그런 후, 윗 뚜껑을 분리해 줍니다. 뚝뚝뚝 하면서 잘 분리 됩니다. 인디케이터와 연결된 flexible cable 에 주의 합니다.



토너쪽 커버를 분리하기 위해서는 뒷면이 먼저 분리되어야 합니다. (사진 밑부분)



후면의 나사를 풀어줍니다.



뒷판이 헐거워 지면, 헤라를 아래처럼 넣어서 이격시켜 줍니다. 보여주기 위한 사진을 따로 찍어서 토너가 제거된 사진이네요.



그러면, 토너쪽 커버를 제거할 수 있습니다.



분리할 필요는 없지만, 토너 반대쪽 커버도 분리해 봅니다. 이 쪽은 나사 채결이 없어서 쉽게 분리 가능합니다.

상판과 연결된 검은색 flexible cable 을 조심합니다. (아래 사진에서 왼쪽 위)



ARM 칩과 WiFi 모듈이 보입니다. 메모리도 있네요. 이뻐서 한컷 찍어서 남겨 봅니다.



상판을 분리한 후, 토너쪽 사진 입니다.



아래 사진 가운데에 보이는 굵은 흰색 선이 메인 전원 입니다. 소켓에서 뽑아 줍니다.



그러면, 토너 쪽 커버를 완전히 분리할 수 있습니다.



앞면의 케이블 정리 고정부분을 잘 풀면, 아래 사진처럼 안전하게 바닥에 놓아 둘 수 있습니다.





10. 청소


재생 토너의 큰 입자들로 인하여, 인입 부분이 막혀있는 것이 아닌가 해서 토너통과 연결된 부분을 청소해 보기로 합니다.



청소기에 비닐을 씌운 다음, 버릴 양말을 덧씌운 후, 빨대를 꽂았습니다. 저 빨대로만 빨아들일 수 있게 만든거죠.



페토너 구멍이든, 토너 피딩 구멍이든 깊숙이 넣어서 청소기를 돌려 줍니다.



피딩 부분은 커버도 쉽게 벗길 수 있습니다.



벗길 수 있는 한, 모두 벗겨서 청소해 줍니다.



그런 다음, 탈탈 털어주면 더 뭔가가 많이 쏟아집니다. 입자가 굵은 것들도 있는걸 보면, 막혀 있던 것들일 수도 있겠네요.



하지만, 이 방법으로도 해결되지 않았습니다.

실패.




11. 빨래 찝게


구입한 Step Motor 컨트롤러가 중국으로부터 도착할 때 까지, 시도해 볼만한 방법들을 계속 생각했습니다.

그러다, 고장나기 직전에 검은 색이 잘 나오지 않았다는 것을 기억해 냅니다.


부팅하면서 잠깐 피딩 롤러가 돌아가는 시점에 검은색 토너를 수동으로 넣어주면 어떨까 했습니다.

토너통을 빼고, 토너칩만 센서에 부착 시키면, 프린터는 토너통이 장착되어 있다고 속을 것이고, 그 공간에서 작업하면 될 것 같았습니다.


이렇게 하면, Toner Motor 를 Step Motor 컨트롤로 + arduino 을 이용하여 강제로 돌려주지 않아도,

롤러 안에서 끊어진 토너의 흐름을 다시 만들어 줄 수 있을 것 같았습니다.


공판장에서 1,400 원 하는 "썬 왕 빨래집게" 구매.



사진처럼 재생토너 뚜껑만 분리하여, 토너칩 센서에 토너칩을 부착시켜 줍니다.



네 가지 모두 장착하여 인식 성공.



프린터가 부팅하면서 잠깐 돌아가는 롤러에 강제로 검정 토너를 흘려 줍니다.



음?!!!!!!!!!!!!!!!!!!!!!!!!!!


뭔가 반응이 다릅니다!!!

딸깍 하면서 느낌표가 떠야 할 시점에 느낌표는 뜨지 않고 Toner Motor 가 마구마구 돌기 시작했습니다!!!!!!!!!!

필시 이것은, 정상으로 되돌아온 신호!


바로 토너를 제대로 장착하고 다시 재부팅.

오오오오!!!!!!!!!!!!! 아래 동영상처럼 Toner Motor 가 돌면서 모든 토너통으로 부터 토너를 빨아들이기 시작 했습니다.



아~~~~~~~~~~~~~~~~~~~~~~~~!!! 성공.

바로 테스트 프린트를 마구마구 찍어 봅니다. 새로 샀을 때 처럼 선명함과 진함이 되돌아 왔습니다.



토너통을 하도 꼈다 뺐다 했더니만, 파란색 기어에 부서질 듯한 소리가 났습니다.

파란색 토너 입구의 쿠션이 말려 들어가 있더군요. 크게 문제될 것이 없을 듯 하여 그냥 제거 했습니다.



조립은 분해의 역순. 이로써 수리가 성공으로 끝났습니다.




FIN


수리 성공기를 마치며, 아래와 같이 정리합니다.


1. 벨트 클리닝 블레이드에 쌓인 먼지는 청소해 주자

2. 폐토너는 생각나면 버려주자

3. 토너통 기어가 잘 돌아가는지 가끔 확인해 주자

4. CTD 센서 부분은 가끔 청소해 주는데, 효과는 별로 없다

5. 어찌 되었든, 토너가 끊기지 않게 하자

6. 만일 끊겼을 경우는 빨래 집게를 이용하여 수리하자


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 | ESP32 의 Dual core 확인해 보기

|

ESP32 대한 지금까지 글은 아래 포스팅들을 참고해 주세요.


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

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




1. Dual core 활용에 앞서


ESP32 의 최장점 중 하나인, dual core 활용에 앞서 개념적인 이야기들이 조금 필요하다고 생각되었다.



그 이유는, 제가 dual core 활용을 이해하고 실행해 보기까지 삽질한 흔적들을 공유하기 위해서 이다.

아직 100% 맞는 이야기 인지는 확신이 들지 않지만, 대략 문제는 없어 보이니, 서두를 조금 길게 하겠다.




2. Arduino IDE 와 ESP-IDF


우선 ESP32 에 프로그램을 올리기 위해서는 coding 하는 환경이 필요한데, 이 개발환경이 ESP-IDF 이다.

보통 ESP-IDF framework 라 부른다.



다만, 우리가 친숙한 Arduino IDE 환경에서도 개발할 수 있도록, Arduino IDE 의 Arduino Core 가 ESP-IDF 를 포함하고 있다.

말하자면, ESP-IDF 의 간편한 interface 인 셈이다.


참고로, Arduino IDE 에서 컴파일한 결과물 보다, ESP-IDF 환경에서 컴파일한 결과물이 대략 65% 정도의 더 빠르다고 하니, ESP32 개발은 가능하면, ESP-IDF 프레임워크에서 개발하는 것이 좋다 하겠다.


살짝 다른 이야기 이지만, 위의 글에서도 설명 되어 있 듯, ESP32 세계에서 이야기 하는 firmware 는, 흔히 OS 기저에 존재하는 HW/OS 사이를 interfacing 해주는 미들웨어가 아니라, 구동 프로그램 자체를 이야기 한다. 헷갈리지 말자.


이 용어의 혼란 때문에, ESP32 의 firmware 를 업데이트 하고자 답도 없는 웹페이지들을 읽어댓다.




3. ESP-IDF FreeRTOS


그럼 자주 등장하는 FreeRTOS 는 도대체 뭔가?


* Quality RTOS & Embedded Software
    - https://www.freertos.org/a00106.html


이름의 약자 부분이 "RTOS - Real Time OS" 인 실시간 처리를 위한 OS이고, 그 중에 가장 유명한 open source 인 RTOS 를 뜻한다.

결국, 아래 발췌한 문구에서 볼 수 있 듯, ESP-IDF framework 는 이 FreeRTOS 실시간 운영 체제를 기반으로 하고 있다.


The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports symmetric multiprocessing (SMP).

이 FreeRTOS 덕에, 이 글의 목적인 dual core 를 활용할 수 있게 된 것이다.

처음엔 ESP32Arduino + WiFi 정도로 생각하고 덤볐는데, 완전 deep 하게 들어온 듯 하다.

상하 관계를 정리하면, FreeRTOS + Components > ESP-IDF > Arduino Core > Arduino IDE 되겠다.



AWS 에서도 MQTT 를 위해 FreeRTOS 서비스를 지원하고 있지만, 아래 그림에서 보면 살짤 달라 보인다.

ESP32 에서는 ESP-IDF 하위 layer 에 FreeRTOS 가 위치하지만, AWS FreeRTOS 는 ESP-IDF 상위에 자리하고 있다.





4. SMP


ESP32 에서 dual core 는 CPU 0 - Protocol CPU (PRO_CPU) CPU 1 - Application CPU (APP_CPU)두 가지로 정의되어 사용된다.

보통, PRO_CPU 는 백그라운드 작업이나 WiFi 인터페이싱 쪽을, APP_CPU 는 어플리케이션 쪽에 우선 순위를 둬서 동작하게 된다.

그러다 보니, APP_CPU 는 항상 바쁘고, PRO_CPU 는 많은 시간 놀게 되는, 좋지 않은 효율을 보일 수 있다.


Arduino IDE 에서 코딩하게 되면, setup() / loop() 는 CPU 1 에서 실행되고, 무선관련 동작은 CPU 0 에서 실행된다고 한다.



효율이 좋지 않다고 하더라도, 엄연한 dual core architecture 이며, memory 와 cache 및 외부기기 접근을 공유한다.



Core 이름에서도 알 수 있듯, 통신에 관한 background 처리는 CPU 0 에서 처리하고, 어플리케이션은 CPU 1 에서 하려고 하기 때문에, 효율 좋게 동작할 수 있도록 아래와 같이 CPU core 지정 명령어를 구비해 놓았습니다.


|------------------------------------------------------------------|
| instruction               | meaning                              |
|------------------------------------------------------------------|
| xTaskCreate()             | for single core tasks                |
| xTaskCreatePinnedToCore() | for assigning tasks to specific core |
| xTaskCreateUniversal()    | for making cores used evenly         |
|------------------------------------------------------------------|


Single core 에는 "xTaskCreate()" 를 사용하고, 특정 core 에 할당하고 싶을 때에는 "xTaskCreatePinnedToCore()" 을 사용합니다.

Core 수를 확인하여, core 갯수에 알맞게 task 를 할당하는 것이 "xTaskCreateUniversal()" 입니다.


우리는 core 를 편중되지 않고, 조금 강제적으로 균등하게 사용하기 위해 "xTaskCreatePinnedToCore()" 을 사용할 껍니다.

참고로, 함수의 첫번째 부분에 표시되는 "x..." 는 FreeRTOS 네이티브 명령어를 지정하는 것이라 하네요.




5. Source 기본 구조


기존 Arduino 처럼 코딩하는 것이 아닌, Dual core 만의 규칙이 있습니다. 사실 FreeRTOS 의 규칙이겠죠.

void task1(void *pvParameters) {
	while (1) {
		...
		...
		delay(10);
	}
}

void task2(void *pvParameters) {
	while (1) {
		...
		...
		delay(10);
	}
}


우선 각 core 에 시킬 일들을 미리 정의해 놓습니다. Function 정의와 비슷합니다.

다만, "while(1)" 구문 안에 작업 루틴이 들어가고, 필히 "delay(10)" 가 필요합니다.

Delay 는 10 microseconds 미만이면, 균등하게 core 끼리 일을 나눠 갖지 못합니다. 꼭 10 microseconds 이상으로 설정해야 합니다.


참고로 Arduino IDE 에서 제공하는 "delay(10)" 함수를 사용해도 되나, FreeRTOS 용으로 표현해 보면 다음과 같습니다.
ESP32 를 활용함에 있어, FreeRTOS 용 함수를 사용하는 것이 좀더 빠를 듯 해서입니다.

portTickType delay_10 = 10 / portTICK_RATE_MS;

...
		vTaskDelay(delay_10);
...

setup()loop() 는 다음과 같은 형식 입니다.

void setup() {
	Serial.begin(115200);
	delay(50); // wait for initialization
	
	xTaskCreatePinnedToCore(
		task1,
		"task1",
		8192,
		NULL,
		1,
		NULL,
		PRO_CPU_NUM
	);
	
	xTaskCreatePinnedToCore(
		task2,
		"task2",
		8192,
		NULL,
		0,
		NULL,
		APP_CPU_NUM
	);
}

void loop() {
}


위에서 언급 했던, core 할당 방법 - xTaskCreate() / xTaskCreatePinnedToCore() / xTaskCreateUniversal() - 과, 어느 CPU - PRO_CPU_NUM / APP_CPU_NUM - 를 사용할 것인지를 정의해 줍니다.


위의 예시는, Core 0 / 1 에 강제적으로 task 를 할당한 예 입니다.

자세한 설명은 아래 FreeRTOS 문서를 참고해 보세요.


* FreeRTOS

- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html




6. Single Core 활용


Core 한개만을 이용하여, 2승을 계산하는 간단한 코드를 만들어 봤습니다.


portTickType delay_10 = 10 / portTICK_RATE_MS;
int countX = 0;

void task1(void *pvParameters) {
	while (1) {
		for ( int i = 0 ; i < 1000 ; i++ ) {
			pow(2, i);
		}
		countX++;
		vTaskDelay(delay_10);
	}
}
 
void setup() {
	Serial.begin(115200);
	delay(50); // wait for initialization
	
	xTaskCreatePinnedToCore(
		task1,
		"task1",
		8192,
		NULL,
		1,
		NULL,
		PRO_CPU_NUM
	);
}
 
void loop() {
	Serial.println(countX);
	delay(100);
}


2의 1000 승이 완료되면 countX 에 1을 증가하는 방식으로, 일정한 시간 동안 Single 과 Dual 의 계산량 차이를 보기 위한 것입니다.


int countX = 0;

...

		for ( int i = 0 ; i < 1000 ; i++ ) {
			pow(2, i);
		}
		countX++;
...

void loop() {
	Serial.println(countX);
	delay(100);
}


setup() 에서 xTaskCreatePinnedToCore 를 이용하여 task1 하나만 정의하여 돌려 봤습니다.


...

	xTaskCreatePinnedToCore(
		task1,
		"task1",
		8192,
		NULL,
		1,
		NULL,
		PRO_CPU_NUM
	);

...


위에서 CPU core 지정 부분을 PRO_CPU_NUMAPP_CPU_NUM 을 바꿔 해봤지만, 동일한 결과를 보여 줬습니다.



약 50 초 동안, 2의 1000 승을 1600 회 정도 계산했습니다.




7. Dual Core 활용


xTaskCreatePinnedToCorePRO_CPU_NUM / APP_CPU_NUM 를 모두 사용하는 코드 입니다.


portTickType delay_10 = 10 / portTICK_RATE_MS;
int countX = 0;

void task1(void *pvParameters) {
	while (1) {
		for ( int i = 0 ; i < 1000 ; i++ ) {
			pow(2, i);
		}
		countX++;
		vTaskDelay(delay_10);
	}
}

void task2(void *pvParameters) {
	while (1) {
		for ( int i = 0 ; i < 1000 ; i++ ) {
			pow(2, i);
		}
		countX++;
		vTaskDelay(delay_10);
	}
}
 
void setup() {
	Serial.begin(115200);
	delay(50); // wait for initialization
	
	xTaskCreatePinnedToCore(
		task1,
		"task1",
		8192,
		NULL,
		1,
		NULL,
		PRO_CPU_NUM
	);
 
	xTaskCreatePinnedToCore(
		task2,
		"task2",
		8192,
		NULL,
		1,
		NULL,
		APP_CPU_NUM
	);
}
 
void loop() {
	Serial.println(countX);
	delay(100);
}


약 3200 개 정도를 계산해 냈네요. Single core 사용할 때보다, 딱 2배의 숫자 입니다. 이로써, dual core 가 사용되었다는 것을 알 수 있습니다.



Single core 의 계산 처리값과 한 그래프에서 비교하고 싶어서 index 를 추가하고 EXCEL 에서 그래프를 그려 보았습니다.


int x = 0;

...
 
void loop() {
  x++;
  Serial.print(x);
  Serial.print("\t");
  Serial.println(countX);
  delay(100);
}

왼쪽이 index, 오른쪽이 100 마이크로초 동안 2 의 1000 승을 계산한 횟수를 보여줍니다.


생각 외로 처리량이 꽤 좋습니다. Arduino Nano 와는 넘사벽인 성능입니다.


ESP32_dual_core_calc.xlsx



그래프에서 선이 겹쳐 보여 잘 보이지 않지만, PRO_CPU_NUM 이 조금 낮게 나올 것이라고 예상 했지만, WiFi 등을 사용하고 있지 않기 때문에, APP_CPU_NUM 과 동일하게 나왔습니다.


Dual core 를 활용하면서, 정확하게 2배의 계산 처리량을 보여 줬습니다.




FIN


CPU 도 Dual core 에다가 내장 센서, 그리고 Arduino IDE 에서 코딩할 수 있다는 점은 엄청나게 큰 장점일 듯 합니다.

거의 왠만한 프로젝트는 ESP32 하나로 커버 가능할 것 같습니다. EPS32 에 대해 다른 기능들도 차차 알아 보겠습니다.




참고 사이트


- https://www.hackster.io/rayburne/esp32-in-love-with-both-cores-8dd948

- https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html/2

- https://qiita.com/makotaka/items/dd035f4b2db94f87b63c

- https://lang-ship.com/blog/work/esp32-freertos-l03-multitask/

- https://techtutorialsx.com/2017/05/16/esp32-dual-core-execution-speedup/



And

Hardware | ESP32 스펙 확인해 보기

|

1. ESP32 specification


ESP32 는 ATmega328 베이스의 arduino 보다는 훨씬 많은 성능을 가지고 있습니다.



거기에 더하여 ESP32 자체도 버전이 조금씩 달라, 구입 후 실적용 전에 spec. 을 확인해 보는 것이 중요하다고 합니다.




2. 참조 사이트


내부 스펙에 대해 확인할 수 있는 글을 발견하여 소개된 소스를 실행해 봤습니다.


* mgo-tec電子工作
    - https://www.mgo-tec.com/blog-entry-chip-info-esp-wroom-32-esp32.html


* ESP32-WROOM-32
    - https://ht-deko.com/arduino/esp-wroom-32.html


위에서 참조한 사이트의 소스를 그냥 실행하니, 동작은 하지만 compile 시 warning 이 뜨더군요.



이유는, 글쓴이의 ESP-IDF (ESP8266 의 SDK 와 같은 기능) 가 예전 버전을 사용해서 확인했기 때문이었습니다. (2017년 버전)
EFUSE 정보에 접근하는 방법도 바뀌었으며, 특히 Heap Memory 확인 방법이 바뀌었습니다.


...
 //Internal RAM
uint32_t getHeapSize(); //total heap size
uint32_t getFreeHeap(); //available heap
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once

the esp.h file: https://github.com/espressif/arduino-esp32/blob/80c110ece70b179ddfe686e8ee45b6c808779454/cores/esp32/Esp.h
...


다행히 업데이트 된 core library 에서 변경된 API 명령어를 찾을 수 있었습니다.



Warning 부분을 모두 확인 했으니, 하나씩 정리해 봅니다.



3. API


ESP32 는, library / API 를 통해 간단하게 컨트롤 할 수 있게 만들어져 있습니다.

자세한 사양을 확인하는 방법도 API 를 통해 확인할 수 있게 되어 있으므로, API 문서만 참고하면 됩니다.


* Miscellaneous System APIs
    - https://docs.espressif.com/projects/esp-idf/en/v3.2.3/api-reference/system/system.html



API 사이트가 잘 만들어져 있습니다.

화면의 왼쪽 밑부분의 버전을 클릭하면, 원하는 version 의 API 문서로 옮겨갈 수 있게 되어 있습니다.



제가 사용하고 있는 ESP-IDF 버전이 v3.2.3-14 이므로, API 문서에서 v3.2.3 을 선택해서 확인 했습니다.




4. API - 사용한 명령어


위에서 설명한 Heap Memory 부분을 제외한 내용 입니다.

ESP32 의 CPU 를 확인할 수 있는 명령어 입니다.



CPU 의 모델과 revision, 그리고 CPU core 갯수 까지 확인 할 수 있습니다.



사용하고 있는 ESP-IDF 버전 확인 명령어 입니다.



위에서 잠깐 언급한 EFUSE 확인용 커멘드 입니다.

확인해 보면, 기기의 기본 MAC 주서는 WiFi Station MAC 주소와 동일하다는 것을 알 수 있습니다. WiFi Station MAC 정보는 세상에서 유일하니, 기기의 유일성을 WiFi Station MAC 로 정의해도 문제는 없겠네요.



WiFi / WiFi Software AP / Bluetooth / Ethernet 의 MAC 주소는 다음과 같은 API 로 정보를 확인 할 수 있습니다.



최종 사용된 API 들 입니다.


	ESP.getEfuseMac()
	ESP.getChipRevision()
	esp_chip_info_t
	esp_chip_info
	ESP.getCpuFreqMHz()
	ESP.getFlashChipSize()
	ESP.getFlashChipSpeed()
	esp_get_idf_version()
	ESP.getHeapSize()
	ESP.getFreeHeap()
	ESP.getMinFreeHeap()
	ESP.getMaxAllocHeap()
	esp_efuse_mac_get_default()
	esp_read_mac(array, ESP_MAC_WIFI_STA)
	esp_read_mac(array, ESP_MAC_WIFI_SOFTAP)
	esp_read_mac(array, ESP_MAC_BT)
	esp_read_mac(array, ESP_MAC_ETH)




5. sketch


참조한 사이트와 ESP-IDF v3.2.3 용으로 조금 바꾼 소스 입니다.


void setup(void) {
	Serial.begin(115200);
	
	uint64_t chipid;
	chipid=ESP.getEfuseMac(); // The chip ID is essentially its MAC address(length: 6 bytes)
	Serial.printf("ESP32 Chip ID = %04X", (uint16_t)(chipid>>32)); // print High 2 bytes
	Serial.printf("%08X\n", (uint32_t)chipid); // print Low 4bytes
	Serial.println("---------------------------------");
	
	Serial.printf("Chip Revision %d\n", ESP.getChipRevision());
	esp_chip_info_t chip_info;
	esp_chip_info(&chip_info);
	Serial.printf("Number of Core: %d\n", chip_info.cores);
	Serial.printf("CPU Frequency: %d MHz\n", ESP.getCpuFreqMHz());  
	Serial.println();
	
	Serial.printf("Flash Chip Size = %d byte\n", ESP.getFlashChipSize());
	Serial.printf("Flash Frequency = %d Hz\n", ESP.getFlashChipSpeed());
	Serial.println();
	
	Serial.printf("ESP-IDF version = %s\n", esp_get_idf_version());
	Serial.println();
	
	Serial.printf("Total Heap Size = %d\n", ESP.getHeapSize());
	Serial.printf("Free Heap Size = %d\n", ESP.getFreeHeap());
	Serial.printf("Lowest Free Heap Size = %d\n", ESP.getMinFreeHeap());
	Serial.printf("Largest Heap Block = %d\n", ESP.getMaxAllocHeap());
	Serial.println();
	
	uint8_t mac0[6];
	esp_efuse_mac_get_default(mac0);
	Serial.printf("Default Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac0[0], mac0[1], mac0[2], mac0[3], mac0[4], mac0[5]);
	
	uint8_t mac3[6];
	esp_read_mac(mac3, ESP_MAC_WIFI_STA);
	Serial.printf("[Wi-Fi Station] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
	
	uint8_t mac4[7];
	esp_read_mac(mac4, ESP_MAC_WIFI_SOFTAP);
	Serial.printf("[Wi-Fi SoftAP] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac4[0], mac4[1], mac4[2], mac4[3], mac4[4], mac4[5]);
	
	uint8_t mac5[6];
	esp_read_mac(mac5, ESP_MAC_BT);
	Serial.printf("[Bluetooth] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac5[0], mac5[1], mac5[2], mac5[3], mac5[4], mac5[5]);
	
	uint8_t mac6[6];
	esp_read_mac(mac6, ESP_MAC_ETH);
	Serial.printf("[Ethernet] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac6[0], mac6[1], mac6[2], mac6[3], mac6[4], mac6[5]);
}
 
void loop() {
	
}


결과 입니다.



이정도면, 사용되는 ESP32 가 어떤 상황인지 알 수 있을 것 같습니다.




6. RESET


소스를 ESP32 에 처음 업로드 하면, Serial Monitor 에 정보가 표시되지 않습니다.

이럴 때에는 "RESET" 버튼을 누르면 reboot 되면서 진행이 됩니다.



RESET 버튼을 누르고 reboot 이 되면, 부팅 시퀀스를 볼 수 있습니다.



MCU 가 arduino nano 와 완전 다른 차원이다 보니, 기본 스펙 파악도 중요하네요.



And

Hardware | ESP32 간단 사용기

|

일전에 ESP-01 (ESP8266) 을 가지고 IoT 환경을 꾸며 봤습니다.


* Hardware | CO2 센서인 MH-Z14A 를 활용해 보자
    - https://chocoball.tistory.com/entry/Hardware-CO2-sensor-MH-Z14A


ESP-01 은, WiFi 모듈로는 초기형이고, 그 이후에 업그레이드 된 모듈들이 많이 출시 되었습니다.

그 다음으로 많이 사용하는 WiFi 모듈은 "ESP32" 인 듯 합니다.




1. ESP32


구매합니다.


* ESP32 ESP-32 ESP32S ESP-32S CP2102 Wireless WiFi Bluetooth Development Board Micro USB Dual Core Power Amplifier Filter Module
    - https://www.aliexpress.com/item/32928267626.html


잘 도착 했습니다.





2. 외형


Arduino nano 의 경우, MCU 가 ATmega328 였지만, ESP32 는 MCU + WiFi + Bluetooth + ... 가 chip 하나에 모두 들어간 형태 입니다.



ESP-WROOM-32 라고 적힌 철판 밑에 MCU 가 자리잡고 있을 듯 합니다.



USB interface 는 Silicon Labs 사의 CP2102 이군요. WiFi 접근에 따로 FTDI 같은 어뎁터를 경유할 필요가 없습니다.



뒷면에는 ESP32 DEVKIT V1 이라고 적혀 있네요.




3. MCU


* ESP32

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



MCU 의 정식 명칭은 "ESP32-DOWDQ6" 라고 확인이 되었습니다. (Arduino IDE 에서 컴파일 할 때 확인 가능)


MCU 의 logic diagram 입니다. 몇 가지 재미있는 부분들이 보이네요. 센서 내장이라던지... (센서에 환장한 人)



이 chip 의 naming 을 통해서도 spec. 을 알 수 있습니다.



데이터 쉬트 입니다.

esp32_datasheet_en.pdf


간단히 ESP8266 (ESP-01) 과 비교 테이블을 만들어 봤습니다.


|------------------------------------------------------------------------------------|
|                       | ESP8266                     | ESP32                        |
|------------------------------------------------------------------------------------|
| release Year          | 2014                        | 2016                         |
| Processor             | Tensilica L106 32-bit       | Xtensa Dual-core 32-bit LX6  |
|                       | single core micro controller| microprocessor with 600 DMIPS|
| Typical Frequency     | 80 MHz                      | 160 MHz                      |
| Co-processor          | no                          | ULP                          |
| ROM                   | no                          | 448 kB                       |
| SRAM                  | 160kB                       | 520 kB                       |
| RTC RAM               | 768 Bytes                   | 8kB slow + 8kB fast          |
| QSPI flash/SRAM       | up to 1 x 16 MB             | up to 4 x 16 MB              |
| GPIOs                 | 17                          | 36                           |
| Wi-Fi                 | HT20                        | HT40                         |
| Bluetooth             | no                          | Bluetooth 4.2 and BLE        |
| Ethernet              | no                          | 10/100 Mbps                  |
| ADC                   | 10 bit                      | 12 bit                       |
| DAC                   | no                          | 2 x 8 bit                    |
| Touch sensor          | no                          | 10                           |
| Temperature Sensor    | no                          | yes                          |
| Security              | no                          | Secure boot Flash encryption |
|                       |                             | 1024-bit OTP                 |
| Crypto                | no                          | AES, SHA-2, RSA, ECC, RNG    |
| SPI                   | 2                           | 4                            |
| I2C                   | 1 (soft)                    | 2                            |
| I2S                   | 2                           | 2                            |
| UART                  | 2 (1 1/2 actually)          | 3                            |
| ADC                   | 1 (10-bit)                  | 18 (12-bit)                  |
| DAC                   | no                          | 2 (8-bit)                    |
| PWM Pins              | 8 Software                  | 1 Hardware / 16 software     |
| SDMMC                 | no                          | yes                          |
| RMT (remote control)  | no                          | yes                          |
| Temperature sensor    | no                          | yes                          |
| Hall sensor           | no                          | yes                          |
| Low Power Consumption | 20uA                        | 10uA deep sleep              |
| Power supply          | 2.5V to 3.6V                | 2.3V to 3.6V                 |
|------------------------------------------------------------------------------------|


생산 업체가 이야기하는 가장 큰 특장점은 아래 5 가지로 정리할 수 있겠습니다.


- Dual-core 32-bit microprocessor

- 2.4 GHz Wi-Fi and Bluetooth combo chip
- TSMC low power 40nm lithography technology
- Best power performance and RF performance
- Robustness, versatility and reliability


Pin out 정보 입니다. 기존 arduino nano 나 ESP-01 과는 차원이 다른 구성입니다.



이거 하나면 왠만한거 다 할 수 있겠네요.




4. Arduino IDE - 드라이버 설치


코딩을 하고 동작을 시키려면 Arduino IDE 등을 이용해서 프로그램을 업로드 할 수 있습니다. 기존 arduino 와 완전히 동일하죠.

다만, Arduino IDE 의 기본 기기나 라이브러리에 등록되어 있지 않으므로, 새로이 등록시켜주는 작업이 필요합니다.


위에서 잠깐 언급한 대로, USB interface 는 Silicon Labs 의 CP2102 이므로, 해당 드라이버를 설치해 줘야 합니다.

Windows 10 에서는 USB 를 연결하자 마자 알아서 설치해 줍니다.


예전에 Serial Adapter 처음 사용할 때, 이 등록절차를 소개한 적이 있습니다. 아래 글을 참고해 주세요.


* Hardware | FTDI Serial Adapter 를 사용해 보자
    - https://chocoball.tistory.com/entry/Hardware-FTDI-FT232RL-using


드라이버가 정상적으로 설치되면, 아래와 같이 포트가 인식됩니다.





5. Arduino IDE - Board Manager


Arduino IDE 에서 보드를 등록하고, 관련 라이브러리를 설치해야 합니다.
우선 File > Preferences 로 갑니다.


Additional Board Manager URLs 에 ESP32 에 관련한 URL 을 등록 합니다.


https://dl.espressif.com/dl/package_esp32_index.json



그러면, Tools > Board > Boards Manager 에서 관련된 라이브러리를 설치할 수 있게 됩니다.



검색 줄에 esp32 라고 검색하면 esp32 by Espressif Systems 가 뜹니다. 인스톨 해 줍니다.



그러면, Board 선택에서 ESP32 보드가 보이고, 선택할 수 있게 됩니다.



Upload Speed 나 Flash Frequency 등은 나중에 차차 수정하기로 하고, 아까 CP2102 가 할당된 포트를 지정합니다.



이제 준비는 끝났습니다.




6. Blink


Arduino 의 "Hello World" 라고 할 수 있는 Blink 를 실행해 봅니다.


File > Examples > 01.Basics > Blink 소스를 업로드 해 줍니다.


/*
  Blink

  Turns an LED on for one second, then off for one second, repeatedly.

  Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
  it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
  the correct LED pin independent of which board is used.
  If you want to know what pin the on-board LED is connected to on your Arduino
  model, check the Technical Specs of your board at:
  https://www.arduino.cc/en/Main/Products

  modified 8 May 2014
  by Scott Fitzgerald
  modified 2 Sep 2016
  by Arturo Guadalupi
  modified 8 Sep 2016
  by Colby Newman

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/Blink
*/

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}


Arduino 기기가 아님에도 불구하고, "LED_BUILTIN" 으로 지정하면, breakout board 상의 LED 를 점등시켜 줍니다.



간단하게 성공해버려 조금 허탈한 감이 있습니다.




7. WiFiScan


MCU 자체적으로 WiFi 까지 커버하는 SoC 인지라, WiFi 도 동작 하는지 확인해 봅시다.
지금까지의 작업으로 라이브러리 및 Examples 소스도 인스톨 되어 있으니, WiFiScan 이라는 소스를 올려 봤습니다.

File > Examples > WiFi > WiFiScan



ESP-01 을 사용할 때에는, AT command 를 사용해서 직접 컨트롤 했지만, ESP32 는 라이브러리 함수만으로 모든게 컨트롤 가능해 보입니다.



소스가 엄청 짧습니다.

컴파일 끝나고 ESP32 에 업로드 할 때, ESP32 기기와 관련된 정보를 뿌려줘서 캡춰해 봤습니다.


동작 주파수가 240MHz 도 가능하고, 외장 Flash 크기가 4MB 군요!


esptool.py v2.6
Serial port COM8
Connecting.....
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: 24:6f:28:a1:af:8c
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 8192 bytes to 47...

Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.0 seconds (effective 5461.3 kbit/s)...
Hash of data verified.
Compressed 15856 bytes to 10276...

Writing at 0x00001000... (100 %)
Wrote 15856 bytes (10276 compressed) at 0x00001000 in 0.1 seconds (effective 953.7 kbit/s)...
Hash of data verified.
Compressed 623776 bytes to 372912...

Writing at 0x00010000... (4 %)
Writing at 0x00014000... (8 %)
Writing at 0x00018000... (13 %)
Writing at 0x0001c000... (17 %)
Writing at 0x00020000... (21 %)
Writing at 0x00024000... (26 %)
Writing at 0x00028000... (30 %)
Writing at 0x0002c000... (34 %)
Writing at 0x00030000... (39 %)
Writing at 0x00034000... (43 %)
Writing at 0x00038000... (47 %)
Writing at 0x0003c000... (52 %)
Writing at 0x00040000... (56 %)
Writing at 0x00044000... (60 %)
Writing at 0x00048000... (65 %)
Writing at 0x0004c000... (69 %)
Writing at 0x00050000... (73 %)
Writing at 0x00054000... (78 %)
Writing at 0x00058000... (82 %)
Writing at 0x0005c000... (86 %)
Writing at 0x00060000... (91 %)
Writing at 0x00064000... (95 %)
Writing at 0x00068000... (100 %)
Wrote 623776 bytes (372912 compressed) at 0x00010000 in 5.3 seconds (effective 933.3 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 128...

Writing at 0x00008000... (100 %)
Wrote 3072 bytes (128 compressed) at 0x00008000 in 0.0 seconds (effective 2234.2 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...


Serial Monitor 로 확인해 보니, 아래와 같이 주변 access point 들을 보여 줍니다. 음... 넘 쉬운데?!!!





8. 자, 다음...


Dual-core 에 여러 센서까지 복잡하게 갖춘 ESP32 지만, 강력한 라이브러리로 인하여, 쉽게 이용할 수 있다는 것에 놀랐습니다.

또한, WiFi / Bluetooth 가 MCU 에서 지원하니, 따로 모듈을 연결할 필요가 없어, IoT 구현 시, 회로가 간단해 지겠네요.


추가 기능들을 가지고 있으니, 다음 편들에서는 아래 내용들에 대해 확인해 보도록 하겠습니다.


 - Dual-core

 - Sleep Modes
 - OTA Updates
 - OTA Web Updater
 - NTP Server
 - Temp Sensor
 - Touch Sensor
 - Hall Sensor


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
prev | 1 | 2 | next