티스토리 뷰

반응형

Home Automation 1탄 격인 집안 온도에 따라 제어하기(http://hero-space.tistory.com/26) 에 이어, 두번째로 진행하는 Home Automation project는 화분에 있는 토양의 수분정도를 측정하여 Cloud에 전송하고 Cloud는 이 데이터를 가공하여 다시 사용자에게 알려주는 시나리오를 기획했다. Activity로서, 

1) 센서를 이용한 토양 수분 측정

2) 측정한 데이터를 Cloud 서버에 Update

3) Cloud 서버에서 사용자에게 알림 전송

이렇게 나열 수 있었는데, 토양 수분의 경우 ADC를 이용하여 측정하였고 관련된 내용을 작성하였다.(http://hero-space.tistory.com/30). 다만, Cloud의 경우 계정을 생성하고 PC에서 Simulator를 만들어서 Cloud와 접속하고 Data를 Write/Read 하는 부분을 바로 전에 확인(http://hero-space.tistory.com/31) 했기 때문에, 이번엔 RaspberryPI에 연결된 토양수분 측정 센서의 실시간 측정값을 Cloud에 전송하고 Cloud에서는 이 Stream 데이터를 csv 파일로 저장하도록 할 것이다. #3에 해당하는 Notification Action은 Cloud 자체적인 기능으로 보고 우선 제외하였다. 대신 Cloud에서 다시 Device로 Signal을 주어서 물을 주는 시스템이 있다면 On/Off 제어가 가능할 것으로 보인다.

# Code 작성

: 기존 2개의 글에서 부터 토양수분 측정 센서제어가 가능하고, Cloud 계정을 생성한 상태라고 보고 Code를 우선 작성해보도록 하겠다.

- Main C# 파일에 Azure 연결을 위한 using 문을 두개 추가한다.

using Microsoft.Azure.Devices.Client;
using Newtonsoft.Json;

- Device가 Client 로 Azure 접속하도록 생성한 계정의 연결 문자열을 복사해서 넣고 'DeviceClient.CreateFromConnectionString'를 호출한다.

public sealed partial class MainPage : Page
{
    private const string cnString = "{연결문자열}";
    private DeviceClient deviceClient;

    public MainPage()
    {
        this.InitializeComponent();
        deviceClient = DeviceClient.CreateFromConnectionString(cnString, TransportType.Http1);

    };

- 비동기적으로 Data를 Azure에 보내도록 Task를 생성하여 Data를 읽고, 읽은 데이터는 Azure로 보낸다. 이때, Sensor 처리부는 별도의 C#파일을 만들 Class로 제공한다.

private async void SendDataToAzure() 
{
    var moistureSensor = await sensor.Create(); 

    while (true)
    {
        int moisture = moistureSensor.GetMoisture(); 

        var sendData = new
        {
            time = DateTime.Now.ToString(),
            Moisture = moisture,
            NodeId = "123",
            Location = "Home"
        };
        var messageString = JsonConvert.SerializeObject(sendData);
        var message = new Message(Encoding.ASCII.GetBytes(messageString));

        await deviceClient.SendEventAsync(message);
        Debug.WriteLine("{0} > Sending message: {1}", DateTime.Now, messageString);
        this.listView.Items.Insert(0, messageString);

        await Task.Delay(1000);
    }  
 } 

sendData의 겨우 Json 형태로 string을 만들어 주는데, time, moisture, NodeID, Location 이렇게 4가지를 parameter로 작성하여 Json으로 만들어준후 Azure에 Data를 보내게 된다. 이때 사용하는 API는 SendEventAsync 이다. 이렇게 만든 method인 SendDataToAzure() 를 main에서 initialize 할때 호출한다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows.Devices.Spi;
using System.Threading.Tasks;
using System.Threading;

namespace sensor_cloud
{
    class sensor
    {
        enum AdcDevice { NONE, MCP3002, MCP3208, MCP3008 };
        private AdcDevice ADC_DEVICE = AdcDevice.MCP3008;

        private const string SPI_CONTROLLER_NAME = "SPI0";  /* Friendly name for Raspberry Pi 2 SPI controller          */
        private const Int32 SPI_CHIP_SELECT_LINE = 0;       /* Line 0 maps to physical pin number 24 on the Rpi2        */

        private const byte MCP3002_CONFIG = 0x68; /* 01101000 channel configuration data for the MCP3002 */
        private const byte MCP3208_CONFIG = 0x06; /* 00000110 channel configuration data for the MCP3208 */
        private readonly byte[] MCP3008_CONFIG = { 0x01, 0x80 }; /* 00000001 10000000 channel configuration data for the MCP3008 */

        public static async Task Create()< sensor > {

            var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
            settings.ClockFrequency = 500000;   /* 0.5MHz clock rate                                        */
            settings.Mode = SpiMode.Mode0;      /* The ADC expects idle-low clock polarity so we use Mode0  */

            var controller = await SpiController.GetDefaultAsync();
            var SpiADC = controller.GetDevice(settings);

            return new sensor(SpiADC);
        }

        private SpiDevice SpiADC;

        private sensor(SpiDevice sensor)
        {
            this.SpiADC = sensor;
        }

        public int GetMoisture()
        {
            byte[] readBuffer = new byte[3]; /* Buffer to hold read data*/
            byte[] writeBuffer = new byte[3] { 0x00, 0x00, 0x00 };

            switch (ADC_DEVICE)
            {
                case AdcDevice.MCP3002:
                    writeBuffer[0] = MCP3002_CONFIG;
                    break;
                case AdcDevice.MCP3208:
                    writeBuffer[0] = MCP3208_CONFIG;
                    break;
                case AdcDevice.MCP3008:
                    writeBuffer[0] = MCP3008_CONFIG[0];
                    writeBuffer[1] = MCP3008_CONFIG[1];
                    break;
            }


            try
            {
                SpiADC.TransferFullDuplex(writeBuffer, readBuffer); /* Read data from the ADC  */
                int moisture = convertToInt(readBuffer);  /* Convert the returned bytes into an integer value */
                
                return moisture;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        public int convertToInt(byte[] data)
        {
            int result = 0;
            switch (ADC_DEVICE)
            {
                case AdcDevice.MCP3002:
                    result = data[0] & 0x03;
                    result <<= 8;
                    result += data[1];
                    break;
                case AdcDevice.MCP3208:
                    result = data[1] & 0x0F;
                    result <<= 8;
                    result += data[2];
                    break;
                case AdcDevice.MCP3008:
                    result = data[1] & 0x03;
                    result <<= 8;
                    result += data[2];
                    break;
            }
            return result;
        }
    }
}

토양 수분센서의 경우 ADC로 MCP3008 칩을 이용하고 SPI 통신을 통해 토양수분 센서의 Analog 값을 받아온다. 바로 위 main c# 코드에서 sensor 클랙스를 하나 생성한후 사용하도록 하였다. UI는 간단하게 list 형태로 Azure에 보내는 data를 출력하도록만 하였으니 코드설명은 생략한다.

# Device To Cloud

이제 빌드한 뒤 Raspberry에서 실행해보자, 물론 실행할때 빌드가 안되거나 하는 부분이 있다면 Azure를 위한 using 추가시 사용했던 라이브러리를 위해 Microsoft.Azure.Devices.Client 와 Newtonsof.json을 NuGet으로 설치해주어야한다. 그런데, 막상 실행해보면 센서데이터는 제대로 가지고오지만 Azure와 연결시 Exception이 발생한다. 코드적으로 이상할 게 없는데 한참을 고민하다가 Network 상태를 확인해봤다. 이전 글 중에서 Windows 10 IoT 로 연결하기(http://hero-space.tistory.com/20) 라는 글을 썼었는데, wifi dongle을 연결하면서 IP를 static으로 하기위하 netsh command를 통해 198.168.0.3으로 고정했다. 첨엔 wifi 문제라서 ethernet으로 바꿨음에도 안되서 자세히 들여다 보니 네트웍 상태가 '로컬 네트워크 엑세스 전용' 으로 되어있다. 

다시 netsh로 dhcp로 IP를 할당하게 했더니 상태가 '로컬 및 인터넷 액세스' 바뀌었다. static으로 설정한건 이런 의도는 아니었지만 로컬에서만 동작되게 되있어서 계속 Azure 접속시 exception이 발생한 것이 었다.

이 난관을 해결하고 Data를 송신하는데 성공했는데, data를 Azure에서 어떻게 확인해야할지 모르겠어서 검색해보니, Azure-iot-sdk(https://github.com/Azure/azure-iot-sdks) 에서 tool로 Device Explorer를 제공해 주고 있었다. 이 tool의 경우 내 계정에 Write/Read도 할 수 있고 Write 되는 내용을 모니터링 할 수 있다. 아래 페이지에 접속해서 우선 Cloud로 데이터를 잘 보내주고 있고 어떤 형태로 보내주는지 확인해보자.(https://github.com/Azure/azure-iot-sdks/blob/master/tools/DeviceExplorer/readme.md)

#Azure IoT 를 위한 스트림 분석

Data가 들어왔다면 이 데이터를 얼마나 오랫동안 저장할 것인지와 같은 내용은 기존의 단순한 Cloud에 불과하다. IoT를 위한 Cloud라면 이 데이터를 분석하고 Rule을 설정해써 등록된 Device와 Interaction을 하거나 Cloud 자체적으로 먼가를 더 할수 있어야한다. Azure역시 이런 기능을 갖추고 있는데, 지금 해볼 내용은 등록된 Device로 부터 Sensor 데이터를 받아서 이를 csv 파일로 생성하는 것이다. 이렇게 하기 위해선 기존에 만들었단 IoT Hub외에도 저장소(blob) 계정이 하나 필요하며, 스트림 분석을 위한 도구도 생성해야한다. DB라는 것이 기본적인 기능을 사용하는 것은 간단할지 모르지만 잘 사용하기 위해서는 DB의 구조도 잘 잡아야 하고, Rule로 잘 설정해야한다. Azure를 처음 사용해본 입장으로 Step-by-Step으로 구성해본 내용을 동작시켜본 내용이다. Azure를 위한 상세한 설정과정 내용은 별도의 글에서 정리하도록 하고 동영상을 보자. 동영상에서는 토양 수분측정을 통해서 값을 읽고 아래의 경우 약660 이라는 정수형 값을 같고 있다. Json 포맷으로 Azure에 전달한다. Azure에서는 데이터를 받고 csv로 저장하고 파일을 열어보면 Parameter와 value 이렇게 값이 들어가 있음을 알 수 있다.

중간 중간 몇가지 문제점이 있었지만 어렵지 않게 동작하는걸 볼수 있다. 이제 좀더 다양하게 접목시킬수 있을것 같은데 평가판 기간이 끝나면 어떻게 해야할지 고민이다. 하지만 오늘은 여기까지~!


반응형
댓글