目的
パルダリウム用のタイマー式照明を普通に買うと高くつく → NTPとかで時刻同期を取って照明を管理するシステムを作ろう
部品
- 100Ω
- BME280
- 温湿度管理をする場合。今回は在庫なしだったので見送り。
- MOSFET 2SK4017 https://akizukidenshi.com/catalog/g/g107597/
- 照明用の5Vをスイッチできるやつならなんでも
- USB Aメス https://akizukidenshi.com/catalog/g/g107429/
- パルダリウム用の照明がUSB-Aから電源を取るので
- Cメス https://akizukidenshi.com/catalog/g/g115426/
- 電源をとってくる
- ESP32C3 https://akizukidenshi.com/catalog/g/g117454/
- なんでもいいけど、小さくてRustで開発できる、RISC-V、の順で検討した
Rustを書く
リポジトリ: cordx56/paluda-man
- nightlyしか使えない
esp_idf_svc とか embedded_svc とかのcrateを使う
use embedded_svc::ota::*;
use esp_idf_svc::{
hal::{gpio::*, reset::restart},
http::{
server::{self, EspHttpServer},
Method,
},
io::{Read, Write},
nvs::*,
ota::*,
sntp::*,
sys::EspError,
wifi::EspWifi,
};
WiFiを繋ぐ
サンプルとかを見つつ適当に。ソースコードはこの辺。
fn wifi() -> Result<EspWifi<'static>, EspError> {
use esp_idf_svc::eventloop::*;
use esp_idf_svc::hal::peripherals::Peripherals;
use esp_idf_svc::wifi::*;
let peripherals = Peripherals::take()?;
let sys_loop = EspSystemEventLoop::take()?;
let mut esp_wifi = EspWifi::new(peripherals.modem, sys_loop.clone(), None)?;
let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sys_loop.clone())?;
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
ssid: SSID.try_into().unwrap(),
password: PASS.try_into().unwrap(),
..Default::default()
}))?;
wifi.start()?;
info!("WiFi started");
wifi.connect()?;
info!("WiFi connected");
wifi.wait_netif_up()?;
info!("WiFi netif up");
Ok(esp_wifi)
}
HTTP server
EspのHTTPサーバを使う。 ソースコード
let mut server = EspHttpServer::new(&server::Configuration {
stack_size: 10240,
..Default::default()
})?;
server.fn_handler("/", Method::Get, |req| {
let on = nvs.get_i8(SCHEDULE_ON_TAG)?.unwrap_or(-1);
let off = nvs.get_i8(SCHEDULE_OFF_TAG)?.unwrap_or(-1);
req.into_ok_response()?
.write_all(html(on, off).as_bytes())
.map(|_| ())
})?;
OTA update
OTAアップデートをしたかったので、HTTPサーバにデータを送りつけたらアップデートするやつを実装した。
server.fn_handler("/ota", Method::Post, |mut req| {
let mut ota = EspOta::new()?;
let update = ota.initiate_update()?;
info!("OTA initiate");
info!("Start OTA update...");
let updated = update.update(&mut req, |_, _| ());
restart();
req.into_ok_response()?
.write_all(format!("{:?}", updated).as_bytes())
.map(|_| ())
})?;
比較的簡単に実装できる。
こけたポイント
OTAアップデートにはパーティションをいじる必要がある
パーティションテーブルはこんな感じになった。
ESPのサーバのスタックサイズが足りない
デフォルトだと普通のHTMLを返すのでも厳しかった。
let mut server = EspHttpServer::new(&server::Configuration {
stack_size: 10240,
..Default::default()
})?;
EspHttpServer を作る時に、 stack_size を大きめに取る。