2020년 6월 18일 목요일

Linux Device Driver for Embedded Processors 에피소드 3 - STM32MP157C Discovery Kit(소개5)

지난 시간에 이어 STM32MP157C Discovery Kit 분석, 다섯번 째 시간이다. 😊

이번 시간에는 HTU21D 온/습도 센서의 결과를 확인해 볼 수 있는 아주 간단한 Qt application을 하나 만들어 보도록 하겠다. 또한 온습도 센서의 결과를 외부 서버로 전송(client & server)하고, 이를 암호화하는 방법에 대해서도 소개해 보고자 한다.




이번 blog post의 목적은 Qt app 작성 방법을 소개(본 저자는 Qt 전문가가 아님^^)하는 것이 아니라, 온/습도 센서의 측정 결과를 UI 형태로 보여주거나, network을 통해 외부 서버로 전달하는 예를 소개하는데에 있다.


6. 온습도 센서 결과 확인용 Qt application 만들기
앞선 blog post에서 HTU21D 온/습도 센서를 i2c bus에 붙여 동작을 확인한 바 있다. 따라서 이번에서는 HTU21D 온/습도 센서의 결과를 읽어 들여 화면에 출력하는 간단한 application을 하나 만들어 보겠다.

<이번 장에서 다룰 내용>
1.  온/습도 센서 결과를 화면에 출력하는 (간단한) app 만들기
    => 참고 문험 [4] 14장의 예제를 참조하였음.
2. 온/습도 센서를 외부 서버로 전송하는 (역시 간단한) client & server  만들기
3. WireGuard로 암호 통신하기

a) 온/습도 센서 결과를 화면에 출력하는 app 만들기
<온/습도 센서 app의 개요>
1) (향후 확장을 위해) QMainWindow style로 만든다
2) 온도와 습도 값을 각각 아래 파일에서 주기적(3초 간격)으로 읽어 화면에 출력한다.
    => /sys/bus/iio/devices/iio:device0/in_temp_input
    => /sys/bus/iio/devices/iio:device0/in_humidityrelative_input
3) 주기적으로 온/습도 값을 읽어 들이기 위해 QTimer class를 사용한다.
   => timeout 시 signal 발생. 이후 해당 slot 함수 호출 
4) 온도와 습도 값은 LCD Number widget으로 표현하도록 한다.
5) "Get Sensor Data" Pushbutton widget을 하나 추가하여, 버튼 선택 시 곧 바로 온/습도 센서 값을 읽어 화면에 출력한다.
6) 하단에 Status bar를 추가하여 간단한 상태 정보(시작, 날짜 정보 등)를 출력하도록 한다.
-----------------------

이상의 내용을 Qt designer를 통해 구성한 전체 화면은 다음과 같다.

[그림 6.1] 화면 디자인
[Tip] LCD 화면이 smart phone 형태(가로 보다 세로가 긺)로 되어 있고, 화면 크기(4인치 TFT - 480 x 800)가 작은 점을 고려하여 font size를 작게하였다.

먼저 main.cpp 파일을 살펴 보면 다음과 같다.

[그림 6.2] main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);     //QApplication의 instance를 하나 생성한다.
    MainWindow w;         // QMainWindow를 상속한 MainWindow라는 이름의 class에 대한 instance를 하나 생성한다.
    w.show();        // 화면에 내용을 출력한다.

    return a.exec();      // loop을 돌면 대기한다(발생하는 event를 처리).
}


다음으로 소개하는 파일은 MainWindow class를 위한 헤더 파일 mainwindow.h이다.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTimer>

namespace Ui {                      // namespace Ui
class MainWindow;            // MainWindow는 Ui scope하에 있음을 뜻함.
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_getSample_clicked();                 // "Get Sensor Data" Pushbutton을 누를 경우 발생하는 signal을 처리하는 slot 함수
    void on_timerUpdate();                              // QTimer timeout event(signal) 발생 시 처리하는 slot 함수

private:
    float temperature, humidity;                    // 온도, 습도를 저장하는 private 변수
    QTimer *timer;                                                // QTimer class member 변수(pointer)
    void updateDisplay();                                 // 화면에 온도, 습도 값을 출력해 주는 함수로 on_timerUpdate( )  등의 함수에서 주기적으로 호출함.
    int readHTU21DSensor();                        // HTU21D 온/습도 센서의 출력 결과를 읽어 들이는 함수(위의 온도, 습도를 저장하는 private 변수에 값 저장)
    Ui::MainWindow *ui;                                     //Ui::MainWindow의 private member 변수(pointer)
};

#endif // MAINWINDOW_H

다음으로 소개하는 파일은 MainWindow class cpp 파일이다.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QtCore/QFile>
#include <QDebug>
#include <unistd.h>
using namespace std;

MainWindow::MainWindow(QWidget *parent) :    // MainWindow class 생성자(constructor)
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);        // ui object의 setupUI( ) 함수 호출 즉, MainWinodw를 구성하는 widget, layout 등을 배치하는 역할을 함.
    statusBar()->showMessage("Sensor Application Started");          // 하단 Status 바에 string 출력

    this->temperature = 0.0;          // temperature 변수 초기화
    this->humidity = 0.0;                 // humidity 변수 초기화 

    this->updateDisplay();

    this->timer = new QTimer(this);          // QTimer class instance 생성 
    connect(timer, SIGNAL(timeout()), this, SLOT(on_timerUpdate()));      // QTimer timeout signal에 on_timerUpdate() slot 함수 mapping
    this->timer->start(3000);                        // 3초 간격으로 timer timeout (signal 발생)
}

void MainWindow::on_getSample_clicked()     //"Get Sensor Data" Pushbutton을 누를 경우 발생하는 signal에 대한 처리
{
    QDateTime local(QDateTime::currentDateTime()); // display sample time
    statusBar()->showMessage(QString("Last update: ").append(local.toString()));    // 상태 바에 현재 시간 정보 출력

    this->readHTU21DSensor();       // HTU21D 센서 값(온도/습도) 읽어 member 변수에 저장
  
    this->updateDisplay();                 // 온/습도 정보를 화면에 출력 
}

void MainWindow::on_timerUpdate()     // QTimer timeout 시 호출되는 slot 함수 
{
    this->on_getSample_clicked();
    this->updateDisplay();           // 온/습도 정보를 화면에 출력
}

void MainWindow::updateDisplay()    // 화면을 갱신하는 함수 
{
    ui->lcdTemperature->display((double)temperature);       // 온도 LCD Number widget에 값 표시
    ui->temperatureUnits->setText("C");

    ui->lcdHumidity->display((double)humidity);                     // 습도 LCD Number widget에 값 표시
}

MainWindow::~MainWindow()        // MainWindow class 소멸자(destructor)
{
    delete ui;
}

int MainWindow::readHTU21DSensor()    // HTU21D 센서 정보를 읽어 들이는 함수 
{
    QFile fd_temp("/sys/bus/iio/devices/iio:device0/in_temp_input");    // 옆의 파일 open
    QFile fd_humidity("/sys/bus/iio/devices/iio:device0/in_humidityrelative_input");   // 옆의 파일 open

    if (!fd_temp.open(QIODevice::ReadOnly | QIODevice::Text))
        return -1;
    if (!fd_humidity.open(QIODevice::ReadOnly | QIODevice::Text))
        return -1;

    this->temperature = QString(fd_temp.readAll()).toDouble() / 1000;     // 파일 내용(string)을 읽어 들여 double 값으로 변경 후, 1000으로 나눔.
    this->humidity = QString(fd_humidity.readAll()).toDouble() / 1000;     // 파일 내용(string)을 읽어 들여 double 값으로 변경 후, 1000으로 나눔.

    qDebug() << "Temperature: " << this->temperature << "Humidity: " << this->humidity;    // 디버그 용

    return 0;
}


마지막으로 IoTSensor.pro 파일을 소개하면 다음과 같다.

QT += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = IoTSensor
TEMPLATE = app

SOURCES += main.cpp mainwindow.cpp

HEADERS += mainwindow.h

FORMS += mainwindow.ui

INSTALLS += target    // 이 두 줄을 적어 주어야,  target board의 원하는 위치로 파일이 복사될 것임.
target.path=/root/workspace

여기까지 해서 모든 코드가 준비되었으니, target board에서 돌려 보도록 하자.


[그림 6.3] Build & Run - Buildroot ARM => Build  & Run

좌측 하단의 실행 버튼을 선택하면, (최초 실행 시)target board에 로긴하기 위해 root password를 확인하는 popup 창이 뜨게 된다.

[그림 6.4] Target board로의 SSH login 화면

정확한 root password 값을 입력하고 나면, 아래와 같은 화면이 보이게 될 것이다. 😎

[그림 6.5] 센서 app 실행 모습

[그림 6.6] target board에 upload된 파일(IoTSensor)  확인

[그림 6.7] 센서 app 실행  상태에서 ps 명령 실행 모습

b) 온/습도 센서 결과를 외부로 전송하는 client & server 만들기
온/습도 센서로 부터 읽어 들인 정보를 Cloud server로 전달하는 경우를 가정하고 아래와 같은 간단한 client & server program을 만들어 보도록 하겠다.

DK2 board(client daemon) ========> internet ======> AWS EC2(Amazon cloud server, Ubuntu 18.04)

아주 간단한 내용이니, 이 부분은 독자 여러분의 몫으로 남기도록 하겠다. 😜


c) 암호 통신하기
이 절에서는 open source VPN solution인 WireGuard를 이용해 client와 server 간에 암호 통신하는 과정을 소개하고자 하였으나, 내용이 너무 길어지는 듯하여 지난 번에 소개했던 내용으로 대신하고자 한다. 관심있는 분들은 아래 blog post의 내용을 참조해 주기 바란다.




============

지금까지 다섯 차례에 걸쳐 STMP32MP157C Discovery Kit를 가지고 다양한 시도를 해 보았다. 사실 이번 blog post를 작성하는데 결정적인 역할을 한 것은 Thomas Petazzoni @ bootlin의 아래 글이다. 


이 자리를 빌어 다시한번 감사의 마음을 전한다. Thank you so much, Thomas !

물론, (약간의 첨언을 하자면) 전반적인 내용을 참조한 것은 맞지만, 내용을 그대로 베끼지는 않았으며, 국내 현실에 맞게 새로운 느낌의 blog post로 재 탄생시키려 노력하였다.

끝으로, 이 글이 Embedded Linux 개발자 혹은 개발자를 꿈꾸는 이들(특히 국내 개발자)에게 조금이나마 보탬이 되었기를 희망해 본다. 👏


May the source be with you
(소스가 그대와 함께 하길 ~)



<확장 편>
7. Cortex-M4 환경에서의 firmware 개발
앞선 장에서는 Cortex-A7 MPU 환경(Linux)에 대해서만 이야기하였다. 하지만 이번 장에서는 DK2 보드에 존재하는 또 다른 CPU인 Cortex-M4(ST firmware)에 관하여 이야기하고자 한다. 사실 DK2를 개발한 ST Microelectronics의 (지금까지의) 주력 분야는 이 부분이었다. 즉, 소규모 embedded system 개발에 관한한 ST Microelectronics의 chip과 s/w(firmware)가 업계 1위(정확한 근거가 있는 것은 아님)라고 말할 수 있을 정도로 널리 사용되고 있다.

To be continued ...



8. References
[4] Exploring Raspberry Pi, Derek Molloy
[5] Application Development with Qt Creator, 2nd edition, Ray Rischpater



Slowboot


댓글 1개: