# IoT 장치에서의 센서 관리

IoT 장치는 다양한 센서를 통해 데이터를 수집하고, 이 데이터를 기반으로 여러 기능을 수행한다. 센서 관리란, 이러한 센서들을 제어하고 데이터를 읽어오는 과정을 의미한다. Yocto 프로젝트를 통해 이러한 센서 관리 작업을 어떻게 구현할 수 있는지 설명하겠다.

#### 센서 인터페이스

IoT 장치에서 사용되는 센서들은 주로 다양한 인터페이스를 통해 연결된다. 주요 인터페이스로는 다음과 같은 것들이 있다:

* **I2C (Inter-Integrated Circuit)**: 여러 개의 장치가 한 버스를 공유하면서 통신할 수 있게 해주는 직렬 버스 인터페이스.
* **SPI (Serial Peripheral Interface)**: 높은 속도로 데이터를 전송할 수 있는 직렬 버스 인터페이스.
* **UART (Universal Asynchronous Receiver/Transmitter)**: 송신선과 수신선을 사용해 데이터를 전송하는 직렬 통신 인터페이스.
* **GPIO (General Purpose Input/Output)**: 입출력을 자유롭게 설정할 수 있는 범용 핀.

#### 드라이버 개발

IoT 장치에서 센서를 관리하기 위해서는, 각 센서에 맞는 드라이버를 개발하거나 이미 존재하는 드라이버를 Yocto 프로젝트에 통합해야 한다.

**드라이버 예시 (I2C 센서 드라이버)**

여기서는 I2C 인터페이스를 사용하는 센서의 드라이버를 개발하는 예시를 들어보겠다.

1. **Device Tree 설정**: Linux 커널에서 I2C 장치를 인식하게 하기 위해, 장치 트리(Device Tree)를 설정해야 한다.

```dts
&i2c1 {
    status = "okay";
    sensor@77 {
        compatible = "sensor,example";
        reg = <0x77>;
    };
};
```

2. **커널 모듈 작성**: 이제 커널 모듈을 작성하여 I2C 센서와 통신할 수 있게 한다.

```c
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>

#define SENSOR_NAME "example_sensor"

static const struct i2c_device_id example_id[] = {
    { SENSOR_NAME, 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, example_id);

static const struct of_device_id example_of_match[] = {
    { .compatible = "sensor,example", },
    { }
};
MODULE_DEVICE_TABLE(of, example_of_match);

static int example_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    printk(KERN_INFO "Example sensor probed\n");
    return 0;
}

static int example_remove(struct i2c_client *client) {
    printk(KERN_INFO "Example sensor removed\n");
    return 0;
}

static struct i2c_driver example_driver = {
    .driver = {
        .name = SENSOR_NAME,
        .of_match_table = example_of_match,
    },
    .probe = example_probe,
    .remove = example_remove,
    .id_table = example_id,
};

module_i2c_driver(example_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple I2C sensor driver");
```

3. **디바이스 드라이버 빌드와 설치**: 작성된 드라이버를 Yocto 빌드 시스템에서 빌드하고 설치한다.

#### 센서 데이터 수집

드라이버가 준비된 후에는 센서 데이터를 수집하는 작업을 해야 한다. 이를 위해 주로 응용 프로그래밍 인터페이스(API) 또는 사용자 공간에서 실행되는 응용 프로그램을 사용한다.

**사용자 공간 응용프로그램**

드라이버를 통해 커널 공간에서 제공하는 인터페이스를 사용하여 센서 데이터를 읽어오는 간단한 프로그램을 만들어 보겠다.

```c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <linux/i2c-dev.h>

int main() {
    int file;
    char *filename = "/dev/i2c-1";
    int addr = 0x77; // Example sensor address

    if ((file = open(filename, O_RDWR)) < 0) {
        perror("Failed to open the i2c bus");
        return 1;
    }

    if (ioctl(file, I2C_SLAVE, addr) < 0) {
        perror("Failed to acquire bus access and/or talk to slave");
        return 1;
    }

    // Example: Read 2 bytes of data from the sensor
    uint8_t buf[2];
    if (read(file, buf, 2) != 2) {
        perror("Failed to read from the sensor");
        return 1;
    } else {
        printf("Data read: 0x%02x%02x\n", buf[0], buf[1]);
    }

    close(file);
    return 0;
}
```

이렇게 함으로써 다양한 센서를 Yocto 기반의 IoT 장치에서 관리하고 데이터를 수집할 수 있다. 이 과정에서 중요한 것은 각 센서의 인터페이스와 특성에 맞는 드라이버와 응용 프로그램을 개발하는 것이다.

IoT 장치에서 센서 관리를 구현하는 방법에 대해 더 설명하겠다.

#### 센서 데이터 처리 및 저장

센서에서 수집한 데이터는 어떻게 처리하고 저장할지도 매우 중요한 문제이다. IoT 장치가 단순히 데이터를 수집하는 역할만 하는 것이 아니라, 그 데이터를 분석하고 필요한 액션을 취할 수 있어야 한다. 다음과 같은 방법으로 센서 데이터를 처리하고 저장할 수 있다.

**데이터 처리**

1. **실시간 분석**: 실시간으로 데이터를 분석하여 즉각적인 결정을 내릴 수 있다. 예를 들어 온도 센서가 특정 임계값을 초과하면 알람을 발생시키거나 냉각 장치를 켤 수 있다.
2. **로컬 저장소**: 장치 내부의 메모리나 외부 저장 장치에 데이터를 저장할 수 있다. 주로 SQLite 같은 경량 데이터베이스를 사용한다.
3. **클라우드 전송**: 수집한 데이터를 주기적으로 클라우드 서버로 전송하여 중앙에서 데이터를 저장하고 분석할 수 있다. 이를 위해 MQTT, HTTP, CoAP 같은 프로토콜을 사용할 수 있다.

**데이터베이스 예시 (SQLite 사용)**

여기서는 SQLite를 사용하여 센서 데이터를 로컬 저장소에 저장하는 방법을 예시로 들어보겠다.

```c
#include <stdio.h>
#include <sqlite3.h>

int main() {
    sqlite3 *db;
    char *err_msg = 0;
    int rc;

    rc = sqlite3_open("sensor_data.db", &db);

    if (rc != SQLITE_OK) {
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }

    char *sql = "CREATE TABLE IF NOT EXISTS SensorData(Id INTEGER PRIMARY KEY, Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, Data REAL);";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
    }

    float sensor_data = 23.5; // Example sensor data
    char insert_sql[256];
    sprintf(insert_sql, "INSERT INTO SensorData(Data) VALUES(%f);", sensor_data);

    rc = sqlite3_exec(db, insert_sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
    } else {
        printf("Data inserted successfully\n");
    }

    sqlite3_close(db);
    return 0;
}
```

#### 클라우드 전송

센서 데이터를 클라우드로 전송하는 것은 IoT 시스템에서 매우 중요한 요소이다. 여러 가지 방법 중에서 가장 일반적으로 사용되는 방법은 다음과 같다.

* **HTTP/HTTPS**: REST API를 사용하여 데이터를 전송한다.
* **MQTT**: 경량 메시지 프로토콜로, IoT 장치 간의 메시지 전송에 매우 적합한다.
* **CoAP**: 간단하고 경량의 프로토콜로, 소형 장치에 적합한다.

**MQTT 사용 예시**

여기서는 매우 간단한 예제로, paho.mqtt.c 라이브러리를 사용하여 MQTT를 통해 데이터를 전송하는 방법을 보여드리겠다.

```c
#include <stdio.h>
#include "MQTTClient.h"

#define ADDRESS     "tcp://broker.hivemq.com:1883"
#define CLIENTID    "ExampleClientPub"
#define TOPIC       "sensor/data"
#define QOS         1
#define TIMEOUT     10000L

int main(int argc, char* argv[]) {
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc;

    MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        return -1;
    }

    char payload[100];
    sprintf(payload, "sensor data: %f", 23.5); // Example sensor data
    pubmsg.payload = payload;
    pubmsg.payloadlen = strlen(payload);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
    printf("Waiting for up to %d seconds for publication of %s\n", (int)(TIMEOUT/1000), payload);
    rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
    printf("Message with delivery token %d delivered\n", token);

    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}
```

이제 IoT 장치에서 센서 데이터를 수집하고, 로컬에 저장하거나 클라우드로 전송하는 방법에 대한 전반적인 과정을 이해하셨을 겁니다.
