This commit is contained in:
shimun 2024-05-06 22:12:00 +02:00
parent a03e890349
commit 14d8f7f683
Signed by: shimun
GPG Key ID: E0420647856EA39E
4 changed files with 134 additions and 93 deletions

2
Cargo.lock generated
View File

@ -701,6 +701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e98f0f58453dd2ce08d99228fc8757fad39d05dfd26643665d1093b8844f42cc" checksum = "e98f0f58453dd2ce08d99228fc8757fad39d05dfd26643665d1093b8844f42cc"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"log",
] ]
[[package]] [[package]]
@ -792,6 +793,7 @@ dependencies = [
"esp-println", "esp-println",
"esp-wifi", "esp-wifi",
"heapless 0.8.0", "heapless 0.8.0",
"log",
"mqttrust", "mqttrust",
"rand", "rand",
"rand_core", "rand_core",

View File

@ -8,7 +8,7 @@ authors = [ "Marvin Drescher <m@sparv.in>" ]
[dependencies] [dependencies]
embassy-executor = { version = "0.5.0", features = ["nightly", "integrated-timers", "arch-riscv32", "executor-thread"] } embassy-executor = { version = "0.5.0", features = ["nightly", "integrated-timers", "arch-riscv32", "executor-thread"] }
embassy-net = { version = "0.4.0", features = ["dhcpv4", "dhcpv4-hostname", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp"] } embassy-net = { version = "0.4.0", features = ["dhcpv4", "dhcpv4-hostname", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"] }
embassy-sync = "0.5.0" embassy-sync = "0.5.0"
embassy-time = { version = "0.3.0" } embassy-time = { version = "0.3.0" }
embedded-io-async = "0.6.1" embedded-io-async = "0.6.1"
@ -17,9 +17,10 @@ esp-alloc = "0.3.0"
esp-backtrace = { version = "0.11.0", features = ["esp32c3", "exception-handler", "panic-handler", "println"] } esp-backtrace = { version = "0.11.0", features = ["esp32c3", "exception-handler", "panic-handler", "println"] }
esp-hal = { version = "0.16.1", features = ["embassy", "embassy-time-timg0", "esp32c3"] } esp-hal = { version = "0.16.1", features = ["embassy", "embassy-time-timg0", "esp32c3"] }
esp-hal-smartled = { version = "0.9.0", features = ["esp32c3"] } esp-hal-smartled = { version = "0.9.0", features = ["esp32c3"] }
esp-println = { version = "0.9.1", features = ["esp32c3", "uart"] } esp-println = { version = "0.9.1", features = ["esp32c3", "log", "uart"] }
esp-wifi = { version = "0.4.0", features = ["embassy-net", "esp32c3", "wifi"] } esp-wifi = { version = "0.4.0", features = ["embassy-net", "esp32c3", "wifi"] }
heapless = { version = "0.8.0", features = ["portable-atomic", "portable-atomic-unsafe-assume-single-core"] } heapless = { version = "0.8.0", features = ["portable-atomic", "portable-atomic-unsafe-assume-single-core"] }
log = "0.4.21"
mqttrust = "0.6.0" mqttrust = "0.6.0"
rand = { version = "0.8.5", default-features = false, features = ["std_rng"] } rand = { version = "0.8.5", default-features = false, features = ["std_rng"] }
rand_core = "0.6.4" rand_core = "0.6.4"
@ -28,4 +29,10 @@ smart-leds = "0.4.0"
static_cell = { version = "2.0.0", features = ["nightly"] } static_cell = { version = "2.0.0", features = ["nightly"] }
[profile.dev.package.esp-wifi] [profile.dev.package.esp-wifi]
opt-level = 2 opt-level = 3
[profile.dev.package.embedded-tls]
opt-level = 3
[profile.release]
opt-level = "s"
lto = true

View File

@ -33,10 +33,12 @@ use esp_hal::{adc::AdcConfig, clock::ClockControl};
use esp_hal::{embassy, prelude::*, rmt, Rmt, Rng, Rtc, IO}; use esp_hal::{embassy, prelude::*, rmt, Rmt, Rng, Rtc, IO};
use esp_hal_smartled::SmartLedsAdapter; use esp_hal_smartled::SmartLedsAdapter;
use esp_hal_smartled::*; use esp_hal_smartled::*;
use esp_println::logger::init_logger;
use esp_println::println; use esp_println::println;
use esp_wifi::wifi::{get_random, ClientConfiguration, Configuration}; use esp_wifi::wifi::{get_random, ClientConfiguration, Configuration};
use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiStaDevice, WifiState}; use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiStaDevice, WifiState};
use esp_wifi::{initialize as initialize_wifi, EspWifiInitFor}; use esp_wifi::{initialize as initialize_wifi, EspWifiInitFor};
use log::{debug, error, info, trace};
use smart_leds::{SmartLedsWrite, RGB8}; use smart_leds::{SmartLedsWrite, RGB8};
use static_cell::{make_static, StaticCell}; use static_cell::{make_static, StaticCell};
@ -69,7 +71,7 @@ impl<TX: rmt::TxChannel, const BUFFER_SIZE: usize> MySmartLed
{ {
fn set_color(&mut self, color: RGB8) { fn set_color(&mut self, color: RGB8) {
if let Err(err) = self.write([color].into_iter()) { if let Err(err) = self.write([color].into_iter()) {
esp_println::println!("{err:?}"); error!("{err:?}");
} }
} }
} }
@ -80,7 +82,6 @@ pub static DATA: Mutex<CriticalSectionRawMutex, BTreeMap<Cow<'static, str>, Stri
#[embassy_executor::task] #[embassy_executor::task]
async fn blink(mut led: Box<dyn MySmartLed>) { async fn blink(mut led: Box<dyn MySmartLed>) {
loop { loop {
esp_println::println!("Bing!");
let scale = 4; let scale = 4;
for r in 0..(255 / scale) { for r in 0..(255 / scale) {
Timer::after(Duration::from_millis(1)).await; Timer::after(Duration::from_millis(1)).await;
@ -100,17 +101,25 @@ async fn blink(mut led: Box<dyn MySmartLed>) {
} }
} }
macro_rules! nb_await {
($ex:expr, $interval:expr) => {
loop {
match $ex {
Err(nb::Error::WouldBlock) => Timer::after($interval).await,
Err(nb::Error::Other(e)) => break Err(e),
Ok(val) => break Ok(val),
}
}
};
($ex:expr) => {
$crate::nb_await!($ex, Duration::from_millis(10))
};
}
async fn async_read<T, E>( async fn async_read<T, E>(
mut fun: impl FnMut() -> nb::Result<T, E>, mut fun: impl FnMut() -> nb::Result<T, E>,
interval: Duration, interval: Duration,
) -> Result<T, E> { ) -> Result<T, E> {
loop { nb_await!(fun(), interval)
match fun() {
Err(nb::Error::WouldBlock) => Timer::after(interval).await,
Err(nb::Error::Other(e)) => return Err(e),
Ok(val) => return Ok(val),
}
}
} }
/// https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32c3/api-reference/peripherals/adc.html /// https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32c3/api-reference/peripherals/adc.html
@ -148,7 +157,7 @@ async fn moisture(
let moisture = ((milli_volt.checked_sub(submerged_in_water).unwrap_or(0)) let moisture = ((milli_volt.checked_sub(submerged_in_water).unwrap_or(0))
<< 8 / (dry - submerged_in_water)) << 8 / (dry - submerged_in_water))
>> 8; >> 8;
esp_println::println!("moisture: {moisture}%, v_s: {milli_volt}"); info!("moisture: {moisture}%, v_s: {milli_volt}");
{ {
DATA.lock() DATA.lock()
.await .await
@ -174,7 +183,7 @@ async fn battery_monitor(
); );
// account for 50:50 voltage divider // account for 50:50 voltage divider
let v_bat = v_out * 2; let v_bat = v_out * 2;
println!("V_bat: {}", v_bat); info!("V_bat: {}", v_bat);
{ {
DATA.lock() DATA.lock()
.await .await
@ -187,7 +196,7 @@ static EXECUTOR: StaticCell<Executor> = StaticCell::new();
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
esp_println::println!("Init!"); init_logger(log::LevelFilter::Info);
init_heap(); init_heap();
let peripherals = Peripherals::take(); let peripherals = Peripherals::take();
@ -270,7 +279,7 @@ fn main() -> ! {
spawner.spawn(net_task(&stack)).unwrap(); spawner.spawn(net_task(&stack)).unwrap();
spawner.spawn(ip_task(&stack)).unwrap(); spawner.spawn(ip_task(&stack)).unwrap();
} }
spawner.spawn(blink(led)).unwrap(); // spawner.spawn(blink(led)).unwrap();
spawner spawner
.spawn(moisture(pin, adc1, moisture_sensor_suppy_pin.into())) .spawn(moisture(pin, adc1, moisture_sensor_suppy_pin.into()))
.unwrap(); .unwrap();
@ -285,8 +294,8 @@ async fn wifi_connection(
ssid: &'static str, ssid: &'static str,
psk: &'static str, psk: &'static str,
) { ) {
println!("start connection task"); trace!("start wifi connection task");
println!("Device capabilities: {:?}", controller.get_capabilities()); debug!("Device capabilities: {:?}", controller.get_capabilities());
loop { loop {
match esp_wifi::wifi::get_wifi_state() { match esp_wifi::wifi::get_wifi_state() {
WifiState::StaConnected => { WifiState::StaConnected => {
@ -303,16 +312,16 @@ async fn wifi_connection(
..Default::default() ..Default::default()
}); });
controller.set_configuration(&client_config).unwrap(); controller.set_configuration(&client_config).unwrap();
println!("Starting wifi"); trace!("starting wifi");
controller.start().await.unwrap(); controller.start().await.unwrap();
println!("Wifi started!"); trace!("Wifi started!");
} }
println!("About to connect..."); trace!("About to connect...");
match controller.connect().await { match controller.connect().await {
Ok(_) => println!("Wifi connected!"), Ok(_) => info!("Wifi connected!"),
Err(e) => { Err(e) => {
println!("Failed to connect to wifi: {e:?}"); error!("Failed to connect to wifi: {e:?}");
Timer::after(Duration::from_millis(5000)).await Timer::after(Duration::from_millis(5000)).await
} }
} }
@ -327,10 +336,10 @@ async fn ip_task(stack: NetworkStack) {
} }
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;
} }
println!("Waiting to get IP address..."); debug!("Waiting to get IP address...");
loop { loop {
if let Some(config) = stack.config_v4() { if let Some(config) = stack.config_v4() {
println!("Got IP: {}", config.address); info!("Got IP: {}", config.address);
break; break;
} }
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;

View File

@ -1,8 +1,10 @@
use embassy_net::tcp::TcpSocket; use embassy_net::tcp::TcpSocket;
use embassy_net::{dns::Error as DnsError, tcp::ConnectError}; use embassy_net::{dns::Error as DnsError, tcp::ConnectError};
use embassy_time::{Duration, Timer}; use embassy_time::{with_timeout, Duration, TimeoutError, Timer};
use embedded_tls::{Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext, TlsError}; use embedded_tls::{Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext, TlsError};
use esp_backtrace as _;
use esp_println::println; use esp_println::println;
use log::{debug, error, info, trace};
use rand::rngs::StdRng; use rand::rngs::StdRng;
use rand::{CryptoRng, RngCore, SeedableRng}; use rand::{CryptoRng, RngCore, SeedableRng};
use rust_mqtt::client::client::MqttClient; use rust_mqtt::client::client::MqttClient;
@ -19,6 +21,7 @@ pub enum SendError {
Tls(TlsError), Tls(TlsError),
Connect(ConnectError), Connect(ConnectError),
MqttReason(ReasonCode), MqttReason(ReasonCode),
Timeout(TimeoutError),
} }
macro_rules! from_impl { macro_rules! from_impl {
@ -35,6 +38,7 @@ from_impl!(DnsError, Dns);
from_impl!(TlsError, Tls); from_impl!(TlsError, Tls);
from_impl!(ConnectError, Connect); from_impl!(ConnectError, Connect);
from_impl!(ReasonCode, MqttReason); from_impl!(ReasonCode, MqttReason);
from_impl!(TimeoutError, Timeout);
const MQTT_SERVER_HOSTNAME: &str = "mqtt.shimun.net"; const MQTT_SERVER_HOSTNAME: &str = "mqtt.shimun.net";
const MQTT_SERVER_PORT: u16 = 8883; const MQTT_SERVER_PORT: u16 = 8883;
@ -43,23 +47,32 @@ pub async fn send_message(
stack: NetworkStack, stack: NetworkStack,
mut messages: impl Iterator<Item = (&str, &[u8])>, mut messages: impl Iterator<Item = (&str, &[u8])>,
mut rng: impl CryptoRng + RngCore, mut rng: impl CryptoRng + RngCore,
) -> core::result::Result<(), SendError> {
async fn inner(
stack: NetworkStack,
mut messages: impl Iterator<Item = (&str, &[u8])>,
mut rng: impl CryptoRng + RngCore,
) -> core::result::Result<(), SendError> { ) -> core::result::Result<(), SendError> {
let dns_resp = stack let dns_resp = stack
.dns_query(MQTT_SERVER_HOSTNAME, embassy_net::dns::DnsQueryType::A) .dns_query(MQTT_SERVER_HOSTNAME, embassy_net::dns::DnsQueryType::A)
.await .await
.map_err(SendError::Dns)?; .map_err(SendError::Dns)?;
let mut rx_buffer = [0; 4096]; const TCP_BUFLEN: usize = 2048 << 2;
let mut tx_buffer = [0; 4096]; let mut rx_buffer = [0; TCP_BUFLEN];
let mut tx_buffer = [0; TCP_BUFLEN];
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(Duration::from_secs(10))); socket.set_timeout(Some(Duration::from_secs(30)));
let socket_addr = dns_resp let socket_addr = dns_resp
.into_iter() .into_iter()
.map(|addr| (addr, MQTT_SERVER_PORT)) .map(|addr| (addr, MQTT_SERVER_PORT))
.next() .next()
.ok_or(SendError::NXDomain(MQTT_SERVER_HOSTNAME))?; .ok_or(SendError::NXDomain(MQTT_SERVER_HOSTNAME))?;
debug!("got address {socket_addr:?}");
// establish TCP connection // establish TCP connection
socket.connect(socket_addr).await?; socket.connect(socket_addr).await?;
trace!("connected");
// seed mqtt rng from rng // seed mqtt rng from rng
let mut mqtt_config = ClientConfig::<5, _>::new( let mut mqtt_config = ClientConfig::<5, _>::new(
@ -73,13 +86,11 @@ pub async fn send_message(
} }
// TLS layer // TLS layer
const TLS_BUF_LEN: usize = 1 << 12; const TLS_BUF_LEN: usize = 4096;
let mut tls_read_record_buffer = [0; TLS_BUF_LEN]; let mut tls_read_record_buffer = [0; TLS_BUF_LEN];
let mut tls_write_record_buffer = [0; TLS_BUF_LEN]; let mut tls_write_record_buffer = [0; TLS_BUF_LEN];
let mut tls = { let mut tls = {
let config = TlsConfig::new() let config = TlsConfig::new();
.with_server_name(MQTT_SERVER_HOSTNAME)
.with_max_fragment_length(embedded_tls::MaxFragmentLength::Bits12);
let mut tls = TlsConnection::new( let mut tls = TlsConnection::new(
socket, socket,
@ -91,11 +102,14 @@ pub async fn send_message(
.await?; .await?;
tls tls
}; };
debug!("tls handshake succeeded");
const BUF_LEN: usize = 1 << 13; const BUF_LEN: usize = 1024;
let mut recv_buffer = [0; BUF_LEN]; let mut recv_buffer = [0; BUF_LEN];
let mut buffer = [0; BUF_LEN]; let mut buffer = [0; BUF_LEN];
mqtt_config.add_client_id("esp32c3");
mqtt_config.max_packet_size = (BUF_LEN - 128) as _;
// MQTT Layer // MQTT Layer
let mut mqtt_client = MqttClient::new( let mut mqtt_client = MqttClient::new(
tls, tls,
@ -106,6 +120,7 @@ pub async fn send_message(
mqtt_config, mqtt_config,
); );
mqtt_client.connect_to_broker().await?; mqtt_client.connect_to_broker().await?;
info!("connected to broker");
for (topic, message) in messages { for (topic, message) in messages {
mqtt_client mqtt_client
.send_message( .send_message(
@ -118,12 +133,19 @@ pub async fn send_message(
} }
Ok(()) Ok(())
} }
with_timeout(Duration::from_secs(60), inner(stack, messages, rng)).await?
}
#[embassy_executor::task] #[embassy_executor::task]
pub async fn publish_data(stack: NetworkStack, rng_seed: [u8; 32], interval: Duration) { pub async fn publish_data(stack: NetworkStack, rng_seed: [u8; 32], interval: Duration) {
let mut rng = StdRng::from_seed(rng_seed); let mut rng = StdRng::from_seed(rng_seed);
loop { loop {
Timer::after(interval).await; Timer::after(interval / 10).await;
if stack.is_config_up() {
break;
}
}
loop {
let mut data = DATA.lock().await; let mut data = DATA.lock().await;
let res = send_message( let res = send_message(
stack, stack,
@ -133,8 +155,9 @@ pub async fn publish_data(stack: NetworkStack, rng_seed: [u8; 32], interval: Dur
) )
.await; .await;
if let Err(e) = res { if let Err(e) = res {
println!("Oh no: {e:?}"); error!("Oh no: {e:?}");
}; };
data.clear(); data.clear();
Timer::after(interval).await;
} }
} }