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"
dependencies = [
"critical-section",
"log",
]
[[package]]
@ -792,6 +793,7 @@ dependencies = [
"esp-println",
"esp-wifi",
"heapless 0.8.0",
"log",
"mqttrust",
"rand",
"rand_core",

View File

@ -8,7 +8,7 @@ authors = [ "Marvin Drescher <m@sparv.in>" ]
[dependencies]
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-time = { version = "0.3.0" }
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-hal = { version = "0.16.1", features = ["embassy", "embassy-time-timg0", "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"] }
heapless = { version = "0.8.0", features = ["portable-atomic", "portable-atomic-unsafe-assume-single-core"] }
log = "0.4.21"
mqttrust = "0.6.0"
rand = { version = "0.8.5", default-features = false, features = ["std_rng"] }
rand_core = "0.6.4"
@ -28,4 +29,10 @@ smart-leds = "0.4.0"
static_cell = { version = "2.0.0", features = ["nightly"] }
[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_smartled::SmartLedsAdapter;
use esp_hal_smartled::*;
use esp_println::logger::init_logger;
use esp_println::println;
use esp_wifi::wifi::{get_random, ClientConfiguration, Configuration};
use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiStaDevice, WifiState};
use esp_wifi::{initialize as initialize_wifi, EspWifiInitFor};
use log::{debug, error, info, trace};
use smart_leds::{SmartLedsWrite, RGB8};
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) {
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]
async fn blink(mut led: Box<dyn MySmartLed>) {
loop {
esp_println::println!("Bing!");
let scale = 4;
for r in 0..(255 / scale) {
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>(
mut fun: impl FnMut() -> nb::Result<T, E>,
interval: Duration,
) -> Result<T, E> {
loop {
match fun() {
Err(nb::Error::WouldBlock) => Timer::after(interval).await,
Err(nb::Error::Other(e)) => return Err(e),
Ok(val) => return Ok(val),
}
}
nb_await!(fun(), interval)
}
/// 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))
<< 8 / (dry - submerged_in_water))
>> 8;
esp_println::println!("moisture: {moisture}%, v_s: {milli_volt}");
info!("moisture: {moisture}%, v_s: {milli_volt}");
{
DATA.lock()
.await
@ -174,7 +183,7 @@ async fn battery_monitor(
);
// account for 50:50 voltage divider
let v_bat = v_out * 2;
println!("V_bat: {}", v_bat);
info!("V_bat: {}", v_bat);
{
DATA.lock()
.await
@ -187,7 +196,7 @@ static EXECUTOR: StaticCell<Executor> = StaticCell::new();
#[entry]
fn main() -> ! {
esp_println::println!("Init!");
init_logger(log::LevelFilter::Info);
init_heap();
let peripherals = Peripherals::take();
@ -270,7 +279,7 @@ fn main() -> ! {
spawner.spawn(net_task(&stack)).unwrap();
spawner.spawn(ip_task(&stack)).unwrap();
}
spawner.spawn(blink(led)).unwrap();
// spawner.spawn(blink(led)).unwrap();
spawner
.spawn(moisture(pin, adc1, moisture_sensor_suppy_pin.into()))
.unwrap();
@ -285,8 +294,8 @@ async fn wifi_connection(
ssid: &'static str,
psk: &'static str,
) {
println!("start connection task");
println!("Device capabilities: {:?}", controller.get_capabilities());
trace!("start wifi connection task");
debug!("Device capabilities: {:?}", controller.get_capabilities());
loop {
match esp_wifi::wifi::get_wifi_state() {
WifiState::StaConnected => {
@ -303,16 +312,16 @@ async fn wifi_connection(
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
println!("Starting wifi");
trace!("starting wifi");
controller.start().await.unwrap();
println!("Wifi started!");
trace!("Wifi started!");
}
println!("About to connect...");
trace!("About to connect...");
match controller.connect().await {
Ok(_) => println!("Wifi connected!"),
Ok(_) => info!("Wifi connected!"),
Err(e) => {
println!("Failed to connect to wifi: {e:?}");
error!("Failed to connect to wifi: {e:?}");
Timer::after(Duration::from_millis(5000)).await
}
}
@ -327,10 +336,10 @@ async fn ip_task(stack: NetworkStack) {
}
Timer::after(Duration::from_millis(500)).await;
}
println!("Waiting to get IP address...");
debug!("Waiting to get IP address...");
loop {
if let Some(config) = stack.config_v4() {
println!("Got IP: {}", config.address);
info!("Got IP: {}", config.address);
break;
}
Timer::after(Duration::from_millis(500)).await;

View File

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