// main.rs
// --- Các import cần thiết ---
use anyhow::Result;
use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoFont, MonoTextStyle},
pixelcolor::Rgb565,
prelude::*,
primitives::{Circle, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle},
text::{self, Text, TextStyleBuilder},
};
use esp_idf_hal::{
delay::{Delay, FreeRtos},
gpio::{AnyOutputPin, OutputPin, PinDriver},
i2c,
peripherals::Peripherals,
prelude::*,
spi::{config::Config as SpiConfig, Dma, SpiDeviceDriver, SpiDriver, SpiDriverConfig},
task::block_on,
};
use esp_idf_svc::{
eventloop::EspSystemEventLoop,
http::server::EspHttpServer,
mqtt::client::{EspMqttClient, QoS},
nvs::{EspNvsPartition, NvsDefault},
};
use log::info;
use mipidsi::{interface::SpiInterface, models::ILI9341Rgb565, Builder};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::Display,
sync::{mpsc, Arc, Mutex},
thread::Builder as ThreadBuilder,
};
use tokio::sync::broadcast;
// --- Các module của dự án ---
mod connection;
mod display;
mod element;
mod mqtt;
mod server;
mod thingspeak;
mod wifi;
use crate::{
connection::{ConnectionManager, ConnectionState},
element::{Element, ElementKind, PayloadFormat},
mqtt::{MqttClient, MqttEventPayload},
server::start_server,
wifi::WifiManager,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DataEvent {
HttpLayout(Vec<Element>), // Chứa layout từ HTTP
MqttMessage(MqttEventPayload), // Sẵn sàng để chứa tin nhắn từ MQTT sau này
}
// main.rs
// Sửa lại tên cho đúng chuẩn và cập nhật kiểu dữ liệu
pub struct DataManager {
// Không cần state Arc<Mutex> nữa, vì event đã chứa đủ thông tin
pub tx: broadcast::Sender<DataEvent>,
}
impl DataManager {
pub fn new() -> Self {
let (tx, _) = broadcast::channel(16);
Self { tx }
}
// Gửi một DataEvent
pub fn send_event(
&self,
event: DataEvent,
) -> Result<(), broadcast::error::SendError<DataEvent>> {
self.tx.send(event)?;
Ok(())
}
pub fn subscribe(&self) -> broadcast::Receiver<DataEvent> {
self.tx.subscribe()
}
}
// Clone cho DataManager
impl Clone for DataManager {
fn clone(&self) -> Self {
Self {
tx: self.tx.clone(),
}
}
}
fn main() -> Result<()> {
// --- Khởi tạo cơ bản ---
esp_idf_sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();
info!("🚀 Bắt đầu ứng dụng...");
// --- Lấy tài nguyên hệ thống ---
let peripherals = Peripherals::take().unwrap();
let sysloop = EspSystemEventLoop::take()?;
let nvs = EspNvsPartition::<NvsDefault>::take()?;
let modem = peripherals.modem;
// --- Quản lý trạng thái và Wi-Fi ---
let connection_manager = ConnectionManager::new();
let mut wifi_manager = WifiManager::new(modem, sysloop.clone(), nvs.clone()).unwrap();
let data_manager = DataManager::new();
let mut state_receiver = connection_manager.subscribe(); // subscribe first
let mut event_receiver = data_manager.subscribe();
block_on(wifi_manager.start_ap_mode(connection_manager.clone()))?;
let wifi = Arc::new(Mutex::new(wifi_manager));
// Giữ cho server sống
let mut server: Option<EspHttpServer> = None;
// --- Chuẩn bị cho MQTT (sẽ được khởi tạo sau) ---
let mut mqtt_client: Option<EspMqttClient> = None;
let (tx, mut rx) = broadcast::channel::<Vec<Element>>(16); // Kênh giao tiếp từ luồng server -> luồng chính
let (tx1, rx1) = broadcast::channel::<MqttEventPayload>(16);
//let latest_layout = Arc::new(Mutex::new(None::<Vec<Element>>));
//
let parsing_rules = Arc::new(Mutex::new(HashMap::<String, PayloadFormat>::new()));
let sclk = peripherals.pins.gpio14;
let sdo = peripherals.pins.gpio13;
let sdi = peripherals.pins.gpio12;
let cs = peripherals.pins.gpio15;
let dc = peripherals.pins.gpio2;
let rst = peripherals.pins.gpio16;
let spi_bus = peripherals.spi2;
let rx_display = tx.subscribe();
let _display = display::Display::start(sclk, sdo, sdi, cs, dc, rst, spi_bus, rx_display, rx1);
let mut layout_receiver = tx.subscribe();
// --- Vòng lặp ứng dụng chính ---
info!("✅ Khởi tạo hoàn tất. Bắt đầu vòng lặp chính.");
loop {
// ---- LOGIC NHẬN LAYOUT VÀ SUBSCRIBE ----
// 1. Kiểm tra và xử lý thay đổi trạng thái kết nối
if let Ok(state) = state_receiver.try_recv() {
match state {
ConnectionState::ApConnected => {
info!("✅ Chế độ AP đã sẵn sàng. Khởi động Web Server...");
if server.is_none() {
let tx = tx.clone();
match start_server(
wifi.clone(),
connection_manager.clone(),
data_manager.clone(),
tx,
) {
Ok(http_server) => {
server = Some(http_server);
info!("✅ Web Server đã khởi động và đang chạy.");
}
Err(e) => {
info!("❌ Lỗi khởi động server: {:?}", e);
}
}
}
}
ConnectionState::WifiConnected => {
info!("✅ Đã kết nối vào Wi-Fi. Bắt đầu khởi tạo MQTT client...");
let topics = vec!["topic1".to_string(), "topic2".to_string()];
let my_mqtt = MqttClient::new(topics);
match MqttClient::init_mqtt_client(&connection_manager) {
Ok((client, connection)) => {
mqtt_client = Some(client);
let tx_clone = tx1.clone();
let cm_clone = connection_manager.clone();
let mqtt_parsing_rules = parsing_rules.clone();
ThreadBuilder::new()
.name("mqtt-thread".into())
.stack_size(8192)
.spawn(move || {
MqttClient::mqtt_event_loop(
connection,
tx_clone,
cm_clone,
mqtt_parsing_rules,
);
})
.unwrap();
info!("✅ Luồng sự kiện MQTT đã bắt đầu.");
}
Err(e) => info!("❌ Lỗi khởi tạo MQTT client: {:?}", e),
}
}
ConnectionState::MqttConnected => {
info!("✅ Đã kết nối tới MQTT Broker!");
//let mut rx = tx.subscribe();
//let layout_for_subscribe = latest_layout.clone();
//
//if let Some(client) = mqtt_client.as_mut() {
// if let Some(elements) = &*layout_for_subscribe.lock().unwrap() {
// for element in elements {
// if let ElementKind::Value { source_topic } = &element.kind {
// if let Err(e) = client.subscribe(source_topic, QoS::AtMostOnce)
// {
// info!("❌ Lỗi subscribe MQTT: {:?}", e);
// } else {
// info!("✅ Đã subscribe vào topic '{}'", &source_topic);
// }
// }
// }
// }
//}
}
_ => {} // Bỏ qua các trạng thái khác
}
}
if let Ok(event) = event_receiver.try_recv() {
match event {
// Xử lý sự kiện nhận layout từ HTTP
DataEvent::HttpLayout(layout) => {
info!("📬 [Main Thread] Nhận được HttpLayout, đang xử lý...");
// 1. Gửi cho Display vẽ lại
// Chúng ta cần một kênh riêng cho display hoặc sửa lại display
// Tạm thời, chúng ta sẽ dùng kênh tx cũ cho việc này
if let Err(e) = tx.send(layout.clone()) {
log::error!("[Main Thread] Lỗi gửi layout đến display: {}", e);
}
let mut new_hashmap = HashMap::new();
for element in &layout {
if let ElementKind::Value {
source_topic,
format,
} = &element.kind
{
new_hashmap.insert(source_topic.to_string(), format.clone());
}
}
*parsing_rules.lock().unwrap() = new_hashmap;
// 2. Thực hiện subscribe
if let Some(client) = mqtt_client.as_mut() {
info!("🔍 MQTT đã kết nối. Bắt đầu subscribe...");
for element in layout {
if let ElementKind::Value { source_topic, .. } = element.kind {
match client.subscribe(&source_topic, QoS::AtMostOnce) {
Ok(_) => info!("✅ Subscribed to '{}'", source_topic),
Err(e) => info!(
"❌ Failed to subscribe to '{}': {:?}",
source_topic, e
),
}
}
}
}
}
// Xử lý sự kiện nhận tin nhắn từ MQTT
DataEvent::MqttMessage(payload) => {
info!("📥 [Main Thread] Nhận được MqttMessage: {:?}", payload);
// Gửi payload này cho luồng display để cập nhật giá trị
if let Err(e) = tx1.send(payload) {
log::error!("[Main Thread] Lỗi gửi payload đến display: {}", e);
}
}
}
}
// 2. Kiểm tra và xử lý tin nhắn từ MQTT
//if let Ok(message) = rx1.try_recv() {
// info!("[Main Thread] Nhận được tin nhắn MQTT: {:?}", message);
// // TODO: Xử lý tin nhắn nhận được (ví dụ: hiển thị lên màn hình)
//}
// Tạm nghỉ để nhường CPU cho các tác vụ khác
FreeRtos::delay_ms(100);
}
}
Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1