---
# System prepended metadata

title: Albireo farm pi experiment/water_temperature
tags: [Albireo, raspberry pi, c/c++]

---

# Albireo farm pi experiment/water_temperature
###### tags: `Albireo` `raspberry pi` `c/c++`

## Wiring

### GPIO
- LED
- Peltier Element (H bridge circuit)
- 
### SPI
- BME280 (temperature, pressure, humidity)
- MCP3002
    - channel0 LM35DZ (peltier element temperature)
    - channel1 LM35DZ (water temperature)
### I2C
- TSL2561 (illuminance)

## Build
```shell=
cmake -G Ninja -B build -D BUILD_SHARED_LIBS=ON -D CMAKE_BUILD_TYPE=Debug
ninja -v -C build
sudo ./build/main
```

## 処理の流れ
塚ちゃんはここを実装してね

- データ収集
一定時間ごと（コマンドラインから変更可能にする）に全てのセンサの値を読み取り、サーバへ送信する。

- LED制御
サーバから設定を読み取り、次の点灯/消灯時間までスレッドを止め、点灯/消灯の処理を行う。
一定時間（コマンドラインから変更可能にする）よりも長くスレッドが止まることがないようにすることにする。

- 温度調節
センサから温度を読み取り、サーバから温度の設定を読み取り、比較してペルチェ素子を作動させる。
制御は一定時間ごと（コマンドラインから変更可能にする）に試みる
温度は一時間ごとに最低/最高温度が設定されているので中間の値を利用する。
    > 現在時刻 8:30
    > 8時の温度設定 最高/最低 10/20
    > 9時の温度設定 最高/最低 20/30
    > 制御に用いる温度設定 最高/最低 15/25


### 注意点
ハードウェアドライバはマルチスレッド非対応なので、mutexを利用して同時にドライバにアクセスすることがないようにする。
ハードウェアの設定は設定ファイル(TOMLがいいなぁ。ライブラリ追加しとくから)から変更可能にする。
ペルチェ素子はずっと電流を流していると熱暴走を起こすのでPWMを利用する。

## 導入済みライブラリ
### Submodule
- cpphttplib
https://github.com/yhirose/cpp-httplib/
A C++ header-only HTTP/HTTPS server and client library
- cxxopts
https://github.com/jarro2783/cxxopts/
Lightweight C++ command line option parser
- fmtlib
https://github.com/fmtlib/fmt/
A modern formatting library
- nlohmann json
https://github.com/nlohmann/json/
JSON for Modern C++
- paho-mqtt-c
https://github.com/eclipse/paho.mqtt.c
An Eclipse Paho C client library for MQTT for Windows, Linux and MacOS.
- paho-mqtt-cpp
https://github.com/eclipse/paho.mqtt.cpp
An Eclipse Paho C++ client library for MQTT for Windows, Linux and MacOS.
- pigpio
https://github.com/joan2937/pigpio/
pigpio is a C library for the Raspberry which allows control of the General Purpose Input Outputs (GPIO).
- spdlog
https://github.com/gabime/spdlog/
Fast C++ logging library.
- toml++
https://github.com/marzer/tomlplusplus/
Header-only TOML config file parser and serializer for C++17 (and later!).

> C言語でやるなら
> MQTTはpaho-mqtt-c
> HTTPはcurl
> JSONはjson-c
> TOMLは独自実装(パーサーだね！)
> コマンドライン引数も独自実装
> ロギングはlog.c
> ハードウェアもpigpioで書いてね(俺は書かないぞ)
> スレッドはpthread

## サンプルコード集

#### Logger
- criticul
- error
- warning
- info
- debug
- trace
```cpp=
#include <memory>
#include <string>

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/daily_file_sink.h"

int main() {
    std::string logger_name = "logger";
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::info);
    console_sink->set_pattern("[%Y/%m/%d %H:%M:%S.%e] [%^%l%$] %v");
    auto file_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("output.log", 0, 0);
    file_sink->set_level(spdlog::level::trace);
    file_sink->set_pattern("[%Y/%m/%d %H:%M:%S.%e] [%n] [%l] %v");
    spdlog::sinks_init_list sinks_list = {console_sink, file_sink};
    auto logger = std::make_shared<spdlog::logger>(logger_name, sinks_list);
    logger->set_level(spdlog::level::trace);
    logger->info("Hello!");
}
```

#### Command Line Arguments Parser
```cpp=
#include <iostream>
#include <string>
#include <vector>

#include "cxxopts.hpp"

int main(const int argc, char** argv) {
    cxxopts::Options options("sample", "This is a cxxopts sample program.");
    options.add_options()
        ("h,help", "show help message")
        ("b,bool", "bool parameter")
        ("i,int", "integer parameter", cxxopts::value<int>())
        ("s,string", "string parameter", cxxopts::value<std::string>())
        ("default", "using default value was false", cxxopts::value<bool>()->default_value("false"))
        ("implicit", "implicit parameter", cxxopts::value<int>()->implicit_value("12"))
        ("v,vector", "vector parameters", cxxopts::value<std::vector<int>>());
    options.add_options("group")
        ("option", "group option");
    auto result = options.parse(argc, argv);
    if (result.count("help") != 0) {
        std::cout << options.help() << std::endl;
        return 0;
    }
    if (result.count("int") != 0) {
        std::cout << result["int"].as<int>() << std::endl;
    }
    if (result.count("vector") != 0) {
        auto values = result["vector"].as<std::vector<int>>();
        std::cout << '[';
        for(auto&& i : values) {
            std::cout << i << ',';
        }
        std::cout << ']' << std::endl;
    }
    if (result["option"].count() != 0) {
        if(result["option"].as<bool>()) {
            std::cout << "group enabled" << std::endl;
        }
    }
}
```

#### JSON Perser
```cpp=
#include <iostream>
#include <string>

#include "nlohmann/json.hpp"

int main() {
    std::string str = R"(
        {
            "key": "value"
        }
    )";
    auto result = nlohmann::json::parse(str);
    std::cout << "key : " << result["key"] << std::endl;
}
```

#### TOML Perser
```toml=
[table]
key = "value"
```
```cpp=
#include <iostream>
#include <string>

#include "toml++/toml.h"

int main() {
    std::string filepath = "test.toml";
    auto result = toml::parse_file(filepath);
    auto value = result["table"]["key"].value<std::string>().value();
    std::cout << value << std::endl;
}
```

#### HTTP Request
```cpp=
#include <iostream>
#include <string>

#include "httplib.h"

int main() {
    std::string base_url = "http://localhost:8000";
    httplib::Client cli(base_url);
    auto res = cli.Get("/endpoint");
    std::cout << "status : " << res->status << '\n'
              << "body : " << res->body << std::endl;
}
```

#### MQTT Publish
```cpp=
#include <iostream>
#include <string>

#include "mqtt/client.h"

int main() {
    mqtt::client cli("tcp://localhost:1883", "");
    try {
        cli.connect();
        auto msg = mqtt::make_message("topic", "payload");
        cli.publish(msg);
        cli.disconnect();
    } catch (const mqtt::exception& e) {
        std::cout << e.what() << std::endl;
        return 0;
    }
}
```

#### Device Handler
```cpp=
#include <iostream>

#include "spdlog/spdlog.h"

#include "driver.hpp"
#include "device/bme280.hpp"
#include "device/light.hpp"

int main() {
    auto logger = spdlog::stdout_color_mt("logger");
    Driver driver(logger);
    driver.initialize();
    auto gpio = driver.createGpio(4, driver::Gpio::Mode::output);
    auto light = createLight(logger, gpio);
    light->initialize();
    light->turnOn();
    light->turnOff();
    light->finalize();
    auto pwm_plus = driver.createPwm(5, 255, 800);
    auto pwm_minus = driver.createPwm(6, 255, 800);
    auto peltier = createPeltier(
        logger, pwm_plus, pwm_minus, 30, 40);
    peltier->initialize();
    auto spi = driver.createSpi(0, 100000, 0, 0, false);
    auto bme280 = createBme280(logger, spi,
                               Bme280::Sampling::x16,
                               Bme280::Sampling::x16,
                               Bme280::Sampling::x16,
                               Bme280::Mode::forced,
                               Bme280::StandbyDuration::ms1000,
                               Bme280::Filter::x16);
    bme280->initialize();
    bme280->measure();
    std::cout << "temperature : " << bme280->getTemperature() << '\n'
              << "pressure : " << bme280->getPressure() << '\n'
              << "humidity : " << bme280->getHumidity() << std::endl;
    bme280->finalize();
    driver.finalize();
}
```
