Sming framework은 가장 진보되고 생산성 높은 ESP8266 개발환경을 제공합니다.

.

본 페이지는 Sming 을 이용해 ESP8266 모듈의 GPIO 핀들을 제어하는 예제입니다.

Sming 개발환경은 지원하는 기능이 다양한 만큼 예제도 풍부하게 준비되어 있습니다. 크게 구분해보면 GPIO 컨트롤, TCP/UDP/IP 네트워크, WiFi 응용 서비스, 웹 및 기타 유틸리티 등이 있습니다. 아래 링크에서 각 예제 소스들을 구하실 수 있습니다.

Sming 개발환경은 아두이노 스타일의 함수들을 제공하며 복잡한 서비스도 라이브러리/클래스화 되어있기 때문에 비교적 사용하기 쉬운 편입니다.  게다가 아두이노 라이브러리를 가져와 사용할 수도 있습니다.

하지만 아두이노와는 중요한 차이점이 하나가 있는데 아두이노의 setup(), loop() 기본 함수를 제공하지 않는다는 점입니다. 대신 Sming 에서는 init() 함수가 실질적인 코드의 시작점 역할을 합니다. 아두이노의 setup() 함수와 유사하다고 할 수 있습니다.

그리고 loop() 함수가 없습니다. 왜냐면 OS 역할을 해주는 ESP8266 core 가 동작하면서 WiFi 네트워크 및 관련 기능들을 처리해줘야 하기 때문에 사용자가 제어의 모든 권한을 가지는 것을 지양합니다. 그래서 loop()와 같은 무한반복 함수를 제공하지 않습니다. 하지만 유사하게 loop() 함수를 구현할 수는 있습니다. 타이머를 이용해서 구현하는데, loop() 함수를 정의해두고 일정 시간 간격으로 loop() 함수가 호출되도록 하면 됩니다.

실제 코드로 작성하면 아래처럼 됩니다.

#include <user_config.h>
#include <SmingCore/SmingCore.h>

Timer procTimer;
void loop();

void init() {
	// Start arduino style loop (run every 50 ms)
	procTimer.initializeMs(50, loop).start();
}

void loop() {
	// Do what you want
}

Timer 객체인 procTimer를 이용해서 50ms 간격으로 loop() 함수가 호출되도록 한겁니다.

하지만 꼭 주의해야 합니다. loop() 함수에서 복잡한 작업을 처리하거나 블로킹 함수(호출후 응답을 받을때까지 코드 진행이 정지되는 함수)를 남발해서 작업을 지연시켜서는 안됩니다. 빨리 작업을 처리하고 제어 권한을 ESP8266 코어에 넘겨줘야 합니다. 특히, 공유기(AP) 연결 이후 WiFi 기능들이 동작할 때 이 점은 더욱 중요합니다. ESP8266 에서는 사용자 작업이 20ms 이내에 처리되도록 코드를 작성하길 권장합니다.

타이머를 이용해 loop() 함수를 구현하는 것은, loop() 함수를 미리 정의해두고 타이머에 대한 callback 함수로 등록하는 과정입니다. 그럼 ESP8266 core에서 지정한 시간 간격마다 loop() = callback 함수를 호출해 주는거죠.

실은 ESP8266 에서 제공하는 그리고 Sming 에서 제공하는 대부분의 기능들이 이런 callback 형태로 동작합니다. 사용자가 특정 기능을 실행시키면 ESP8266 core가 해당 기능을 수행하다가 특정 이벤트, 결과가 있을 때만 callback 함수를 호출해서 사용자의 코드가 실행되도록 하는겁니다.

.

Sming 예제 만들기 준비

Sming 개발환경이 설치는 링크의 내용을 참고하세요. 개발환경이 준비되면 예제 폴더에서 Basic_Blink 예제를 복사해서 원하는 폴더에 넣어둡니다. 그리고 아래 과정대로 불러와보세요.

  • Basic_Blink 폴더를 원하는 이름으로 변경 (여기서는 Blink_Test)
  • Basic_Blink 폴더안에 있는 [.project] 파일을 텍스트 에디터로 엽니다.
  • Basic_Blink 이름을 Blink_Test로 바꿉니다. 같은 이름의 프로젝트가 있으면 프로젝트가 열리지 않기 때문에 바꿔준겁니다.
  • 이클립스에서 [File -> Import -> General -> Existing Project into Workspace] 선택
  • [Select root directory] 라인에서
  • Blink_Test 프로젝트 폴더를 선택
  • Makefile-user.mk 파일을 선택해서 엽니다.
  • 아래 내용들 앞의 # 을 제거해서 주석을 해제합니다.
    • COM_PORT 는 보드가 인식된 포트에 맞게 숫자를 수정해주세요
MODULES = app
EXTRA_INCDIR = include
COM_PORT = COM4
COM_SPEED	= 115200
SPI_MODE = dio
  • 프로젝트에서 마우스 오른쪽 키 – Build (이상없이 빌드 되어야 함)

빌드가 성공하면 개발할 준비가 된겁니다. 새로운 프로젝트를 만들때는 위 순서대로 준비하세요.

이제 예제 프로젝트를 만들어서 테스트를 해볼건데, 여기서는 NodeMCU 보드(ESP12E 기반, flash size = 4MByte)를 이용합니다. 다른 ESPxx 보드도 유사하게 사용할 수 있는데… Flash 메모리 사이즈가 조금씩 틀려서 펌웨어 업로드 할 때 설정이 조금씩 다를 수 있습니다. 이런 경우는 자신이 가진 Flash 사이즈를 확인하고 Makefile-user.mk 파일에 아래 내용을 넣어줘야 합니다. (링크 참고)

  • SPI_SIZE = 512K

한 가지 더 확인하고 시작하겠습니다. NodeMCU 보드를 보면 보드에 D0 ~ D10, A0 핀들이 표기되어 있습니다. 그런데 Sming 프로그래밍을 할 때는 GPIO 넘버를 사용합니다. 따라서 자신이 가진 보드의 GPIO 배치를 확인하고 원하는 GPIO 숫자를 소스코드에 기입해야 합니다. ESP12 기반 NodeMCU 보드의 경우 아래 이미지들을 참고하시고 다른 보드를 사용하는 경우 인터넷 검색으로 GPIO 배치를 확인하세요.

Node-MCU-Pin-Out-Diagram1

Digistump_Oak_Pinout_Diagram

.

LED Blink (Digital output) 테스트

ESP 보드가 가진 GPIO 핀들을 컨트롤하는 예제부터 만들어 보겠습니다. 그 중 가장 기본인 digital output 기능을 LED로 테스트 할겁니다. 프로젝트 이름은 앞서 새로 생성한 Basic_Blink 입니다.

LED와 1K 저항 하나를 준비해서 ESP8266 보드에 아래처럼 연결합니다.

  • ESP8266 GPIO 12 ==> 저항 ==> LED (+)
  • LED (-) ==> ESP8266 GND

[app/application.cpp] 파일을 엽니다. 앞으로 소개할 대부분의 예제들이 이 파일을 사용합니다. 사용자 코드의 시작점입니다. 파일에 아래 내용을 넣습니다.

#include <user_config.h>
#include <SmingCore/SmingCore.h>

#define LED_PIN 12 // GPIO12 (D6)

Timer procTimer;
bool state = true;

void loop()
{
	digitalWrite(LED_PIN, state);
	state = !state;
}

void init()
{
	pinMode(LED_PIN, OUTPUT);
	procTimer.initializeMs(1000, loop).start();
}

소스코드는 init() 초기화 함수와 loop() 반복 함수로 구성되어 있습니다. init() 함수에서는 LED가 연결된 GPIO 12 핀을 pinMode() 함수로 초기화하고 타이머를 시작합니다. 타이머는 1초마다 호출되도록 설정되어 있고 타이머가 호출될 때 callback 함수인 loop() 함수를 실행합니다.

	procTimer.initializeMs(1000, loop).start();

고로 1초마다 loop() 함수가 실행되면서 LED 핀을 on/off 시키겠죠. 아두이노와 동일하게 pinMode() 함수로 초기화하고 digitalWrite() 함수로 on/off 상태를 바꿔줍니다.

이클립스의 왼쪽 Project Explorer 윈도에서 [Basic_Blink 프로젝트 우클릭 – 빌드]를 해줍니다. 빌드가 성공적으로 끝나면 [out/firmware] 폴더가 생기고 여기에 ESP8266 에 올릴 펌웨어가 생성됩니다.

이클립스의 오른쪽 [Make Target] 탭에서 Basic_Blink 프로젝트를 선택하세요. 하위 메뉴에 [flash]가 있을겁니다. flash 를 더블클릭하면 생성된 펌웨어를 ESP8266 모듈에 올려줍니다. 아두이노의 [업로드] 버튼과 같은 역할을 합니다. 단 Makefile-user.mk 파일에 COM 포트 설정이 정확해야 합니다.

주의!!! NodeMCU 등의 보드는 리셋 신호를 받고 자동으로 펌웨어 업데이트 모드로 들어갑니다. 기타 보드의 경우 펌웨어 업데이트 모드로 들어가는 방법이 다를 수 있습니다.

v2

.

LED Button (Digital input) 테스트

이번엔 버튼으로 digital input 기능을 테스트 해보겠습니다. Basic_Button 프로젝트를 새로 셋업하세요.

앞서 사용한 LED를 그대로 연결해 둔 상태에서 버튼을 추가로 연결할겁니다. push(tactile) 버튼과 10K 저항을 준비합니다.

버튼은 GPIO14에 추가로 연결해줍니다. 버튼은 Pull-down 저항으로 연결합니다.

  • ESP8266 GPIO14 ==> 버튼 다리 1
  • ESP8266 3V ==> 버튼 다리 2
  • ESP8266 GND ==> 10K 저항 ==> 버튼 다리 2

[app/application.cpp] 파일에 아래 소스코드를 올려줍니다.

#include <user_config.h>
#include <SmingCore/SmingCore.h>

#define BUTTON_PIN 14 // GPIO14 (D5)
#define LED_PIN 12 // GPIO12 (D6)

Timer procTimer;
void loop();

void init() {
	// Set GPIO mode
	pinMode(BUTTON_PIN, INPUT);
	pinMode(LED_PIN, OUTPUT);
	// Start arduino style loop
	procTimer.initializeMs(50, loop).start();
}

void loop() {
	bool isPressed = digitalRead(BUTTON_PIN);
	if(isPressed)
		digitalWrite(LED_PIN, HIGH);
	else
		digitalWrite(LED_PIN, LOW);
}

앞선 LED 예제와 유사합니다. init() 함수에서 LED와 버튼 핀을 OUTPUT, INPUT 모드로 초기화 했습니다. 그리고 타이머를 시작하는데 시간 간격을 50ms 로 지정했습니다. 버튼 입력을 놓치지 않고 체크하려면 가급적 짧은 시간 간격으로 체크해야 하기 때문입니다.

타이머로 호출되는 loop() 콜백 함수는 버튼 입력을 체크하고, 입력 상태에 맞게 LED를 on/off 시킵니다. 버튼 입력은 digitalRead() 함수로 체크하고, LED 출력은 digitalWrite() 함수를 사용했습니다. 아두이노 함수와 사용법이 똑같습니다.

빌드 후 펌웨어를 올려서 버튼 입력에 따라 LED가 동작하는지 확인하세요.

.

LED dimming (PWM – Analog output) 테스트

이제 아날로그 출력을 PWM으로 구현하는 예제를 테스트 해보겠습니다. 프로젝트 폴더를 새로 만들어서 이클립스에 불러오세요.

LED만 연결되어 있는 상태면 테스트가 가능합니다. 여기서는 GPIO 12 핀에 LED를 연결했는데 원하신다면 다른 핀에 연결해도 됩니다.

소스코드는 아래 링크의 소스코드를 사용하세요.

일단은 소스코드 빌드해서 펌웨어 업로드까지 해보세요.

소스코드 최상단에 보면 HW PWM을 사용하기위한 라이브러리와 사전 작업이 정의되어 있습니다.

#include <HardwarePWM.h>

uint8_t pins[8] = { 4, 5, 0, 2, 15, 13, 12, 14 }; // List of pins that you want to connect to pwm
HardwarePWM HW_pwm(pins, 8);

ESP8266 보드에서는 GPIO 0, 2, 4, 5, 12, 13, 14, 15 핀이 PWM 핀입니다. NodeMCU 보드의 경우 PWM 핀들은 핀 넘버 앞에 ~ 마크가 있습니다. Sming 에서 (동시) 사용가능한 PWM 핀은 8개입니다. 이 핀들을 pins[] 배열에 넣어서 초기화 해주는 작업이 위 코드입니다.

PWM 테스트를 하는 김에 Serial 통신 기능도 함께 테스트를 하겠습니다. init() 초기화 함수가 실행될 때 디버그 메시지를 PC로 출력하기 위해 Serial 통신 기능을 초기화 해줬습니다.

void init() {
	Serial.begin(SERIAL_BAUD_RATE); // 115200 by default
	Serial.systemDebugOutput(true); // Enable debug output to serial
	......

SERIAL_BAUD_RATE 는 통신 속도인데 115200으로 지정했습니다. PC의 시리얼 모니터 프로그램도 이 속도와 똑같이 맞춘다음 COM 포트를 열어야 합니다. 원하신다면 바꿔서 사용하세요.

그리고 앞서 지정한 8개의 PWM 핀에 analogWrite() 함수를 사용해서 출력합니다.

	// Setting PWM values on 8 different pins
	HW_pwm.analogWrite(4, 22222);
	HW_pwm.analogWrite(5, 11111);
	HW_pwm.analogWrite(0, 22222);
	HW_pwm.analogWrite(2, 11111);
	HW_pwm.analogWrite(15, 22222);
	HW_pwm.analogWrite(13, 11111);
	HW_pwm.analogWrite(12, 22222);
	HW_pwm.analogWrite(14, 11111);

아두이노의 analogWrite() 함수와 거의 같은데 2가지 차이점이 있습니다.

아두이노에서는 analogWrite() 함수를 단독으로 사용하는데, Sming에서는 파일 최상단에서 정의한 HW_pwm 의 함수로 사용합니다. 즉, HW_pwn.analogWrite() 형태로 사용하는 점이 틀립니다.

그리고 아두이노는 0~255 까지의 값을 사용하는데, 여기서는 0~22222 까지의 값을 사용할 수 있습니다.

LED 밝기를 조금씩 변화시키려면 타이머로 callback 함수를 주기적으로 호출하게 만들어야겠죠?

	procTimer.initializeMs(100, doPWM).start();

100ms 간격으로 doPWM() 함수가 호출되도록 만들었습니다.

doPWM() 함수는 LED 밝기를 조금씩 변화시키는 역할을 합니다. 현재 LED 밝기를 저장하는 변수를 만들어두고 이 값을 doPWM() 함수가 호출될 때 마다 변화시킵니다. 그리고 analogWrite() – PWM 함수로 실제 밝기 조절을 합니다.

	HW_pwm.analogWrite(12, i);
	Serial.print("current var = ");
	Serial.print(i);
	Serial.println();

GPIO 12 번 핀을 변수 i 값으로 PWM 출력한겁니다. i 값은 0~22222 범위여야 합니다.

init() 함수에서 Serial 통신 초기화도 했었죠? 그래서 테스트 삼아 Serial 출력도 여기서 해봤습니다. Serial.print() 함수는 문자열 또는 변수 값을 출력하는 함수고, Serial.println() 함수는 마지막에 줄 넘김 문자까지 붙여주는 함수입니다.

PC에 USB로 연결된 상태에서 Serial monitor 프로그램을 실행하세요. 그리고 통신속도(baud rate)와 COM 포트를 맞춰서 open 하면 ESP8266이 출력하는 메시지를 받아볼 수 있습니다.

.

Potentiometer (Analog input) 테스트

PWM을 이용한 아날로그 출력을 테스트 했으니 아날로그 입력도 테스트 해보겠습니다. 테스트를 위해 포텐셔미터를 추가로 연결하고, 포텐셔미터로 LED 밝기를 조절하도록 만들어 보겠습니다.

LED 는 GPIO 12 핀에 연결하고 포텐셔미터를 ADC0(A0) 아날로그 핀에 연결할겁니다. 포텐셔미터는 아래와 같이 연결하세요.

  • 포텐셔미터 1 핀 ==> ESP8266 3V
  • 포텐셔미터 2 핀 (가운데 핀) ==> ESP8266 ADC0(A0)
  • 포텐셔미터 3 핀 ==> ESP8266 GND

프로젝트를 새로 만들고 아래 소스코드를 사용하세요.

앞선 예제에서 아날로그 출력을 위해 Hardware PWM을 사용했습니다. 여기서는 Sming에서 제공하는 또다른 PWM인 Driver PWM을 사용해 보겠습니다. Driver PWM을 위해 파일 최상단에 아래처럼 ledPWM을 선언해줬습니다.

DriverPWM ledPWM;

초기화 함수인 init() 함수에서는 LED 핀 초기화, PWM 초기화, Serial 통신 초기화, 타이머 시작 작업을 순서대로 해줍니다.

void init() {
	// Set GPIO mode
	pinMode(LED_PIN, OUTPUT);
	// initialize PWM
	ledPWM.initialize();
	// Setup serial
	Serial.begin(SERIAL_BAUD_RATE); // 115200 by default
	Serial.systemDebugOutput(true); // Enable debug output to serial
	// Start arduino style loop
	procTimer.initializeMs(50, loop).start();
}

이제 타이머가 50ms 간격으로 loop() 함수를 주기적으로 호출해 줄겁니다. 여기서 포텐서미터의 값을 읽어서 LED 가 연결된 PWM 핀에 아날로그 출력을 해주면 됩니다.

void loop() {
	int brightness = system_adc_read(); // Replace analogRead(ANALOG_PIN) of arduino
	if(brightness > 1000) brightness = 1000;
	else if(brightness < 0) brightness = 0;
	ledPWM.analogWrite(LED_PIN, brightness/10);
}

아두이노에서는 아날로그 핀에서 값을 읽을 때 analogRead() 함수를 사용합니다. Sming 에서는 system_adc_read() 함수가 그 역할을 대신합니다. 아날로그 핀도 하나 뿐이기 때문에 따로 핀 넘버를 입력할 필요도 없습니다.

	int brightness = system_adc_read(); // Replace analogRead(ANALOG_PIN) of arduino

system_adc_read()로 읽은 값을 Serial로 출력하도록 코드를 넣어서 확인해보세요. 그럼 0~1023 까지의 값을 받게 될겁니다.

그런데 Driver PWM 을 이용해서 쓸 수 있는 값은 0~100(?) 까지입니다. 그래서 값을 약간 변형해줘야 합니다. 아날로그 핀으로 읽은 값을 10으로 나눠주면 대략 비슷해지겠죠.

	int brightness = system_adc_read(); // Replace analogRead(ANALOG_PIN) of arduino
	if(brightness > 1000) brightness = 1000;
	else if(brightness < 0) brightness = 0;
	ledPWM.analogWrite(LED_PIN, brightness/10);

소스코드를 빌드해서 펌웨어를 업로드 해보면 포텐셔미터의 움직임에 따라 LED 밝기가 바뀔겁니다.

.

각종 센서, 디스플레이 제어

기본적인 digital/analog 입출력은 테스트 해보았으니 간단한 센서류는 위 예제들을 변형해서 제어할 수 있을겁니다. 그 밖에 OneWire/I2C/SPI 통신을 사용하거나, 전용 통신 방법을 사용하는 센서-모듈의 경우는 전용 라이브러리를 사용하게 됩니다. 이때는 아두이노용으로 개발된 라이브러리들을 가져와서 사용하면 됩니다.

그 중 자주 사용하는 센서, 모듈의 경우 Sming framework 에 라이브러리와 예제가 이미 포함되어 있습니다.

다른 예제들 확인하기

참고자료