# 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();
}
```