라즈베리 파이에서 flask를 사용해 REST API를 구현하여 스마트폰이나 컴퓨터에서 원격으로 센서값을 읽거나 장치를 제어할 수 있도록 간단하게 구현 해 보았다.
HTTP GET과 POST method를 사용할 수 있도록 해 놓았고 POST method에서 파라미터는 JSON으로 넘겨주면 된다.
아래 코드가 구현해 놓은 API는 다음과 같다.
* /api/led :
method: POST
parameter: {'ledId': [id], 'val': [OnOff]} - [id]는 조작하고자 하는 LED번호, [OnOff]에는 LED를 켜고 싶으면 1, 끄고 싶으면 0
return: 명령이 실행되고난 후 LED 상태
* /api/led :
method: POST
parameter: {'ledId': [id], 'val': [OnOff]} - [id]는 조작하고자 하는 LED번호, [OnOff]에는 LED를 켜고 싶으면 1, 끄고 싶으면 0
return: 명령이 실행되고난 후 LED 상태
* /api/sw :
method: POST
parameter: {'id': [id]} - [id]는 상태를 알고 싶은 스위치 번호
return: 현재 스위치 상태
* /api/sensor/[sensorName] :현재 sensorName에 올 수 있는 센서는 temperature, humidity, pressure, light
method:GET
return:지정한 센서에서 읽은 값
* /api/sensors :
method: GET
return: 전체 센서 값 (JSON 포맷)
* /api/sensors :
method: POST
parameter: {'sensorList': [sensor list]} -값을 읽고 싶은 센서 이름. 한개 이상이 올 수 있고 복수개일 경우 ','로 구분
return: 지정한 센서에서 읽은 값 (JSON 포맷)
* /api/exec :
method: POST
parameter: {'cmd': [command line]} - [commnad line]은 라즈베리 파이에서 실행할 명령
return: 명령 실행 결과
코드를 실행하면 라즈베리 파이에 다음과 같은 화면이 나오게 된다.
PC의 브라우저에 [ip]:5000/api/sensors 를 입력해 주면 다음과 같은 결과를 얻을 수 있다. 이 경우 4개의 센서값을 모두 JSON 포맷으로 리턴해 준다.
[ip]:5000/api/sensor/temperature 를 읿력해 주면 다음과 같이 온도센서 값만 리턴 해 준다.
지금까지는 브라우저의 주소창에 입력했는데 이 경우 GET method를 사용하는 것이다. POST method를 확인하려면 curl, postman, rested 같은 툴을 사용해야 한다. 여기서는 파이어폭스에 설치한 rested를 사용했다.
앞으로 파란색 부분은 저 값으로 고정시켜 놓고 빨간색 부분만 바꿔주면서 테스트 하면 된다. 위의 사진은 api/led 를 호출해 원하는 LED를 on/off 하는 예제이다. 소스코드의 @app.route('/api/led', methods = ['POST']) 에서 빨간색 굵은 부분이 주소가 된다. 단 여기서는 flask 서버가 5000번 포트를 사용하고 있으므로 주소 뒤에 ':5000'을 추가해 줘야 한다.
Request Body 부분에 id와 val 이라는 두개의 키가 있어 id의 값 부분에 조작할 LED 번호를 넣어주고, val의 값 부분에 LED on/off 를 넣어주면 된다.
실행이 되면 바뀐 LED 상태값이 리턴된다. 위의 예에서는 4번 LED를 ON 시킨 것이다. 맨 마지막 부분에 결과로 'ON'이 리턴되었다.
라즈베리 파이 화면을 보면 이번엔 /api/led 주소로 POST emethod를 사용했음을 확인할 수 있다.
위의 화면은 LED를 끄는 것이다. 나머지 부분은 동일하고 val 의 값만 '0'으로 보내주면 된다.
다음은 /api/sw를 통해 스위치 상태를 확인해 본 것이다. URL 부분이 /api/led에서 /api/sw로 바뀌었다. 또한 이번엔 파라미터로 id만 보내주면 된다. 여기서는 2번 스위치의 상태를 확인해 본 것이다. 현재 스위치 상태 값이 리턴된 걸 확인할 수 있다.
다음은 /api/sensors를 호출하는데 위쪽에서는 GET method로 호출해서 모든 센서 값을 읽어왔는데 여기서는 POST method를 사용해 값을 읽기를 원하는 센서만 지정할 수 있다. sensorList 의 값 부분에 원하는 센서 이름을 넣어주면 된다. 두개 이상을 넣을 땐 센서 이름 사이에 ','로 구분해 주면 된다. 여기서는 temperature와 humidity 센서 값을 읽도록 해 보았다. 결과값으로 두 센서값이 JSON 포맷으로 리턴되었다.
마지막으로는 라즈베리 파이에서 명령을 실행할 수도 있다. /api/exe에 POST method로 실행하기 원하는 명령을 보내주면 된다. 실행된 결과가 리턴된다. 이 방법을 이용하면 라즈베리 파이에 연결해 놓은 장치가 API를 제공해 주지 않거나 python module이 없고 CLI로 제어하는 프로그램만 있어도 원격에서 제어가 가능해 진다.
전체 코드는 다음과 같다. 이 코드는 Raspberry Pi 4에서 python3를 사용해 동작시켜 보았는데 python3만 지원하는 환경이면 어디에서나 동작하는데 문제는 없을 것이다.
* 여기서는 Raspberry Pi에서 실행되는 서버 코드만 설명하고 있다. 스마트폰 앱에서 제어하려면 해당하는 GET, POST 메소드로 API를 사용하면 된다. 해당 부분 코드 작성은 직접 하지 않아도 postman같이 자동으로 생성해주는 툴을 사용하면 매우 간단하게 사용할 수 있다. 다음 포스트에서는 postman을 사용해 클라이언트의 http request 부분의 코드를 자동으로 생성하는걸 설명하겠다.
#!/usr/bin/python3
from flask import Flask, request, jsonify
import os, ntpath
import json
import subprocess
app = Flask(__name__)
# 실제로는 GPIO library를 사용해 읽은 스위치 값을 리턴해줘야 함
def readSwitch(id):
print ("# readSwitch is called for switch %d" % id)
return 1
# 실제로는 온도센서 값을 읽어 리턴해 줘야 함
def readTemp():
return str(25.3)
# 실제로는 습도센서 값을 읽어 리턴해 줘야 함
def readHumid():
return str(73)
# 실제로는 압력센서 값을 읽어 리턴해 줘야 함
def readPressure():
return str(1025)
# 실제로는 조도센서 값을 읽어 리턴해 줘야 함
def readLight():
return str(419)
def readSensor(s):
return sensorFunc[s]()
sensorFunc = {'temperature': readTemp, 'humidity': readHumid, 'pressure': readPressure, 'light': readLight}
@app.route('/api/led', methods = ['POST'])
def ledSvc():
data = request.json
#ledId = data['id']
#onOff = data['val']
ledId = int(data['id'])
onOff = int(data['val'])
if onOff == 0:
newLedState = 'OFF'
# 여기에 해당 LED(LED번호는 변수 ledId에 들어 있음)를 끄는 코드를 집어 넣어 줌
else:
newLedState = 'ON'
# 여기에 해당 LED(LED번호는 변수 ledId에 들어 있음)를 켜는 코드를 집어 넣어 줌
print ("Turn LED %d to %s" % (ledId, newLedState))
return newLedState
@app.route('/api/sw', methods = ['POST'])
def swStatus():
data = request.json
ledId = int(data['id'])
swState = readSwitch(ledId) # readSwitch() 함수에서 스위치(ledId) 값을 읽어 리턴해 줌
print ("Switch %d state : %d" % (ledId,swState))
return str(swState)
@app.route('/api/sensor/<string:sensorName>', methods = ['GET'])
def readSensor(sensorName):
try:
ret = sensorFunc[sensorName]()
return ret
except:
return 'Wrong Sensor'
@app.route('/api/sensors', methods = ['GET', 'POST'])
def readSensors():
if request.method == 'GET':
result = {}
for s in sensorFunc:
result[s] = sensorFunc[s]()
elif request.method == 'POST':
result = {}
sl = request.json['sensorList']
print (sl)
sl = sl.split(',')
for s in sl:
print (s)
result[s] = readSensor(s)
return json.dumps(result)
@app.route('/api/exec', methods = ['POST'])
def externalPgm():
data = request.json
cmds = data['cmd']
print (cmds)
cmdl = cmds.split(' ')
result = subprocess.check_output(cmdl)
return result
if __name__=='__main__':
app.run(host='0.0.0.0', debug=True)
2019년 12월 12일 목요일
2017년 5월 11일 목요일
라즈베리 파이에서 MQTT를 이용해 ESP8266 제어하기
여기서는 라즈베리 파이, ESP8266(또는 와이파이 쉴드를 붙인 아두이노), PC를 MQTT로 연결해 보도록 하겠다.
이번에 하고자 하는 것은 라즈베리파이가 python을 사용해 PC에 있는 MQTT 브로커에 메시지를 보내면 그 메시지가 ESP8266으로 전달되어 거기에 있는 LED를 제어하는 것이다.
MQTT server(브로커)로 사용할 수 있는 옵션이 몇가지 있다. Amazon의 AWS IoT나 AdafruitIO 같은 서비스들은 클라우드 기반의 브로커이다. 각 서비스는 여러가지 장점이 있다. 프로젝트의 필요성에 따라 클라우드 기반 서비스를 사용할 필요가 있을수도 있다.
자신의 호스트에 브로커를 실행하고 싶으면 오픈소스인 moqsuitto가 훌륭한 옵션이다. 이 브로커는 MQTT를 완전히 지원할 뿐 아니라 pub/sub를 위한 커맨드라인 유틸리티까지 제공한다.
윈도우를 사용하면 mosquitto.org에서 바이너리 인스톨러를 받을 수 있다. 리눅스의 경우 패키지 매니져에서 'mosquitto' 또는 'mosquitto-mqtt'를 찾으면 된다. 맥에서는 homebrew를 사용해 mosquitto를 설치할 수 있다.
* 맥에서 설치한 경우 '/usr/local/sbin'을 PATH에 넣어줘야 한다.
Mosquitto는 디폴트로 1883번 포트를 사용하고 보안기능은 다 꺼져 있다. 처음 시작할 때는 이것으로 충분하지만 결국에는 보안기능을 추가해 줄 필요가 있다.
Mosquitto를 verbose 모드로 실행하길 권장한다. 그러면 출력을 보고 어떤 일이 일어나는지에 대한 아이디어를 얻을 수 있을 것이다.
MacMan:~ james$ mosquitto -v
1456272937: mosquitto version 1.4.5 (build date 2015-11-09 14:23:46-0800) starting 1456272937: Using default config.
1456272937: Opening ipv4 listen socket on port 1883.
1456272937: Opening ipv6 listen socket on port 1883.
서버 코드를 verbose 모드에서 실행시키고, 추가로 두개의 명령어 프롬프트를 열어준다. 각각의 명령어 프롬프트에서 메시지 전송 테스트를 위해 mosquitto_pub와 mosquitto_sub를 사용할 것이다.
먼저 클라이언트가"debug"라는 토픽을 subscribe하게 해 준다. 첫번째 명령어 프롬프트에서 아래 명령을 입력해 주면 된다.
$ mosquitto_sub -h 127.0.0.1 -i testSub -t debug
* -h : host flag는 mosquitto가 실행되고 있는 서버를 지정. 여기서는 localhost
* -i : identity flag 는 옵션이다. Client ID가 지정되지 않으면 mosquitto_sub가 임의로 만듬
* -t : topic flag 는 subscribe 하려고 하는 topic을 지정. 여기서는 "debug"
서버에서는 topic을 만들지 않았다는걸 주목해라. Topic은 subscriber나 publisher가 처음 지정할 때 만들어진다.
이제 topic을 기다리는 클라이언트를 가지고 있으므로, 다른 클라이언트가 그 topic을 publish하게 해 본다.
$ mosquitto_pub -h 127.0.0.1 -i testPublish -t debug -m 'Hello World'
이제 첫번째 명령어 프롬프트 화면을 보면 publish된 메시지가 와 있는것을 확인할 수 있다. mosquitto_pub와 mosquitto_sub 명렁 모두 --help 플래그로 확인 해 봐라. 매우 여러가지 유용한 옵션들을 찾아볼 수 있을 것이다.
이제 mosquitto 브로커가 정상 동작하고 있는것이 확인되었으므로 디바이스와 통신을 하게 해 보자.
라즈베리 파이에서 MQTT 메지시를 보내고 받을 수 있는 옵션으로는 mosquitto를 설치하는 것이다. 그러면 라즈베리 파이에서도 동일하게 mosquitto_pub와 mosquitto_sub 명령어를 사용할 수 있다.
다른 방법으로는 자신이 선호하는 언어용 MQTT 라이브러리를 사용하는 것이다. 여기서는 파이선을 사용한다.
여기서 사용할 MQTT library는 Paho Python Client이다. 오픈소스로 가장 최신의 MQTT를 지원한다.
설치는 간단하다. 먼저 'pip'를 설치하고 나서 아래 명령을 실행한다.
$ pip install paho-mqtt
* 만일 python3를 사용한다면 'pip' 대신 'pip3'를 사용해야 하는걸 잊지 마라.
Paho는 MQTT 서버와 통신을 매우 쉽게 만들어 준다.
import paho.mqtt.publish as publish
import time
print ("Sending 0...")
publish.single("ledStatus", "0", hostname="macman")
time.sleep(1)
print ("Sending 1...")
publish.single("ledStatus", "1", hostname="macman")
진짜 간단하다. 스크립트에 서버가 실행되고 있는 호스트 이름(여기서는 'macman'), Topic (여기서는 'ledStatus'), 메시지(여기서는 '0' 또는 '1')만 알려주면 된다.
이 MQTT 튜토리얼 예제에서는 '0'을 보내고 1초 기다린 후에 '1'을 보낸다. 이 동작은 ESP8266에 있는 LED가 잠깐동안 꺼졌다 다시 켜지게 만든다.
이제 RPi가 MQTT 메시지를 보내게 만들었으니 그 메시지를 받도록 만들어 보자. 다음 섹션은 ESP8266이 topic "ledStatus"의 메시지를 받아 실제로 LED를 깜빡이게 만들 것이다.
PubSubClient는 아두이노 기반의 MQTT 클라이언트이다. 단지 몇 줄의 코드만으로 매우 쉽게 topic을 subscribe하거나 새로운 topic을 publish할 수 있다.
여기서는 ESP8266을 프로그래밍 하기 위해 아두이노 IDE를 사용했다.
아두이노 IDE에 PubSubClient를 설치하는건 간단하다. Github에서 수동으로 설치하거나 또는 Arduino Package Installer를 사용할 수 있다.
Ethernet 쉴드가 장착된 Uno와 ESP8266간에 스위칭에는 별도의 추가적인 노력이 필요 없다. 라이브러리는 양쪽 모두 잘 동작한다.
PubSubCilent에 포함되어 있는 예제는 매우 훌륭하다. 예제에는 특히 MQTT에 대한 ESP8266 코드도 들어있다. 처음 시작할 때 그것들도 꼭 확인해 보기 바란다.
아래는 WiFi에 연결하고 "ledStatus" topic을 subscribe하는 코드이다. ESP8266이 메시지를 받으면 그에 따라 ledPin을 on/off한다.
#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
// Connect to the WiFi
const char* ssid = "Dear John";
const char* password = "password123";
const char* mqtt_server = "macman";
WiFiClient espClient;
PubSubClient client(espClient);
const byte ledPin = 0; // Pin with LED on Adafruit Huzzah
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i=0;i<length;i++) {
char receivedChar = (char)payload[i];
Serial.print(receivedChar);
if (receivedChar == '0')
// ESP8266 Huzzah outputs are "reversed"
digitalWrite(ledPin, HIGH);
if (receivedChar == '1')
digitalWrite(ledPin, LOW);
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266 Client")) {
Serial.println("connected");
// ... and subscribe to topic
client.subscribe("ledStatus");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup()
{
Serial.begin(115200);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
pinMode(ledPin, OUTPUT);
}
void loop()
{
if (!client.connected()) {
reconnect();
}
client.loop();
}
모든 마법은 "callback" 함수 안에서 일어난다. 실제 코드에서는 받은 메시지를 처리하는 함수를 작성하고 싶을 것이다. 여기서는 topic에 대해 한글자 메시지만을 보내기 때문에 이 간단한 코드로 충분하다.
이 MQTT 튜토리얼은 매우 기초적이다. 하지만 라즈베리 파이, ESP8266을 MQTT 브로커가 실행되고 있는 PC에 연결하는데 필요한 최소한의 것을 보여준다.
다음에는 MQTT를 통해 어떻게 더 복잡한 메시지를 보낼 수 있는가를 설명하겠다.
이번에 하고자 하는 것은 라즈베리파이가 python을 사용해 PC에 있는 MQTT 브로커에 메시지를 보내면 그 메시지가 ESP8266으로 전달되어 거기에 있는 LED를 제어하는 것이다.
MQTT for PC
MQTT server(브로커)로 사용할 수 있는 옵션이 몇가지 있다. Amazon의 AWS IoT나 AdafruitIO 같은 서비스들은 클라우드 기반의 브로커이다. 각 서비스는 여러가지 장점이 있다. 프로젝트의 필요성에 따라 클라우드 기반 서비스를 사용할 필요가 있을수도 있다.
Mosquitto
자신의 호스트에 브로커를 실행하고 싶으면 오픈소스인 moqsuitto가 훌륭한 옵션이다. 이 브로커는 MQTT를 완전히 지원할 뿐 아니라 pub/sub를 위한 커맨드라인 유틸리티까지 제공한다.
윈도우를 사용하면 mosquitto.org에서 바이너리 인스톨러를 받을 수 있다. 리눅스의 경우 패키지 매니져에서 'mosquitto' 또는 'mosquitto-mqtt'를 찾으면 된다. 맥에서는 homebrew를 사용해 mosquitto를 설치할 수 있다.
* 맥에서 설치한 경우 '/usr/local/sbin'을 PATH에 넣어줘야 한다.
Running Mosquitto
Mosquitto는 디폴트로 1883번 포트를 사용하고 보안기능은 다 꺼져 있다. 처음 시작할 때는 이것으로 충분하지만 결국에는 보안기능을 추가해 줄 필요가 있다.
Mosquitto를 verbose 모드로 실행하길 권장한다. 그러면 출력을 보고 어떤 일이 일어나는지에 대한 아이디어를 얻을 수 있을 것이다.
MacMan:~ james$ mosquitto -v
1456272937: mosquitto version 1.4.5 (build date 2015-11-09 14:23:46-0800) starting 1456272937: Using default config.
1456272937: Opening ipv4 listen socket on port 1883.
1456272937: Opening ipv6 listen socket on port 1883.
Testing Mosquitto
서버 코드를 verbose 모드에서 실행시키고, 추가로 두개의 명령어 프롬프트를 열어준다. 각각의 명령어 프롬프트에서 메시지 전송 테스트를 위해 mosquitto_pub와 mosquitto_sub를 사용할 것이다.
Subscribing to MQTT Topic with Mosquitto
먼저 클라이언트가"debug"라는 토픽을 subscribe하게 해 준다. 첫번째 명령어 프롬프트에서 아래 명령을 입력해 주면 된다.
$ mosquitto_sub -h 127.0.0.1 -i testSub -t debug
* -h : host flag는 mosquitto가 실행되고 있는 서버를 지정. 여기서는 localhost
* -i : identity flag 는 옵션이다. Client ID가 지정되지 않으면 mosquitto_sub가 임의로 만듬
* -t : topic flag 는 subscribe 하려고 하는 topic을 지정. 여기서는 "debug"
서버에서는 topic을 만들지 않았다는걸 주목해라. Topic은 subscriber나 publisher가 처음 지정할 때 만들어진다.
Publish to MQTT Topic with Mosquitto
이제 topic을 기다리는 클라이언트를 가지고 있으므로, 다른 클라이언트가 그 topic을 publish하게 해 본다.
$ mosquitto_pub -h 127.0.0.1 -i testPublish -t debug -m 'Hello World'
이제 첫번째 명령어 프롬프트 화면을 보면 publish된 메시지가 와 있는것을 확인할 수 있다. mosquitto_pub와 mosquitto_sub 명렁 모두 --help 플래그로 확인 해 봐라. 매우 여러가지 유용한 옵션들을 찾아볼 수 있을 것이다.
이제 mosquitto 브로커가 정상 동작하고 있는것이 확인되었으므로 디바이스와 통신을 하게 해 보자.
MQTT for Raspberry Pi (Python)
라즈베리 파이에서 MQTT 메지시를 보내고 받을 수 있는 옵션으로는 mosquitto를 설치하는 것이다. 그러면 라즈베리 파이에서도 동일하게 mosquitto_pub와 mosquitto_sub 명령어를 사용할 수 있다.
다른 방법으로는 자신이 선호하는 언어용 MQTT 라이브러리를 사용하는 것이다. 여기서는 파이선을 사용한다.
Install MQTT for Python
여기서 사용할 MQTT library는 Paho Python Client이다. 오픈소스로 가장 최신의 MQTT를 지원한다.
설치는 간단하다. 먼저 'pip'를 설치하고 나서 아래 명령을 실행한다.
$ pip install paho-mqtt
* 만일 python3를 사용한다면 'pip' 대신 'pip3'를 사용해야 하는걸 잊지 마라.
Example MQTT Python Code for Raspberry Pi
Paho는 MQTT 서버와 통신을 매우 쉽게 만들어 준다.
import paho.mqtt.publish as publish
import time
print ("Sending 0...")
publish.single("ledStatus", "0", hostname="macman")
time.sleep(1)
print ("Sending 1...")
publish.single("ledStatus", "1", hostname="macman")
진짜 간단하다. 스크립트에 서버가 실행되고 있는 호스트 이름(여기서는 'macman'), Topic (여기서는 'ledStatus'), 메시지(여기서는 '0' 또는 '1')만 알려주면 된다.
이 MQTT 튜토리얼 예제에서는 '0'을 보내고 1초 기다린 후에 '1'을 보낸다. 이 동작은 ESP8266에 있는 LED가 잠깐동안 꺼졌다 다시 켜지게 만든다.
이제 RPi가 MQTT 메시지를 보내게 만들었으니 그 메시지를 받도록 만들어 보자. 다음 섹션은 ESP8266이 topic "ledStatus"의 메시지를 받아 실제로 LED를 깜빡이게 만들 것이다.
MQTT for ESP8266
PubSubClient는 아두이노 기반의 MQTT 클라이언트이다. 단지 몇 줄의 코드만으로 매우 쉽게 topic을 subscribe하거나 새로운 topic을 publish할 수 있다.
여기서는 ESP8266을 프로그래밍 하기 위해 아두이노 IDE를 사용했다.
Installing MQTT for Arduino IDE
아두이노 IDE에 PubSubClient를 설치하는건 간단하다. Github에서 수동으로 설치하거나 또는 Arduino Package Installer를 사용할 수 있다.
Arduino ESP8266 and Uno Note
Ethernet 쉴드가 장착된 Uno와 ESP8266간에 스위칭에는 별도의 추가적인 노력이 필요 없다. 라이브러리는 양쪽 모두 잘 동작한다.
Code Example for Arduino MQTT
PubSubCilent에 포함되어 있는 예제는 매우 훌륭하다. 예제에는 특히 MQTT에 대한 ESP8266 코드도 들어있다. 처음 시작할 때 그것들도 꼭 확인해 보기 바란다.
아래는 WiFi에 연결하고 "ledStatus" topic을 subscribe하는 코드이다. ESP8266이 메시지를 받으면 그에 따라 ledPin을 on/off한다.
#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
// Connect to the WiFi
const char* ssid = "Dear John";
const char* password = "password123";
const char* mqtt_server = "macman";
WiFiClient espClient;
PubSubClient client(espClient);
const byte ledPin = 0; // Pin with LED on Adafruit Huzzah
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i=0;i<length;i++) {
char receivedChar = (char)payload[i];
Serial.print(receivedChar);
if (receivedChar == '0')
// ESP8266 Huzzah outputs are "reversed"
digitalWrite(ledPin, HIGH);
if (receivedChar == '1')
digitalWrite(ledPin, LOW);
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266 Client")) {
Serial.println("connected");
// ... and subscribe to topic
client.subscribe("ledStatus");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup()
{
Serial.begin(115200);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
pinMode(ledPin, OUTPUT);
}
void loop()
{
if (!client.connected()) {
reconnect();
}
client.loop();
}
모든 마법은 "callback" 함수 안에서 일어난다. 실제 코드에서는 받은 메시지를 처리하는 함수를 작성하고 싶을 것이다. 여기서는 topic에 대해 한글자 메시지만을 보내기 때문에 이 간단한 코드로 충분하다.
Conclusion
이 MQTT 튜토리얼은 매우 기초적이다. 하지만 라즈베리 파이, ESP8266을 MQTT 브로커가 실행되고 있는 PC에 연결하는데 필요한 최소한의 것을 보여준다.
다음에는 MQTT를 통해 어떻게 더 복잡한 메시지를 보낼 수 있는가를 설명하겠다.
Labels:
라즈베리 파이,
아두이노,
arduino,
esp8266,
IoT,
Mosquitto,
MQTT,
Raspberry Pi,
RPi,
wifi shield
2017년 1월 5일 목요일
라즈베리 파이 제로 테스트 패드 배치 (Raspberry Pi Test Pad Layout)
라즈베리 파이 제로는 크기가 작다 보니 제대로(?) 활용을 하려면 주변에 몇가지 장치들을 붙여줘야 한다. 특히 USB 허브를 붙이려고 할 때 USB 케이블을 이용하면 케이블이 옆으로 튀어나와 보기에도 안좋고 케이블이 빠지기도 쉽다.
그래서 해결책은 보드의 아래쪽 면에 테스트 패드들을 활용하는 것이다. 기본적인 전원 신호, USB 데이터 신호선, SD카드 신호선들이 나와 있어 이 패드에 납땜을 하거나 아니면 좀 더 깔끔한 방법으로는 스프링이 들어있는 pogo pin을 이용하면 바깥쪽으로 튀어나온 케이블 없이 깔끔하게 주변회로를 부착할 수 있다.
Test pad의 위치는 다음과 같다.
주의할 것은 라즈베리 파이 v1.2와 v1.3의 테스트 패드 위치가 다르다. v1.2는 카메라 커넥터가 없는 초기모델이고 v1.3은 카메라 커넥터가 붙어있는 모델이다. 최근에 구입한 사람이라면 대부분 v1.3이겠지만 인터넷에서 pogo pin으로 연결하는 보드를 구입하는 경우 버젼을 꼭 확인하고 구입해야만 한다.
USB 허브를 연결하려면 PP1, PP6, PP22, PP23에 각각 +5V, GND, D+, D- 선을 연결해 주면 된다.
반대로 전원 공급을 마이크로USB 케이블을 이용하지 않고 라즈베리 파이에 직접 공급하고 싶은 경우에도 PP1, PP6에 +5V, GND를 연결해 줘도 된다.
그래서 해결책은 보드의 아래쪽 면에 테스트 패드들을 활용하는 것이다. 기본적인 전원 신호, USB 데이터 신호선, SD카드 신호선들이 나와 있어 이 패드에 납땜을 하거나 아니면 좀 더 깔끔한 방법으로는 스프링이 들어있는 pogo pin을 이용하면 바깥쪽으로 튀어나온 케이블 없이 깔끔하게 주변회로를 부착할 수 있다.
![]() |
| USB 무선랜 동글을 직접 납땜 |
![]() |
| 스프링이 달려 있는 pogo pin으로 납땜하지 않고 연결 |
Test pad의 위치는 다음과 같다.
![]() |
| 라즈베리 파이 제로 v1.2 (클릭하면 크게 확대해 볼 수 있음) |
![]() |
| 라즈베리 파이 제로 v1.3 (클릭하면 크게 확대해 볼 수 있음) |
주의할 것은 라즈베리 파이 v1.2와 v1.3의 테스트 패드 위치가 다르다. v1.2는 카메라 커넥터가 없는 초기모델이고 v1.3은 카메라 커넥터가 붙어있는 모델이다. 최근에 구입한 사람이라면 대부분 v1.3이겠지만 인터넷에서 pogo pin으로 연결하는 보드를 구입하는 경우 버젼을 꼭 확인하고 구입해야만 한다.
USB 허브를 연결하려면 PP1, PP6, PP22, PP23에 각각 +5V, GND, D+, D- 선을 연결해 주면 된다.
반대로 전원 공급을 마이크로USB 케이블을 이용하지 않고 라즈베리 파이에 직접 공급하고 싶은 경우에도 PP1, PP6에 +5V, GND를 연결해 줘도 된다.
Labels:
라즈베리 파이,
라즈베리 파이 제로,
테스트 패드,
RPi,
RPi zero,
Test pad layout
2016년 9월 22일 목요일
WS2812와 APA102의 차이 비교 (Comparison between WS2812 and APA102)
개별 칼라를 제어할 수 있는 LED strip으로 가장 많이 쓰이는 것이 WS2812B(Adafruit에서는 NeoPixel 이라는 제품명으로 판매)이다. 전원라인(+5V, GND)을 제외하면 단 한개의 신호선으로 수백개의 LED를 제어할 수 있어 매우 편리하다.
하지만 별도의 클럭신호 선 없이 한개의 신호선으로 데이터를 전송하다 보니 타이밍에 매우 민감하다. 아두이노용 WS2812 라이브러리(Adafruit_NeoPixel 등) 소스를 열어 본 사람들은 알겠지만 데이터를 전송하는 부분 코드는 타이밍을 정밀하게 맞춰주기 위해 전송중에는 인터럽트도 disable시켜 놓고 어셈블리 코드로 하드코딩 되어 있다.
최대 800KHz의 속도로 데이터를 전송하는데 만일 128개의 LED를 구동한다고 하면 한번 색을 변경하는데 약 4ms 정도의 시간이 걸리게 된다. 즉 이 시간동안은 프로세서가 다른 작업은 커녕 심지어 인터럽트도 안 걸리고 LED에 데이터를 보내야만 한다.
이는 특히 라즈베리 파이같이 OS를 사용하는 경우 매우 치명적이다. 물론 라즈베리 파이에서도 WS2812를 사용할 수 있게 DMA를 사용해 데이터를 전송하는 라이브러리가 나와 있긴 하지만 이런 라이브러리는 portability가 거의 없다. 즉 라이브러리를 가져다 다른데 포팅하는것이 매우 힘들다.
그래서 이런 문제를 해결한 비슷한 종류의 LED가 몇 종류 나와 있다. 그 중 하나가 APA102이다.
우선 WS2812와 가장 큰 차이점은 전원선(+5V, GND)외에 두개의 신호선(Data, Clk)이 있다는 것이다. (WS2812는 1개의 신호선(Data)만 있음)
그래서 SPI 버스의 MOSI, SCK 에 Data, Clk를 연결해서 SPI 방식으로 데이터를 보내주면 된다. 실제 데이터 전송은 SPI가 담당하기 때문에 프로세서가 데이터를 전송하는 동안 기다릴 필요도 없고 타이밍을 신경 쓸 필요도 없기 때문에 라이브러리 코드도 훨씬 간단해진다.
데이터 전송도 SPI 클럭 기준으로 이루어지기 때문에 16MHz의 속도로도 충분히 전송이 가능하다. (WS2812에 비해 20배 이상의 속도) 그래서 POV같이 LED 색이 빠른 속도로 바뀌어야 하는 경우 WS2812는 전환속도가 느려 적합하지 않지만 APA102는 충분히 빠르게 색을 바꿔줄 수 있다.
라즈베리 파이에서도 DMA를 사용하지 않아도 충분하기 때문에 순전히 파이선으로 만든 라이브러리도 있다.
https://github.com/tinue/APA102_Pi
하지만 별도의 클럭신호 선 없이 한개의 신호선으로 데이터를 전송하다 보니 타이밍에 매우 민감하다. 아두이노용 WS2812 라이브러리(Adafruit_NeoPixel 등) 소스를 열어 본 사람들은 알겠지만 데이터를 전송하는 부분 코드는 타이밍을 정밀하게 맞춰주기 위해 전송중에는 인터럽트도 disable시켜 놓고 어셈블리 코드로 하드코딩 되어 있다.
최대 800KHz의 속도로 데이터를 전송하는데 만일 128개의 LED를 구동한다고 하면 한번 색을 변경하는데 약 4ms 정도의 시간이 걸리게 된다. 즉 이 시간동안은 프로세서가 다른 작업은 커녕 심지어 인터럽트도 안 걸리고 LED에 데이터를 보내야만 한다.
이는 특히 라즈베리 파이같이 OS를 사용하는 경우 매우 치명적이다. 물론 라즈베리 파이에서도 WS2812를 사용할 수 있게 DMA를 사용해 데이터를 전송하는 라이브러리가 나와 있긴 하지만 이런 라이브러리는 portability가 거의 없다. 즉 라이브러리를 가져다 다른데 포팅하는것이 매우 힘들다.
그래서 이런 문제를 해결한 비슷한 종류의 LED가 몇 종류 나와 있다. 그 중 하나가 APA102이다.
우선 WS2812와 가장 큰 차이점은 전원선(+5V, GND)외에 두개의 신호선(Data, Clk)이 있다는 것이다. (WS2812는 1개의 신호선(Data)만 있음)
그래서 SPI 버스의 MOSI, SCK 에 Data, Clk를 연결해서 SPI 방식으로 데이터를 보내주면 된다. 실제 데이터 전송은 SPI가 담당하기 때문에 프로세서가 데이터를 전송하는 동안 기다릴 필요도 없고 타이밍을 신경 쓸 필요도 없기 때문에 라이브러리 코드도 훨씬 간단해진다.
데이터 전송도 SPI 클럭 기준으로 이루어지기 때문에 16MHz의 속도로도 충분히 전송이 가능하다. (WS2812에 비해 20배 이상의 속도) 그래서 POV같이 LED 색이 빠른 속도로 바뀌어야 하는 경우 WS2812는 전환속도가 느려 적합하지 않지만 APA102는 충분히 빠르게 색을 바꿔줄 수 있다.
라즈베리 파이에서도 DMA를 사용하지 않아도 충분하기 때문에 순전히 파이선으로 만든 라이브러리도 있다.
https://github.com/tinue/APA102_Pi
2016년 8월 26일 금요일
라즈베리 파이 제로를 USB 케이블 하나로 사용하기 (Use Raspberry Pi Zero as RNDIS/Ethernet gadget through single USB cable)
라즈베리 파이를 사용할 때 USB 케이블 하나로 전원공급부터 컴퓨터와 네트웍 통신을 해서 ssh로 접속해 제어할 수 있는 방법이 라즈베리 파이 제로를 RNDIS/Ethernet gadget으로 만들어 주는 것이다.
이를 위해서는 먼저 라즈베리 파이 부팅 이미지가 들어있는 sd카드를 PC에 연결해서 내용을 보면 다음과 같은 파일들이 들어 있다.
위의 파일들 중에 cmdline.txt와 config.txt 두개의 파일을 수정해야 한다.
먼저 config.txt 파일을 에디터로 열어준다.
파일의 맨 마지막 부분에 'dtoverlay=dwc2' 를 추가하고 저장해 준다.
다음은 cmdline.txt 파일을 오픈해서 rootwait 뒷부분에 'modules-load=dwc2,g_ether'를 추가하고 저장해 준다. 여기서 주의할 것은 이 파일에는 엔터키는 사용하지 않는다. 모든 파라미터는 스페이스로 구분된다.
이제 모든 변경이 끝났으니 sd 카드를 제거해서 라즈베리 파이 제로에 꼽아주면 된다.
맥의 경우 RNDIS 드라이버를 설치해야 하는데 관련된 내용은 HoRNDIS - 맥용 RNDIS 드라이버 를 참고하면 된다. 단 아쉬운 점은 HoRNDIS가 El Capitan(Mac OS X 10.11)을 지원하지 않아 현재까지는 Yosemite가 설치된 맥에서 까지만 사용할 수 있다.
윈도우의 경우 윈도우 7용 RNDIS 드라이버 설치하기 를 참고해 RNDIS 드라이버를 설치해 주면 된다.
이제 ssh 클라이언트를 실행해 연결해 보자. 아래 화면은 윈도우용 Tera Term이다. Hostname을 'raspberrypi.local'로 해서 ssh로 연결하면 된다.
첫번째 연결하는 것이면 아래와 같이 security warning이 뜬다. 'Add this machine and its key to the known hosts list'를 체크하고 Continue를 누르면 다음번 부터는 나타나지 않을 것이다.
Username과 Passphrase를 묻는 화면이 나타난다.
디폴트 사용자 이름은 'pi', 암호는 'raspberry' 이다.

로그인이 성공해 프롬프트가 나왔다.
* 'raspberrypi.local' 이름으로 연결이 가능한 건 애플의 Bonjour 기술 덕분이다. 만일 윈도우에서 저 이름으로 접속을 하는데 연결이 안된다면 아이튠즈를 먼저 설치한 다음 다시 시도하면 문제 없이 될 것이다. (아이튠즈를 설치하면 윈도우용 bonjour이 같이 설치되기 때문이다.)
위의 방법으로 하면 편하긴 한데 한가지 문제가 있다.
ifconfig 명령으로 확인해 보면 라즈베리 파이 제로의 네트웍 인터페이스는 usb0인데 그 ip address는 dhcp로 받아오기 때문에 매번 바뀌게 된다.
IP 주소를 고정하려면 /etc/network/interfaces 파일을 수정해 줘야 한다.
에디터로 파일을 열어보면 다음과 같은 내용이 들어 있다.
파일 맨 아래쪽에 빨간색 부분을 추가한 다음 저장해 주면 된다. 아래에서는 라즈베리 파이의 IP주소를 '192.168.10.2'로 지정했다.
이제 라즈베리 파이를 재시동하고 확인해 보면 ip 주소가 바뀐걸 확인할 수 있다.
라즈베리 파이의 설정에 맞춰 pc의 설정도 변경 해 줘야 한다.
윈도우의 경우 먼저 제어판에서 '네트워크 및 공유 센터'를 선택한 다음 '어댑터 설정 변경'을 누른다.
어댑터 중에 RNDIS/Ethernet Gadget을 찾아 선택한 다음 마우스 오른쪽 버튼을 눌러 메뉴에서 '속성'을 선택한다.
Internet Protocol Version 4 (TCP/IPv4)를 선택하고 '속성' 버튼을 누른다.
IP 설정을 넣어주면 된다. 라즈베리 파이가 192.168.10.2를 사용하고 있으므로 윈도우도 192.168.10.x 를 사용해야만 한다. (x는 1~254 사이에서 2를 제외한 임의의 숫자중에 하나)
이제 설정이 끝났으므로 pc에서 라즈베리 파이로 ping을 날려 응답이 오는걸 확인해 보면 된다.
이를 위해서는 먼저 라즈베리 파이 부팅 이미지가 들어있는 sd카드를 PC에 연결해서 내용을 보면 다음과 같은 파일들이 들어 있다.
![]() |
| Raspian SD card 내의 파일 |
먼저 config.txt 파일을 에디터로 열어준다.
![]() |
| config.txt 파일 |
파일의 맨 마지막 부분에 'dtoverlay=dwc2' 를 추가하고 저장해 준다.
다음은 cmdline.txt 파일을 오픈해서 rootwait 뒷부분에 'modules-load=dwc2,g_ether'를 추가하고 저장해 준다. 여기서 주의할 것은 이 파일에는 엔터키는 사용하지 않는다. 모든 파라미터는 스페이스로 구분된다.
![]() |
| cmdline.txt 수정 전 |
![]() |
| cmdline.txt 수정 후 |
이제 모든 변경이 끝났으니 sd 카드를 제거해서 라즈베리 파이 제로에 꼽아주면 된다.
맥의 경우 RNDIS 드라이버를 설치해야 하는데 관련된 내용은 HoRNDIS - 맥용 RNDIS 드라이버 를 참고하면 된다. 단 아쉬운 점은 HoRNDIS가 El Capitan(Mac OS X 10.11)을 지원하지 않아 현재까지는 Yosemite가 설치된 맥에서 까지만 사용할 수 있다.
윈도우의 경우 윈도우 7용 RNDIS 드라이버 설치하기 를 참고해 RNDIS 드라이버를 설치해 주면 된다.
이제 ssh 클라이언트를 실행해 연결해 보자. 아래 화면은 윈도우용 Tera Term이다. Hostname을 'raspberrypi.local'로 해서 ssh로 연결하면 된다.
첫번째 연결하는 것이면 아래와 같이 security warning이 뜬다. 'Add this machine and its key to the known hosts list'를 체크하고 Continue를 누르면 다음번 부터는 나타나지 않을 것이다.
Username과 Passphrase를 묻는 화면이 나타난다.
디폴트 사용자 이름은 'pi', 암호는 'raspberry' 이다.

로그인이 성공해 프롬프트가 나왔다.
* 'raspberrypi.local' 이름으로 연결이 가능한 건 애플의 Bonjour 기술 덕분이다. 만일 윈도우에서 저 이름으로 접속을 하는데 연결이 안된다면 아이튠즈를 먼저 설치한 다음 다시 시도하면 문제 없이 될 것이다. (아이튠즈를 설치하면 윈도우용 bonjour이 같이 설치되기 때문이다.)
위의 방법으로 하면 편하긴 한데 한가지 문제가 있다.
ifconfig 명령으로 확인해 보면 라즈베리 파이 제로의 네트웍 인터페이스는 usb0인데 그 ip address는 dhcp로 받아오기 때문에 매번 바뀌게 된다.
IP 주소를 고정하려면 /etc/network/interfaces 파일을 수정해 줘야 한다.
에디터로 파일을 열어보면 다음과 같은 내용이 들어 있다.
파일 맨 아래쪽에 빨간색 부분을 추가한 다음 저장해 주면 된다. 아래에서는 라즈베리 파이의 IP주소를 '192.168.10.2'로 지정했다.
이제 라즈베리 파이를 재시동하고 확인해 보면 ip 주소가 바뀐걸 확인할 수 있다.
라즈베리 파이의 설정에 맞춰 pc의 설정도 변경 해 줘야 한다.
윈도우의 경우 먼저 제어판에서 '네트워크 및 공유 센터'를 선택한 다음 '어댑터 설정 변경'을 누른다.
어댑터 중에 RNDIS/Ethernet Gadget을 찾아 선택한 다음 마우스 오른쪽 버튼을 눌러 메뉴에서 '속성'을 선택한다.
Internet Protocol Version 4 (TCP/IPv4)를 선택하고 '속성' 버튼을 누른다.
IP 설정을 넣어주면 된다. 라즈베리 파이가 192.168.10.2를 사용하고 있으므로 윈도우도 192.168.10.x 를 사용해야만 한다. (x는 1~254 사이에서 2를 제외한 임의의 숫자중에 하나)
이제 설정이 끝났으므로 pc에서 라즈베리 파이로 ping을 날려 응답이 오는걸 확인해 보면 된다.
Labels:
라즈베리 파이,
라즈베리 파이 제로,
bonjour,
Raspberry Pi,
raspberry pi zero,
RNDIS,
rndis/ethernet gadget
2016년 8월 22일 월요일
라즈베리 파이 3 시리얼/블루투스 문제 (Raspberry Pi 3 UART/Bluetooth problem)
라즈베리 파이에는 디버그 콘솔용 또는 시리얼 통신을 위한 핀이 40핀 커넥터에 나와 있다.
위의 그림에서 처럼 8/10번 핀이 각각 TxD, RxD가 된다.
위의 사진과 같은 USB-to-Serial(TTL level) converter를 사용해 PC와 연결해서 시리얼 통신을 할 수 있다.
이렇게 연결해 주면 된다.
컴퓨터에서 시리얼 에뮬레이터를 실행하고 통신속도를 115200bps로 맞춰 준 다음 라즈베리 파이에 전원을 공급하면 시리얼 에뮬레이터 화면에 이런 내용들이 출력되게 된다.
그런데 라즈베리 파이 2까지는 아무 문제가 없었던 것이 라즈베리 파이 3를 동일하게 연결하면 위와 같은 내용이 나오지 않고 이상하게 깨진 글자가 나오게 된다.
저렇게 깨진 글자가 나오게 되는 이유는 통신 속도가 틀려서이다.
원인을 이해하기 위해 라즈베리 파이의 내부 구조를 살펴보겠다.
라즈베리 파이 1B/B+는 BCM2835, 2는 BCM2836 SoC를 사용하고 있는데 여기에는 각각 2개의 UART가 들어 있는데 UART0는 Full UART, UART1은 mini UART이다. 여기서 UART0는 /dev/ttyAMA0로 사용되고 UART1은 /dev/ttyS0로 사용된다.
위의 그림처럼 mini UART는 몇가지 제약이 있지만, 40핀 커넥터의 8/10번 핀은 full UART의 Tx/Rx로 연결되어 있으므로 아무 문제가 없다.
하지만 라즈베리 파이 3로 오면서 블루투스가 추가되는 바람에 문제가 복잡해졌다.
먼저 하드웨어 구조를 살펴 보자.
위의 사진이 라즈베리 파이 1/2의 구조이다. 시리얼 포트 핀이 UART0에 연결되어 있다.
라즈베리 파이 3로 오면서 무선랜/블루투스 기능 추가를 위해 Broadcom의 BCM43438 칩을 사용하고 있는데 무선랜 기능은 SDIO로 연결하지만 블루투스 기능은 UART0으로 연결을 하고 시리얼 포트 핀은 UART1에 연결되도록 구조가 변경되었다. 그러므로 그 동안 아무 문제 없이 시리얼 포트를 사용하던 프로그램이 라즈베리 파이 3로 오면서 블루투스를 사용하게 되면 제대로 동작을 할 수 없게 되어 버린 것이다.
또한 코드의 /dev/ttyAMA0를 /dev/ttyS0로 변경한다고 해도 문제가 남는다.
UART1의 통신 속도는 다음 공식에 의해 결정된다.
라즈베리 파이 3의 경우 부팅시의 코어 동작 주파수는 400MHz이고 디폴트 baud rate는 115200 bps이다. 즉 위의 공식은...
이 된다. 여기서 baudRateReg는 부팅시에 코어 동작 주파수에 의해 계산된다.
그런데 전력소비를 줄이기 위해 frequency scaling 기능을 사용하고 있기 때문에 시스템 로드가 작은 경우 코어 동작 주파수를 250MHz로 줄일 수 있다. baudRateReg 값은 고정되어 있는데 시스템 클럭 주파수가 변동되기 때문에 결과적으로 baudrate도 따라서 바뀌게 되어 버린다.
즉 코어가 400MHz로 동작할 때는 115200bps로 동작하던 시리얼 포트가 코어 동작 속도가 250MHz가 되면 시리얼 포트 통신속도가 72004 bps로 바뀌어 버리기 때문에 위쪽의 그림처럼 깨진 글자가 나오게 되는 것이다.
그러므로 headless system (모니터 없이 사용하는 경우)에서 시리얼 디버그 콘솔을 사용하거나, UART를 사용하는 RPi shield(GPS/GSM, RS232 등등)를 사용하거나, 시리얼 포트로 다른 프로세서와 통신을 하는 경우 등에는 치명적인 문제가 될 수 있다.
이 문제의 해결책으로는 먼저 부팅시 코어 동작 속도를 고정해 버리는 것이다.
예전 펌웨어를 사용하는 경우 /etc/config.txt 파일에 "core_freq=250" 또는 "force_turbo=1"을 추가해 주는 것이다. 이렇게 하면 코어 동작 속도를 각각 최소 또는 최대(400MHz)로 고정시켜 버리게 된다. 최소로 고정시키면 RPi3의 성능에 영향을 미치게 되고, 최대로 고정시키면 방열대책을 신경 써 줘야 한다. 하지만 최대로 고정시켰다 하더라도 전압이 떨어지거나 코어 온도가 매우 높게 올라가면 250MHz로 쓰로틀링 될 수 있다는걸 주의해야 한다.
새 펌웨어의 경우에는 /boot/config.txt 파일에 "enable_uart=1"을 추가해주면 된다. 앞으로 모든 RPi distribution은 이 옵션을 다 지원하게 될 것이다. 이는 "core_freq=250"과 유사하지만 좀 더 명확한 의도를 표현한다. 이 옵션을 지원하는 distros인 경우 "enable_uart=1"이 활성화 되지 않으면 디버그 콘솔을 비활성화 시켜 놓는다. "enable_uart=1"에 대해 Raspberry Pi Foundation의 엔지니어 Phil Elwell이 상세히 설명 해 놓았다. https://github.com/raspberrypi/firmware/issues/553#issuecomment-199486644
두번째 방법으로는 디바이스 트리 오버레이로 40핀 헤더의 핀 매핑을 바꿔 Full UART를 사용하게 해 주는 것이다.
원래는 위의 사진에서 왼쪽같이 되어 있는데 오른쪽같이 되도록 변경하는 것이다. 이를 위해서는 '/boot/config.txt'에 'dtoverlay=pi3-disable-bt'를 추가해 주거나, '/boot/config.txt'에 'dtoverlay=pi3-miniuart-bt'를 추가해 주면 된다.
'dtoverlay=pi3-disable-bt'를 추가해주면 UART1을 사용하지 않는걸로 해서 블루투스를 비활성화 시킨다. 이 경우 라즈비안에서는 "sudo systemctl disable hciuart" 명령을 추가로 실행해 줘야 한다.
'dtoverlay=pi3-miniuart-bt'를 추가해 주는 경우는 블루투스가 mini-UART를 사용한다. 이를 위해 '/boot/config.txt'에 'core_freq=250' 또는 'force_turbo=1'이 들어가 있어야 한다. 또한 라즈비안의 경우 '/lib/systemd/system/hciuart.service'의 ttyAMA0를 ttyS0로 수정해 줘야 한다. 그리고 이 때는 블루투스의 성능에 제한이 생길 수 있기 때문에 저속 BLE만 실질적으로 사용하는데 문제가 없다.
위의 어떤 옵션을 사용하건 'enable_uart=0'을 넣어주지 않는 한 시리얼 콘솔은 활성화 되어 있다.
즉 위의 설명을 요약하자면 다음과 같다.
* 시리얼 콘솔과 일반적인 블루투스를 사용하고 싶은 경우 (/boot/config.txt 파일을 수정)
- 코어 주파수를 고정
- "core_freq=250" 또는 "force_turbo=1"을 추가 (old 펌웨어인 경우)
- "enable_uart=1" (만일 동작 주파수를 고속으로 고정하고 싶으면 "force_turbo=1"을 추가)
* 고속 시리얼을 사용하고 싶은 경우
- UART0와 UART1 핀을 교체
- 블루투스를 사용할 수 없는 경우
+ "dtoverlay=pi3-disable-bt"
+ (라즈비안) "sudo systemctl disable hciuart"를 실행
- 저속 블루투스 클래식 또는 BLE
+ "dtoverlay=pi3-miniuart-bt" 와 "core_freq=250" 또는 "force_turbo=1"중의 하나를 추가
+ (라즈비안) "/lib/systemd/system/hciuart.service" 파일의 "ttyAMA0"를 "ttyS0"로 수정
- 디버그 콘솔을 비활성화 시키려면 "disable_uart=0" 추가
* Mini UART를 사용하려면 항상 코어 주파수를 고정할 필요가 있음
위의 그림에서 처럼 8/10번 핀이 각각 TxD, RxD가 된다.
위의 사진과 같은 USB-to-Serial(TTL level) converter를 사용해 PC와 연결해서 시리얼 통신을 할 수 있다.
이렇게 연결해 주면 된다.
컴퓨터에서 시리얼 에뮬레이터를 실행하고 통신속도를 115200bps로 맞춰 준 다음 라즈베리 파이에 전원을 공급하면 시리얼 에뮬레이터 화면에 이런 내용들이 출력되게 된다.
그런데 라즈베리 파이 2까지는 아무 문제가 없었던 것이 라즈베리 파이 3를 동일하게 연결하면 위와 같은 내용이 나오지 않고 이상하게 깨진 글자가 나오게 된다.
저렇게 깨진 글자가 나오게 되는 이유는 통신 속도가 틀려서이다.
원인을 이해하기 위해 라즈베리 파이의 내부 구조를 살펴보겠다.
라즈베리 파이 1B/B+는 BCM2835, 2는 BCM2836 SoC를 사용하고 있는데 여기에는 각각 2개의 UART가 들어 있는데 UART0는 Full UART, UART1은 mini UART이다. 여기서 UART0는 /dev/ttyAMA0로 사용되고 UART1은 /dev/ttyS0로 사용된다.
위의 그림처럼 mini UART는 몇가지 제약이 있지만, 40핀 커넥터의 8/10번 핀은 full UART의 Tx/Rx로 연결되어 있으므로 아무 문제가 없다.
하지만 라즈베리 파이 3로 오면서 블루투스가 추가되는 바람에 문제가 복잡해졌다.
먼저 하드웨어 구조를 살펴 보자.
위의 사진이 라즈베리 파이 1/2의 구조이다. 시리얼 포트 핀이 UART0에 연결되어 있다.
라즈베리 파이 3로 오면서 무선랜/블루투스 기능 추가를 위해 Broadcom의 BCM43438 칩을 사용하고 있는데 무선랜 기능은 SDIO로 연결하지만 블루투스 기능은 UART0으로 연결을 하고 시리얼 포트 핀은 UART1에 연결되도록 구조가 변경되었다. 그러므로 그 동안 아무 문제 없이 시리얼 포트를 사용하던 프로그램이 라즈베리 파이 3로 오면서 블루투스를 사용하게 되면 제대로 동작을 할 수 없게 되어 버린 것이다.
또한 코드의 /dev/ttyAMA0를 /dev/ttyS0로 변경한다고 해도 문제가 남는다.
UART1의 통신 속도는 다음 공식에 의해 결정된다.
라즈베리 파이 3의 경우 부팅시의 코어 동작 주파수는 400MHz이고 디폴트 baud rate는 115200 bps이다. 즉 위의 공식은...
이 된다. 여기서 baudRateReg는 부팅시에 코어 동작 주파수에 의해 계산된다.
그런데 전력소비를 줄이기 위해 frequency scaling 기능을 사용하고 있기 때문에 시스템 로드가 작은 경우 코어 동작 주파수를 250MHz로 줄일 수 있다. baudRateReg 값은 고정되어 있는데 시스템 클럭 주파수가 변동되기 때문에 결과적으로 baudrate도 따라서 바뀌게 되어 버린다.
즉 코어가 400MHz로 동작할 때는 115200bps로 동작하던 시리얼 포트가 코어 동작 속도가 250MHz가 되면 시리얼 포트 통신속도가 72004 bps로 바뀌어 버리기 때문에 위쪽의 그림처럼 깨진 글자가 나오게 되는 것이다.
그러므로 headless system (모니터 없이 사용하는 경우)에서 시리얼 디버그 콘솔을 사용하거나, UART를 사용하는 RPi shield(GPS/GSM, RS232 등등)를 사용하거나, 시리얼 포트로 다른 프로세서와 통신을 하는 경우 등에는 치명적인 문제가 될 수 있다.
이 문제의 해결책으로는 먼저 부팅시 코어 동작 속도를 고정해 버리는 것이다.
예전 펌웨어를 사용하는 경우 /etc/config.txt 파일에 "core_freq=250" 또는 "force_turbo=1"을 추가해 주는 것이다. 이렇게 하면 코어 동작 속도를 각각 최소 또는 최대(400MHz)로 고정시켜 버리게 된다. 최소로 고정시키면 RPi3의 성능에 영향을 미치게 되고, 최대로 고정시키면 방열대책을 신경 써 줘야 한다. 하지만 최대로 고정시켰다 하더라도 전압이 떨어지거나 코어 온도가 매우 높게 올라가면 250MHz로 쓰로틀링 될 수 있다는걸 주의해야 한다.
새 펌웨어의 경우에는 /boot/config.txt 파일에 "enable_uart=1"을 추가해주면 된다. 앞으로 모든 RPi distribution은 이 옵션을 다 지원하게 될 것이다. 이는 "core_freq=250"과 유사하지만 좀 더 명확한 의도를 표현한다. 이 옵션을 지원하는 distros인 경우 "enable_uart=1"이 활성화 되지 않으면 디버그 콘솔을 비활성화 시켜 놓는다. "enable_uart=1"에 대해 Raspberry Pi Foundation의 엔지니어 Phil Elwell이 상세히 설명 해 놓았다. https://github.com/raspberrypi/firmware/issues/553#issuecomment-199486644
두번째 방법으로는 디바이스 트리 오버레이로 40핀 헤더의 핀 매핑을 바꿔 Full UART를 사용하게 해 주는 것이다.
원래는 위의 사진에서 왼쪽같이 되어 있는데 오른쪽같이 되도록 변경하는 것이다. 이를 위해서는 '/boot/config.txt'에 'dtoverlay=pi3-disable-bt'를 추가해 주거나, '/boot/config.txt'에 'dtoverlay=pi3-miniuart-bt'를 추가해 주면 된다.
'dtoverlay=pi3-disable-bt'를 추가해주면 UART1을 사용하지 않는걸로 해서 블루투스를 비활성화 시킨다. 이 경우 라즈비안에서는 "sudo systemctl disable hciuart" 명령을 추가로 실행해 줘야 한다.
'dtoverlay=pi3-miniuart-bt'를 추가해 주는 경우는 블루투스가 mini-UART를 사용한다. 이를 위해 '/boot/config.txt'에 'core_freq=250' 또는 'force_turbo=1'이 들어가 있어야 한다. 또한 라즈비안의 경우 '/lib/systemd/system/hciuart.service'의 ttyAMA0를 ttyS0로 수정해 줘야 한다. 그리고 이 때는 블루투스의 성능에 제한이 생길 수 있기 때문에 저속 BLE만 실질적으로 사용하는데 문제가 없다.
위의 어떤 옵션을 사용하건 'enable_uart=0'을 넣어주지 않는 한 시리얼 콘솔은 활성화 되어 있다.
즉 위의 설명을 요약하자면 다음과 같다.
* 시리얼 콘솔과 일반적인 블루투스를 사용하고 싶은 경우 (/boot/config.txt 파일을 수정)
- 코어 주파수를 고정
- "core_freq=250" 또는 "force_turbo=1"을 추가 (old 펌웨어인 경우)
- "enable_uart=1" (만일 동작 주파수를 고속으로 고정하고 싶으면 "force_turbo=1"을 추가)
* 고속 시리얼을 사용하고 싶은 경우
- UART0와 UART1 핀을 교체
- 블루투스를 사용할 수 없는 경우
+ "dtoverlay=pi3-disable-bt"
+ (라즈비안) "sudo systemctl disable hciuart"를 실행
- 저속 블루투스 클래식 또는 BLE
+ "dtoverlay=pi3-miniuart-bt" 와 "core_freq=250" 또는 "force_turbo=1"중의 하나를 추가
+ (라즈비안) "/lib/systemd/system/hciuart.service" 파일의 "ttyAMA0"를 "ttyS0"로 수정
- 디버그 콘솔을 비활성화 시키려면 "disable_uart=0" 추가
* Mini UART를 사용하려면 항상 코어 주파수를 고정할 필요가 있음
피드 구독하기:
글 (Atom)




















































