Remote Temperature/Humidity Checker using TTS

Posted by 카이젠8501 카이젠8501
2015.10.08 14:44 IT 개발/DIY 프로젝트

Remote Temperature/Humidity Checker & LED controller

앞에서 설명한 mbed RPC Library와 Python Interface를 활용한 예제를 만들어 보려고 하다 보니 제목이 거창해 진거 같다.

간단하게 말하자면, 본 예제는 아래와 같은 기능을 수행한다.

  • mbed platform의 3색 LED 제어 ( On/Off )
  • mbed platform의 현재 LED 상태 확인 ( Smartphone의 TTS 기능을 이용하여 Green,Blue,Red 인지 상태 확인 기능 )
  • mbed platform으로 부터 온도, 습도 정보를 확인 ( Smartphone의 TTS 기능을 이용하여 온/습도 정보를 확인 가능)

본 예제에서는 mbed RPC를 이용하여 스마트폰에서 mbed platform을 제어하고 있으며, 스마트폰의 Application은 python 기반의 kivy 라이브러리를 사용하였다.

20150727_132817

mbed HTTP RPC Server

본 장에서는 위에서 설명한 동작을 수행하기 위한 mbed code를 구현하는 방법에 대해 설명한다. 아래 코드는 mbed rpc와 HTTP Server 등을 구동하기 위한 코드이며, 사용된 라이브러리는 아래와 같다. mbed platform에 아래 코드를 컴파일해서 Upload 하면, 외부에서 HTTP Data를 이용하여 mbed platform을 제어 할 수 있다.

  • DHT : 온/습도 센서를 제어 하기 위한 라이브러리
  • HTTPServer : HTTP Server를 위한 라이브러리
  • mbed-rpc : mbed RPC를 위한 라이브러리
  • WIZnetInterface : 별도의 Software stack 없이 Ethernet을 사용하기 위한 라이브러리

Example Code는 아래 경로에서 다운로드 할 수 있다.
RPC_HTTP_WIZnetInterface

#include "mbed.h"
#include "EthernetInterface.h"
#include "HTTPServer.h"
#include "mbed_rpc.h"
#include "DHT.h"

RpcDigitalOut led1(D9,"led1");
RpcDigitalOut led2(D10,"led2");
RpcDigitalOut led3(D11,"led3");

//RPCVarialbe<float> RPCTemperature(&GetTemperature, "Temperature");
//RPCVarialbe<float> RPCHumidity(&GetHumidity, "Humidity");
void Get_Temp_and_Humidity(Arguments * input, Reply * output);

RPCFunction Temp_and_Humidity_Finder(&Get_Temp_and_Humidity, "Temp_and_Humidity_Finder");

EthernetInterface eth;  
HTTPServer svr;

DHT sensor(D4, DHT11);

void Get_Temp_and_Humidity(Arguments * input, Reply *output){
    int error = 0;
    float h = 0.0f, c = 0.0f;
    char arg[100];

    error = sensor.readData();
    if (0 == error) {
        c   = sensor.ReadTemperature(CELCIUS);
        h   = sensor.ReadHumidity();
        sprintf(arg,"Temperature in Celcius: %4.2f, Humidity is %4.2f",c, h);

        output->putData(arg);
    }
}

int main() {
  //Turn the LEDs off
  uint8_t mac_addr[6] = {0x00, 0x08, 0xDC, 0x32, 0x23, 0x42}; 
  led1.write(0);
  led2.write(0);
  led3.write(0);

  RPC::add_rpc_class<RpcDigitalOut>();

  printf("Setting up...\n");
  eth.init(mac_addr);
  int ethErr = eth.connect();
  if(ethErr < 0)
  {
    printf("Error %d in setup.\n", ethErr);
    return -1;
  }
  svr.addHandler<HTTPRpcRequestHandler>("/rpc");

  //attach server to port 80
  printf("Listening...\n");
  svr.start(80, &eth);

  Timer tm;
  tm.start();
  //Listen indefinitely
  while(true)
  {
    svr.poll();
    if(tm.read()>.5)
    {
      tm.start();
    }
  }
}
  • 7~8 : 3색 LED를 위한 GPIO를 RPC에 등록
  • 15 : 온/습도를 얻어 오기 위한 Custom 함수를 RPC에 등록
  • 22~35 : Custom 함수의 구현 부
    • mbed RPC client로 부터 요청이 들어오면 온/습도를 추출한 후, Temperature in Celcius : … “ 데이터를 RPC client에게 송신 한다.
  • 54 : HTTPRPC Request 등록

Smartphone Simple Application

본 장에서는 Smartphone에서 mbed device를 제어하기 위한 간단한 Application을 만드는 방법을 설명한다. 본 예제는 python으로 구현 하였으며, UI를 위해 kivy 라이브러리를 사용하였다. 본 예제를 구현 및 구동하기 위해서는 아래와 같은 절차를 수행 해야 한다.

Download Qpython

Android 폰에서 python을 구동하기 위해서는 Qpython 이라는 프로그램이 필요 하며, 다운로드 경로는 아래와 같다.
QPython Download

20150727_140513

앞에서 설명한 것과 같이 python으로 UI를 만들기 위해서는 kivy 라이브러리가 필요하며, 스마트폰의 TTS(Text to Speach) 기능을 사용하기 위해서는 AndroidHelper 라이브러리가 있어야 한다. 각각의 라이브러리는 아래 그림과 같은 절차를 수행하면 설치 할 수 있다.

20150727_140920 20150727_141000 20150727_141026

20150727_141051 20150727_141110 20150727_141151 20150727_141247

Edit Source Code

스마트폰 앱으로 소스코드를 작성하는 일은 매우 불편하다. 다행히도 QPython은 미리 작성한 코드를 FTP로 핸드폰에 저장하는 기능을 지원하고 있다.
나는 이 기능을 이용해서 PC에서 미리 구현해 놓은 프로그램을 스마트폰에 복사 한 후 실행하는 절차로 코드를 구현 및 디버깅 했다. ( 이 또한 매우 불편 하였다. )

Setting for FTP

아래 그림과 같은 절차를 수행하면 Qpython의 FTP 기능을 사용 할 수 있다.

20150727_143112 20150727_143133 20150727_143156

Upload Source Code to Smartphone

아래 그림들은 PC에서 QPython의 FTP Server에 접속해서 미리 작성한 코드를 업로드 하는 방법을 나타낸다.

20150727_143344

FTP 접속에 성공하면 아래와 같이 Qpython의 폴더 트리를 볼 수 있다. 폴더 트리에 나오는 폴더를 하나 선택하여 미리 작성한 코드를 업로드 하면 된다.

20150727_143413

나는 project3/RPC_TTS 폴더를 만들고 kivy.py와 mbedRPC.py를 복사 해서 사용한다.
mbedRPC.py는 앞에서 설명한 mbed python RPC library이며, kivy.py는 Application의 UI 및 동작을 구현한 파일이다.

20150727_143443

Source code of kivy.py

#-*-coding:utf8;-*-
#qpy:2
#qpy:kivy

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from mbedRPC import *
import androidhelper

class CommandScreen(GridLayout):
    def __init__(self, **kwargs):
        super(CommandScreen, self).__init__(**kwargs)
        self.cols = 2
        self.row = 2
        self.row_force_default=True
        self.row_default_height=200

        self.add_widget(Label(text='Device IP'))
        self.m_text_device_ip = TextInput(multiline=False,text="192.168.18.121")
        self.add_widget(self.m_text_device_ip)

        self.m_button_init = Button(text="Init")
        self.m_button_init.bind(on_press=self.mbedInit)
        self.add_widget(self.m_button_init)

        self.m_button_GetTemp = Button(text="Get Temp")
        self.m_button_GetTemp.bind(on_press=self.GetTemp)
        self.add_widget(self.m_button_GetTemp)

        self.btn_onLed1 = Button(text="Turn On",background_color=(1,0,0,1))
        self.btn_onLed1.bind(on_press=self.TurnOnLed1)
        self.add_widget(self.btn_onLed1)

        self.btn_offLed1 = Button(text="Turn Off",background_color=(1,0,0,1))
        self.btn_offLed1.bind(on_press=self.TurnOffLed1)
        self.add_widget(self.btn_offLed1)

        self.btn_onLed2 = Button(text="Turn On",background_color=(0,1,0,1))
        self.btn_onLed2.bind(on_press=self.TurnOnLed2)
        self.add_widget(self.btn_onLed2)

        self.btn_offLed2 = Button(text="Turn Off",background_color=(0,1,0,1))
        self.btn_offLed2.bind(on_press=self.TurnOffLed2)
        self.add_widget(self.btn_offLed2)

        self.btn_onLed3 = Button(text="Turn On",background_color=(0,0,1,1))
        self.btn_onLed3.bind(on_press=self.TurnOnLed3)
        self.add_widget(self.btn_onLed3)

        self.btn_offLed3 = Button(text="Turn Off",background_color=(0,0,1,1))
        self.btn_offLed3.bind(on_press=self.TurnOffLed3)
        self.add_widget(self.btn_offLed3)

        self.btn_getLedStatus = Button(text="LED Status")
        self.btn_getLedStatus.bind(on_press=self.GetLedStatus)
        self.add_widget(self.btn_getLedStatus)

        self.btn_getA0Status = Button(text="Get A0 Status")
        self.btn_getA0Status.bind(on_press=self.GetA0Status)
        self.add_widget(self.btn_getA0Status)


    def mbedInit(self,event):
        self.droid = androidhelper.Android()

        self.mbed = HTTPRPC(self.m_text_device_ip.text)
        self.led1 = DigitalOut(self.mbed,"led1")
        self.led2 = DigitalOut(self.mbed,"led2")
        self.led3 = DigitalOut(self.mbed,"led3")
        self.temp_humidity = RPCFunction(self.mbed,"Temp_and_Humidity_Finder")

        box = BoxLayout(orientation='vertical')
        box.add_widget(Label(text='Init Complete'))
        button_close = Button(text='Close me!')
        box.add_widget(button_close)

        popup = Popup(title='message', content=box,
                      size_hint=(None,None),size=(400,400), auto_dismiss=False)

        button_close.bind(on_press=popup.dismiss)
        popup.open()


    def GetTemp(self,event):
        resp = self.temp_humidity.run("")
        self.droid.ttsSpeak(resp)

    def TurnOnLed1(self,event):
        self.led1.write(1)

    def TurnOffLed1(self,event):
        self.led1.write(0)

    def TurnOnLed2(self,event):
        self.led2.write(1)

    def TurnOffLed2(self,event):
        self.led2.write(0)

    def TurnOnLed3(self,event):
        self.led3.write(1)

    def TurnOffLed3(self,event):
        self.led3.write(0)

    def GetLedStatus(self,event):
        if( self.led1.read()[0] == '1' ): self.droid.ttsSpeak("Turn on red.")
        if( self.led2.read()[0] == '1' ): self.droid.ttsSpeak("Turn on green.")
        if( self.led3.read()[0] == '1' ): self.droid.ttsSpeak("Turn on blue.")

    def GetA0Status(self,event):
        print ''

class MyApp(App):
    def build(self):
        return CommandScreen()        

if __name__ == '__main__':
    MyApp().run()
  • 15 ~ 65 : App Button 및 UI 초기화
  • 68 ~ 86 : mbed RPC library 초기화
  • 89 : mbed platform으로 부터 온/습도 데이터를 수신 후 TTS로 출력
  • 93 ~ 109 : LED1/2/3 On/Off
  • 111 ~ 114 : 현재 mbed platform의 LED 색깔을 확인 후 TTS로 출력

Result after run

20150727_130822

Demo Video

저작자 표시
신고
이 댓글을 비밀 댓글로

mbed rpc interfacing with Python

Posted by 카이젠8501 카이젠8501
2015.10.08 14:43 IT 개발/mbed

mbed rpc interfacing with Python

Python RPC Library

mbed에서는 mbed RPC client가 python을 이용하여 mbed RPC server의 데이터를 read/write하거나 custom 함수를 call 하기 위해 아래와 같은 python module을 제공한다.

아래 코드는 HTTP를 사용한 python RPC 예제이며, 192.168.0.4인 RPC Server에 접속하여 GPIO와 ADC 값을 read/write 하는 예제 이다.

#!python
python
>>> from mbedrpc import *
>>> mbed = HTTPRPC("192.168.0.4")
>>> x = DigitalOut(mbed,"LED1")#These objects should already exist on mbed
>>> z = DigitalOut(mbed,"LED2") 
>>> ain = AnalogIn(mbed, "LED3")
>>> x.write(1)
>>> z.write(0.5)
>>> ain.read()
0.786757474
>>>

Python RPC library에 대한 자세한 설명은 아래 주소를 참고 하기 바란다.

https://developer.mbed.org/cookbook/Interfacing-with-Python

앞에서 설명한 mbed 코드와 같이 사용하기 위해서는 아래 코드를 참고 하기 바란다.

#!python
python
>>> from mbedrpc import *
>>> mbed = HTTPRPC("192.168.0.4")
>>> x = DigitalOut(mbed,"led1")#These objects should already exist on mbed
>>> x.write(1)
>>> x.write(0)
저작자 표시
신고

'IT 개발 > mbed' 카테고리의 다른 글

W7500 mbed porting Guide ( 2/6 )  (0) 2015.10.08
W7500 mbed porting Guide ( 1/6 )  (0) 2015.10.08
mbed rpc interfacing with Python  (0) 2015.10.08
mbed RPC Library porting guide for WIZwiki-W7500  (0) 2015.10.08
mbed RPC  (0) 2015.10.08
[FRDM-KL25Z]WIZ550 ioShield-A Porting Guide  (0) 2014.08.20
이 댓글을 비밀 댓글로

mbed RPC Library porting guide for WIZwiki-W7500

Posted by 카이젠8501 카이젠8501
2015.10.08 14:42 IT 개발/mbed

mbed RPC Library porting guide for WIZwiki-W7500

Used library in this project

Modified Point

mbed rpc를 사용하기 위한 Interface는 RPC over Serial과 RPC over HTTP로 나눌 수 있다. 본 예제에서는 HTTP를 이용하여 RPC를 사용하며, mbed platform 에는 HTTPServer가 구동되고 있다. mbed rpc를 위한 Interface에 대한 자세한 정보는 Link를 참고 하기 바란다.

WIZnetInterface

HTTPServer Library에서 아래와 같은 코드를 사용하는데, WIZnetInterface에는 init()함수가 없음 —> WIZnetLibrary에 init() 함수 추가

HTTPServer/HTTPServer.cpp ( Line 50~56 )

INFO("Initializing network\n");
if (m_pEthernet->init() != 0) {
    ERR("Failed to initialize the ethernet interface !");
    delete m_pEthernet;
    m_pEthernet = NULL;
    return false;
}

mbed-rpc

공식 mbed-rpc Library에서 WIZwiki-W7500의 Pin들을 사용할 수 있도록 아래와 같이 코드를 추가 하였다.

mbed-rpc/parse_pins.cpp

#elif defined(TARGET_WIZwiki_W7500)
    if (str[0] == 'P' && str[2] == '_') {   // Px_n
        uint32_t port = str[1] - 'A';
        uint32_t pin  = str[3] - '0';       // Px_n
        uint32_t pin2 = str[4] - '0';       // Px_nn

        if (pin2 <= 9) {
            pin = pin * 10 + pin2;
        }
        return port_pin((PortName)port, pin);
#endif

WIZwiki-W7500의 Port와 Pin들은 아래와 같은 규칙으로 정의 되어 있으므로, parse_pins.cpp 에서는 PA_0와 같은 String Data를 받아 0x000과 같은 Int Data로 변환하는 동작을 수행한다.

// In PinNames.h

// W7500x PORT[5:4] + PIN[3:0])
PA_0  = 0x000,
PA_1  = 0x001,
PA_2  = 0x002,
...
PB_9  = 0x019,
PB_10 = 0x01A,
PB_11 = 0x01B,
...
PC_0  = 0x020,
PC_1  = 0x021,
PC_2  = 0x022,
...
// In PortNames.h

typedef enum {
    PortA = 0,
    PortB = 1,
    PortC = 2,
    PortD = 3,
} PortName;

mbed rpc library에 WIZwiki-W7500을 위한 코드를 추가한 후, mbed official repository에 pull request를 보내서 코드 업데이트를 요청 하였다.

Run Simple Example

#include "mbed.h"
#include "EthernetInterface.h"
#include "HTTPServer.h"
#include "mbed_rpc.h"

RpcDigitalOut led1(LED1,"led1");

EthernetInterface eth;  
HTTPServer svr;

int main() {
  //Turn the LEDs off
  uint8_t mac_addr[6] = {0x00, 0x08, 0xDC, 0x00, 0x00, 0x00}; 
  led1.write(1);

  RPC::add_rpc_class<RpcDigitalOut>();

  printf("Setting up...\n");
  eth.init(mac_addr);
  int ethErr = eth.connect();
  if(ethErr < 0)
  {
    printf("Error %d in setup.\n", ethErr);
    return -1;
  }

  svr.addHandler<HTTPRpcRequestHandler>("/rpc");

  //attach server to port 80
  svr.start(80, &eth);

  printf("Listening...\n");

  Timer tm;
  tm.start();
  //Listen indefinitely
  while(true)
  {
    svr.poll();
    if(tm.read()>.5)
    {
      tm.start();
    }
  }
}
  • 6 : 제어를 원하는 GPIO의 RPC Name 지정
  • 19~20 : ethernet interface 초기화 및 DHCP 수행
  • 27 : HTTPRpcRequestHandler 등록
  • 30 : HTTP Server Start ( TCP Server로 80포트 리슨)


저작자 표시
신고

'IT 개발 > mbed' 카테고리의 다른 글

W7500 mbed porting Guide ( 1/6 )  (0) 2015.10.08
mbed rpc interfacing with Python  (0) 2015.10.08
mbed RPC Library porting guide for WIZwiki-W7500  (0) 2015.10.08
mbed RPC  (0) 2015.10.08
[FRDM-KL25Z]WIZ550 ioShield-A Porting Guide  (0) 2014.08.20
Eclipse에서 mbed repository 연동 하기  (0) 2014.06.15
이 댓글을 비밀 댓글로

mbed RPC

Posted by 카이젠8501 카이젠8501
2015.10.08 14:40 IT 개발/mbed

mbed RPC

What is the RPC(Remote Procedure Call)?

별도의 원격제어를 위한 코딩 없이 다른 주소공간에서 함수나 프로시저를 실행할 수 있게하는 프로세스 간 통신 기술이다. 다시말해, 원격 프로시저 호출을 이용하면 프로그래머는 함수가 실행프로그램에 로컬 위치에 있든 원격 위치에 있든 동일한 코드를 이용할 수 있다.

참고자료:wikipedia

더 간단히 말하면 아래 그림과 같이 Client에서 Server의 특정 함수를 실행하거나 특정 변수 값을 알고 싶을 때 RPC를 사용할 수 있으며, RPC Library를 통해 Client와 Server 간의 통신을 할 수 있다.

rpc

그림 출처:http://blog.facilelogin.com/2011/02/rpc-with-java.html

mbed RPC Interface Library

mbed Repository에는 RPC Interface를 위한 라이브러리가 이미 존재하며, 크게 아래와 같은 기능을 지원한다.

  • RPCFunction : RPC를 통해 User가 정의한 함수를 호출 하기 위한 클래스
  • RPCVariable : RPC를 통해 mbed platform 안의 변수를 read/write 하기 위한 클래스
  • SerialRPCInterface : Serial을 통해 RPC를 Set up 하기 위한 클래스

RPCFunction

RPC Function object는 RPC를 통해 User가 정의한 Custom 함수를 호출 하기 위해 사용된다. 이 때 사용되는 함수는 void foo(char input, char output)과 같은 포맷으로 인자값을 받아야 한다. ( 최근 라이브러리에서는 void foo(Arguments input, Reply output) 과 같이 char 형 대신 Arguments , Reply형으로 인자값을 받아야, 컴파일 에러 없이 사용 할 수 있다. )

Arguments, Reply에 대한 Class 정의는 mbed-rpc/Arguments.h를 참고 하기 바란다.

//Create a function of the required format
void foo(Arguments * input, Reply * output);
//Attach it to an RPC object
RPCFunction rpc_foo(&foo, "foo");

void foo(Arguments * input, Reply * output){
   int x,y;
   char arg[100];
   sscanf(input, "%i, %i", &x, &y);

   //Do something here......
   sprintf(arg, "%i, %i", x, y);
   output->putData(arg);
}
/**
* Copyright (c)2010 ARM Ltd.
* Released under the MIT License: http://mbed.org/license/mit
*/
#include "mbed.h"
#include "SerialRPCInterface.h"
#include "SRF08.h"

using namespace mbed;

//Create the interface on the USB Serial Port
SerialRPCInterface RPC(USBTX, USBRX);
void ReadRange(Arguments * input, Reply * output);
RPCFunction RangeFinder(&ReadRange, "RangeFinder");
SRF08 srf08(p9, p10, 0xE0);      // Define SDA, SCL pin and I2C address 
DigitalOut myled(LED1);

int main() {

    while(1) {


        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}

//As neither I2C nor the SRF08 library is avalible directly over RPC we create a Custom Function which we make RPCable by attaching to an RPCFunction
void ReadRange(Arguments * input, Reply * output){
   char arg[100];

    //Format the output of the srf08 into the output string
   sprintf(arg, "%f",srf08.read());
   output->putData(arg);
}
  • 14 : Custom 함수 등록
  • 31 ~ 34 : Custom 함수 구현 부
 RPC command to read the range:/RangeFinder/run

RPCVariable

RPCVariable 클래스는 mbed platform 내에 구현되어 있는 변수를 read/write하기 위해 사용된다.

//First create the variables you wish to use
float f;
int i;
char c;
//Then attach them to an RPCVariable Object
RPCVariable<float> rpc_f(&f, "f");
RPCVariable<int> rpc_i(&i, "i");
RPCVariable<char> rpc_c(&c, "c");
The RPC commands to read and write the float f would be:

/f/write 0.123
/f/read
/**
* Copyright (c)2010 ARM Ltd.
* Released under the MIT License: http://mbed.org/license/mit
*/

#include "mbed.h"
#include "QEI.h"
#include "Motor.h"
#include "SerialRPCInterface.h"

//Create the interface on the USB Serial Port
SerialRPCInterface SerialInterface(USBTX, USBRX);

QEI Encoder(p29 ,p30, NC, 48);
Motor Wheel(p23, p21, p22);

//Create float variables
float MotorOutput = 50;
float Percentage = 0;

//Make these variables accessible over RPC by attaching them to an RPCVariable
RPCVariable<float> RPCMotorOut(&MotorOutput, "MotorOutput");
RPCVariable<float> RPCPercentage(&Percentage, "Percentage");

int main(){

    Encoder.reset();
    float NoPulses;

    while(1){ 
         NoPulses = Encoder.getPulses();
        Percentage = ((NoPulses / 48) * 100);
        //RPC will be used to set the value of MotorOutput.
        Wheel.speed((MotorOutput - 50) * 2 / 100);
        wait(0.005);
    }
}

Client 측에서 아래와 같은 포맷으로 데이터를 송신하면, MotorOutput변수와 Percetage변수를 read/write 할 수 있다.

rpc command to get the position: /percentage/read
rpc command to set the motor speed: /MotorOutput/write 0.8
저작자 표시
신고
이 댓글을 비밀 댓글로