vykostene
This commit is contained in:
@@ -9,20 +9,19 @@
|
|||||||
// TODO WARNING core 1 should be logic, core 0 wifi, its flipped now
|
// TODO WARNING core 1 should be logic, core 0 wifi, its flipped now
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_futures::select::{select, Either, select3, Either3};
|
use embassy_futures::select::{select, select3, Either, Either3};
|
||||||
use embassy_net::{Runner, StackResources};
|
use embassy_net::{Runner, StackResources};
|
||||||
use embassy_sync::signal::Signal;
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_time::{Duration, Timer, Instant};
|
use embassy_sync::signal::Signal;
|
||||||
use projekt_final::bus::I2cInner;
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use projekt_final::mqtt::client;
|
use projekt_final::mqtt::client;
|
||||||
|
|
||||||
use esp_alloc as _;
|
use esp_alloc as _;
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
|
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
gpio::InputConfig,
|
|
||||||
clock::CpuClock,
|
clock::CpuClock,
|
||||||
|
gpio::InputConfig,
|
||||||
gpio::{Input, Pull},
|
gpio::{Input, Pull},
|
||||||
i2c::master::{Config as I2cConfig, I2c},
|
i2c::master::{Config as I2cConfig, I2c},
|
||||||
rng::Rng,
|
rng::Rng,
|
||||||
@@ -34,25 +33,21 @@ use esp_wifi::{
|
|||||||
EspWifiController,
|
EspWifiController,
|
||||||
};
|
};
|
||||||
|
|
||||||
use pages_tui::input::Key;
|
use core::cell::RefCell;
|
||||||
|
use core::fmt::Write;
|
||||||
|
use heapless::String;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use pages_tui::input::Key;
|
||||||
use rust_mqtt::packet::v5::publish_packet::QualityOfService;
|
use rust_mqtt::packet::v5::publish_packet::QualityOfService;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use core::cell::RefCell;
|
|
||||||
use heapless::String;
|
|
||||||
use core::fmt::Write;
|
|
||||||
|
|
||||||
use projekt_final::{
|
use projekt_final::mqtt::client::{
|
||||||
bus,
|
mqtt_events, mqtt_publish, mqtt_subscribe, mqtt_task, mqtt_try_publish, IncomingMsg,
|
||||||
display,
|
|
||||||
mpu,
|
|
||||||
mqtt::client::{mqtt_events, mqtt_try_publish, mqtt_publish, mqtt_subscribe, mqtt_task, IncomingMsg},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
|
|
||||||
static I2C_BUS: StaticCell<RefCell<I2cInner>> = StaticCell::new();
|
|
||||||
static APP_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
static APP_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
||||||
static EXECUTOR_CORE1: StaticCell<esp_hal_embassy::Executor> = StaticCell::new();
|
static EXECUTOR_CORE1: StaticCell<esp_hal_embassy::Executor> = StaticCell::new();
|
||||||
static NETWORK_READY: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
static NETWORK_READY: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
@@ -83,17 +78,6 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
let peripherals = esp_hal::init(config);
|
let peripherals = esp_hal::init(config);
|
||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
info!("Initializing I2C bus...");
|
|
||||||
let i2c = I2c::new(peripherals.I2C0, I2cConfig::default())
|
|
||||||
.expect("Failed to create I2C instance")
|
|
||||||
.with_sda(peripherals.GPIO21)
|
|
||||||
.with_scl(peripherals.GPIO22)
|
|
||||||
.into_async();
|
|
||||||
|
|
||||||
let i2c_bus = I2C_BUS.init(RefCell::new(i2c));
|
|
||||||
let display_i2c = bus::new_device(i2c_bus);
|
|
||||||
let mpu_i2c = bus::new_device(i2c_bus);
|
|
||||||
|
|
||||||
info!("Initializing WiFi...");
|
info!("Initializing WiFi...");
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
let mut rng = Rng::new(peripherals.RNG);
|
let mut rng = Rng::new(peripherals.RNG);
|
||||||
@@ -103,8 +87,7 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
esp_wifi::init(timg0.timer0, rng.clone()).unwrap()
|
esp_wifi::init(timg0.timer0, rng.clone()).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let (controller, interfaces) =
|
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, peripherals.WIFI).unwrap();
|
||||||
esp_wifi::wifi::new(esp_wifi_ctrl, peripherals.WIFI).unwrap();
|
|
||||||
|
|
||||||
let wifi_interface = interfaces.sta;
|
let wifi_interface = interfaces.sta;
|
||||||
let timg1 = TimerGroup::new(peripherals.TIMG1);
|
let timg1 = TimerGroup::new(peripherals.TIMG1);
|
||||||
@@ -114,15 +97,21 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
|
|
||||||
// Start core 1 for WiFi and MQTT (network stack created there)
|
// Start core 1 for WiFi and MQTT (network stack created there)
|
||||||
let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);
|
let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);
|
||||||
let _guard = cpu_control.start_app_core(
|
let _guard = cpu_control
|
||||||
APP_CORE_STACK.init(Stack::new()),
|
.start_app_core(APP_CORE_STACK.init(Stack::new()), move || {
|
||||||
move || {
|
|
||||||
let executor = EXECUTOR_CORE1.init(esp_hal_embassy::Executor::new());
|
let executor = EXECUTOR_CORE1.init(esp_hal_embassy::Executor::new());
|
||||||
executor.run(|spawner| {
|
executor.run(|spawner| {
|
||||||
spawner.spawn(core1_network_task(spawner, controller, wifi_interface, seed)).ok();
|
spawner
|
||||||
|
.spawn(core1_network_task(
|
||||||
|
spawner,
|
||||||
|
controller,
|
||||||
|
wifi_interface,
|
||||||
|
seed,
|
||||||
|
))
|
||||||
|
.ok();
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
).unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Wait for network to be ready (signaled from core 1)
|
// Wait for network to be ready (signaled from core 1)
|
||||||
NETWORK_READY.wait().await;
|
NETWORK_READY.wait().await;
|
||||||
@@ -131,63 +120,60 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
let config = InputConfig::default().with_pull(Pull::Down);
|
let config = InputConfig::default().with_pull(Pull::Down);
|
||||||
let button_select = Input::new(peripherals.GPIO32, config);
|
let button_select = Input::new(peripherals.GPIO32, config);
|
||||||
let button_next = Input::new(peripherals.GPIO35, config);
|
let button_next = Input::new(peripherals.GPIO35, config);
|
||||||
spawner.spawn(button_detection_task(button_select, button_next)).unwrap();
|
spawner
|
||||||
|
.spawn(button_detection_task(button_select, button_next))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Core 0: display and MPU tasks
|
|
||||||
spawner.spawn(display::task::display_task(display_i2c)).expect("spawn display_task");
|
|
||||||
spawner.spawn(mpu::task::mpu_task(mpu_i2c)).expect("spawn mpu_task");
|
|
||||||
|
|
||||||
display::api::set_status("Booting...").await;
|
|
||||||
mqtt_subscribe("esp32/read").await;
|
mqtt_subscribe("esp32/read").await;
|
||||||
mqtt_publish("esp32/imu", b"online", QualityOfService::QoS1, false).await;
|
mqtt_publish("esp32/imu", b"online", QualityOfService::QoS1, false).await;
|
||||||
|
|
||||||
display::api::set_status("Running").await;
|
|
||||||
display::api::set_mqtt_status(true, 0).await;
|
|
||||||
|
|
||||||
let mqtt_rx = mqtt_events();
|
let mqtt_rx = mqtt_events();
|
||||||
let imu_rx = mpu::api::events();
|
|
||||||
let mut imu_reading_count: u32 = 0;
|
|
||||||
let mut mqtt_msg_count: u32 = 0;
|
let mut mqtt_msg_count: u32 = 0;
|
||||||
let mut mqtt_publish_drops: u32 = 0;
|
let mut mqtt_publish_drops: u32 = 0;
|
||||||
let mut last_mqtt_publish = Instant::now();
|
let mut last_mqtt_publish = Instant::now();
|
||||||
let mqtt_publish_interval = Duration::from_secs(3);
|
let mqtt_publish_interval = Duration::from_secs(3);
|
||||||
|
|
||||||
loop {
|
// loop {
|
||||||
match select3(
|
// match select3(
|
||||||
mqtt_rx.receive(),
|
// mqtt_rx.receive(),
|
||||||
imu_rx.receive(),
|
// // imu_rx.receive(),
|
||||||
Timer::after(Duration::from_secs(5)),
|
// Timer::after(Duration::from_secs(5)),
|
||||||
).await {
|
// )
|
||||||
Either3::First(msg) => {
|
// .await
|
||||||
mqtt_msg_count += 1;
|
// {
|
||||||
handle_mqtt_message(msg).await;
|
// Either3::First(msg) => {
|
||||||
display::api::set_mqtt_status(true, mqtt_msg_count).await;
|
// mqtt_msg_count += 1;
|
||||||
}
|
// handle_mqtt_message(msg).await;
|
||||||
Either3::Second(mut reading) => {
|
// display::api::set_mqtt_status(true, mqtt_msg_count).await;
|
||||||
// Drain zabezpečuje, že 'reading' je najčerstvejšia možná hodnota
|
// }
|
||||||
let mut drained = 0;
|
// Either3::Second(mut reading) => {
|
||||||
while let Ok(next) = imu_rx.try_receive() {
|
// // Drain zabezpečuje, že 'reading' je najčerstvejšia možná hodnota
|
||||||
reading = next;
|
// let mut drained = 0;
|
||||||
drained += 1;
|
// while let Ok(next) = imu_rx.try_receive() {
|
||||||
}
|
// reading = next;
|
||||||
|
// drained += 1;
|
||||||
|
// }
|
||||||
|
|
||||||
imu_reading_count += 1;
|
// imu_reading_count += 1;
|
||||||
display::api::show_imu(reading);
|
// display::api::show_imu(reading);
|
||||||
|
|
||||||
// 3. Nahraďte pôvodnú podmienku týmto časovým zámkom
|
// // 3. Nahraďte pôvodnú podmienku týmto časovým zámkom
|
||||||
if last_mqtt_publish.elapsed() >= mqtt_publish_interval {
|
// if last_mqtt_publish.elapsed() >= mqtt_publish_interval {
|
||||||
let payload = client::encode_imu_json(&reading);
|
// let payload = client::encode_imu_json(&reading);
|
||||||
client::mqtt_set_imu_payload(payload);
|
// client::mqtt_set_imu_payload(payload);
|
||||||
last_mqtt_publish = Instant::now();
|
// last_mqtt_publish = Instant::now();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
Either3::Third(_) => {
|
// Either3::Third(_) => {
|
||||||
crate::mpu::api::IMU_CHANNEL.clear();
|
// crate::mpu::api::IMU_CHANNEL.clear();
|
||||||
info!("IMU heartbeat: force-cleared queue, {} readings total, {} mqtt drops",
|
// info!(
|
||||||
imu_reading_count, mqtt_publish_drops);
|
// "IMU heartbeat: force-cleared queue, {} readings total, {} mqtt drops",
|
||||||
}
|
// imu_reading_count, mqtt_publish_drops
|
||||||
}
|
// );
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs on core 1 - creates and owns the network stack
|
// Runs on core 1 - creates and owns the network stack
|
||||||
@@ -212,7 +198,9 @@ async fn core1_network_task(
|
|||||||
|
|
||||||
// Wait for network
|
// Wait for network
|
||||||
loop {
|
loop {
|
||||||
if stack.is_link_up() { break; }
|
if stack.is_link_up() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
@@ -232,9 +220,10 @@ async fn core1_network_task(
|
|||||||
async fn handle_mqtt_message(msg: IncomingMsg) {
|
async fn handle_mqtt_message(msg: IncomingMsg) {
|
||||||
if let Ok(txt) = core::str::from_utf8(&msg.payload) {
|
if let Ok(txt) = core::str::from_utf8(&msg.payload) {
|
||||||
match txt {
|
match txt {
|
||||||
"clear" => { display::api::clear().await; }
|
"status" => {
|
||||||
"status" => { mqtt_publish("esp32/status", b"running", QualityOfService::QoS1, false).await; }
|
mqtt_publish("esp32/status", b"running", QualityOfService::QoS1, false).await;
|
||||||
_ => { display::api::add_chat_message(txt).await; }
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,19 +263,21 @@ async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
|
|||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn button_detection_task(mut select_btn: Input<'static>, mut next_btn: Input<'static>) {
|
async fn button_detection_task(mut select_btn: Input<'static>, mut next_btn: Input<'static>) {
|
||||||
loop {
|
loop {
|
||||||
match select(
|
// match select(
|
||||||
select_btn.wait_for_rising_edge(),
|
// select_btn.wait_for_rising_edge(),
|
||||||
next_btn.wait_for_rising_edge(),
|
// next_btn.wait_for_rising_edge(),
|
||||||
).await {
|
// )
|
||||||
Either::First(_) => {
|
// .await
|
||||||
info!("Detection: GPIO 32 (Select) triggered!");
|
// {
|
||||||
display::api::push_key(Key::enter()).await;
|
// Either::First(_) => {
|
||||||
}
|
// info!("Detection: GPIO 32 (Select) triggered!");
|
||||||
Either::Second(_) => {
|
// display::api::push_key(Key::enter()).await;
|
||||||
info!("Detection: GPIO 35 (Next) triggered!");
|
// }
|
||||||
display::api::push_key(Key::tab()).await
|
// Either::Second(_) => {
|
||||||
}
|
// info!("Detection: GPIO 35 (Next) triggered!");
|
||||||
}
|
// display::api::push_key(Key::tab()).await
|
||||||
|
// }
|
||||||
|
// }
|
||||||
// Debounce: prevent mechanical bouncing from double-triggering
|
// Debounce: prevent mechanical bouncing from double-triggering
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
|
embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
// src/mqtt/client.rs
|
// src/mqtt/client.rs
|
||||||
|
|
||||||
use embassy_net::{tcp::TcpSocket, Stack};
|
|
||||||
use embassy_time::{Duration, Timer, Instant};
|
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_net::{tcp::TcpSocket, Stack};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use rust_mqtt::client::client::MqttClient;
|
use rust_mqtt::client::client::MqttClient;
|
||||||
use rust_mqtt::client::client_config::{ClientConfig, MqttVersion};
|
use rust_mqtt::client::client_config::{ClientConfig, MqttVersion};
|
||||||
use rust_mqtt::packet::v5::publish_packet::QualityOfService;
|
use rust_mqtt::packet::v5::publish_packet::QualityOfService;
|
||||||
use rust_mqtt::packet::v5::reason_codes::ReasonCode;
|
use rust_mqtt::packet::v5::reason_codes::ReasonCode;
|
||||||
use rust_mqtt::utils::rng_generator::CountingRng;
|
use rust_mqtt::utils::rng_generator::CountingRng;
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::mutex::Mutex;
|
|
||||||
use embassy_sync::channel::{Channel, Receiver};
|
use embassy_sync::channel::{Channel, Receiver};
|
||||||
|
use embassy_sync::mutex::Mutex;
|
||||||
use embassy_sync::signal::Signal;
|
use embassy_sync::signal::Signal;
|
||||||
use heapless::{String, Vec};
|
use heapless::{String, Vec};
|
||||||
use static_cell::ConstStaticCell;
|
|
||||||
use core::fmt::Write;
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use static_cell::ConstStaticCell;
|
||||||
|
|
||||||
use crate::mqtt::config::mqtt_broker_endpoint;
|
use crate::mqtt::config::mqtt_broker_endpoint;
|
||||||
use crate::contracts::ImuReading;
|
|
||||||
|
|
||||||
const RECONNECT_DELAY_SECS: u64 = 5;
|
const RECONNECT_DELAY_SECS: u64 = 5;
|
||||||
const KEEPALIVE_SECS: u64 = 60;
|
const KEEPALIVE_SECS: u64 = 60;
|
||||||
@@ -102,31 +101,6 @@ pub fn mqtt_try_publish(topic: &str, payload: &[u8], qos: QualityOfService, reta
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mqtt_set_imu(reading: ImuReading) {
|
|
||||||
// Encode JSON into a bounded buffer (no alloc::format!)
|
|
||||||
let payload = encode_imu_json(&reading);
|
|
||||||
IMU_SIG.signal(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mqtt_set_imu_payload(payload: Vec<u8, PAYLOAD_MAX>) {
|
|
||||||
IMU_SIG.signal(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode_imu_json(reading: &ImuReading) -> Vec<u8, PAYLOAD_MAX> {
|
|
||||||
let mut s: String<256> = String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut s,
|
|
||||||
"{{\"ax\":{:.2},\"ay\":{:.2},\"az\":{:.2},\"gx\":{:.1},\"gy\":{:.1},\"gz\":{:.1},\"t\":{:.1},\"ts\":{}}}",
|
|
||||||
reading.accel_g[0], reading.accel_g[1], reading.accel_g[2],
|
|
||||||
reading.gyro_dps[0], reading.gyro_dps[1], reading.gyro_dps[2],
|
|
||||||
reading.temp_c,
|
|
||||||
reading.timestamp_ms
|
|
||||||
);
|
|
||||||
let mut v: Vec<u8, PAYLOAD_MAX> = Vec::new();
|
|
||||||
let _ = v.extend_from_slice(s.as_bytes());
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn mqtt_subscribe(topic: &str) {
|
pub async fn mqtt_subscribe(topic: &str) {
|
||||||
let t = truncate_str::<TOPIC_MAX>(topic);
|
let t = truncate_str::<TOPIC_MAX>(topic);
|
||||||
{
|
{
|
||||||
@@ -139,8 +113,7 @@ pub async fn mqtt_subscribe(topic: &str) {
|
|||||||
CMD_CHAN.send(Command::Subscribe(t)).await;
|
CMD_CHAN.send(Command::Subscribe(t)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mqtt_events(
|
pub fn mqtt_events() -> Receiver<'static, CriticalSectionRawMutex, IncomingMsg, EVENT_QUEUE> {
|
||||||
) -> Receiver<'static, CriticalSectionRawMutex, IncomingMsg, EVENT_QUEUE> {
|
|
||||||
EVT_CHAN.receiver()
|
EVT_CHAN.receiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,8 +194,7 @@ async fn run_one_session(
|
|||||||
cfg.keep_alive = KEEPALIVE_SECS as u16;
|
cfg.keep_alive = KEEPALIVE_SECS as u16;
|
||||||
cfg.add_client_id("esp32-client");
|
cfg.add_client_id("esp32-client");
|
||||||
|
|
||||||
let mut client =
|
let mut client = MqttClient::new(socket, mqtt_tx, mqtt_tx.len(), mqtt_rx, mqtt_rx.len(), cfg);
|
||||||
MqttClient::new(socket, mqtt_tx, mqtt_tx.len(), mqtt_rx, mqtt_rx.len(), cfg);
|
|
||||||
|
|
||||||
match client.connect_to_broker().await {
|
match client.connect_to_broker().await {
|
||||||
Ok(_) => info!("MQTT CONNACK received"),
|
Ok(_) => info!("MQTT CONNACK received"),
|
||||||
@@ -284,7 +256,11 @@ async fn run_one_session(
|
|||||||
last_imu_sig = now;
|
last_imu_sig = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ping_in = if next_ping_at > now { next_ping_at - now } else { Duration::from_secs(0) };
|
let ping_in = if next_ping_at > now {
|
||||||
|
next_ping_at - now
|
||||||
|
} else {
|
||||||
|
Duration::from_secs(0)
|
||||||
|
};
|
||||||
|
|
||||||
// Timebox receive_message() even if lower layers misbehave.
|
// Timebox receive_message() even if lower layers misbehave.
|
||||||
let recv_fut = async {
|
let recv_fut = async {
|
||||||
|
|||||||
Reference in New Issue
Block a user