Compare commits
7 Commits
7c5186ed49
...
871c9bddd1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
871c9bddd1 | ||
|
|
a3954ea3e8 | ||
|
|
948602bd1e | ||
|
|
cfcdb56fe8 | ||
|
|
99b1bf6c3d | ||
|
|
980fb67f85 | ||
|
|
0eab3bbaba |
@@ -7,6 +7,7 @@ ESP_LOG="info"
|
||||
[build]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-nostartfiles",
|
||||
"-C", "link-arg=-Tdefmt.x",
|
||||
"-Z", "stack-protector=all",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
SSID = "nazov_wifi_siete"
|
||||
PASSWORD = "heslo_od_wifi"
|
||||
BROKER_IP= "5.196.78.28"
|
||||
BROKER_PORT= "1883"
|
||||
|
||||
59
final/Cargo.lock
generated
59
final/Cargo.lock
generated
@@ -205,6 +205,16 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-rtt"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2cac3b8a5644a9e02b75085ebad3b6deafdbdbdec04bb25086523828aa4dfd1"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"defmt 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.13.4"
|
||||
@@ -1167,6 +1177,31 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "projekt_final"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"defmt-rtt",
|
||||
"dotenvy",
|
||||
"embassy-executor",
|
||||
"embassy-net",
|
||||
"embassy-time 0.5.0",
|
||||
"embedded-io",
|
||||
"embedded-io-async",
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-bootloader-esp-idf",
|
||||
"esp-hal",
|
||||
"esp-hal-embassy",
|
||||
"esp-println",
|
||||
"esp-wifi",
|
||||
"log",
|
||||
"rust-mqtt",
|
||||
"smoltcp",
|
||||
"static_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
@@ -1379,30 +1414,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"dotenvy",
|
||||
"embassy-executor",
|
||||
"embassy-net",
|
||||
"embassy-time 0.5.0",
|
||||
"embedded-io",
|
||||
"embedded-io-async",
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-bootloader-esp-idf",
|
||||
"esp-hal",
|
||||
"esp-hal-embassy",
|
||||
"esp-println",
|
||||
"esp-wifi",
|
||||
"log",
|
||||
"rust-mqtt",
|
||||
"smoltcp",
|
||||
"static_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "test1"
|
||||
name = "projekt_final"
|
||||
rust-version = "1.86"
|
||||
version = "0.1.0"
|
||||
|
||||
[[bin]]
|
||||
name = "test1"
|
||||
name = "projekt_final"
|
||||
path = "./src/bin/main.rs"
|
||||
|
||||
[dependencies]
|
||||
@@ -19,6 +19,7 @@ log = "0.4.27"
|
||||
|
||||
embassy-net = { version = "0.7.0", features = [
|
||||
"dhcpv4",
|
||||
"proto-ipv6",
|
||||
"log",
|
||||
"medium-ethernet",
|
||||
"tcp",
|
||||
@@ -57,6 +58,7 @@ smoltcp = { version = "0.12.0", default-features = false, features = [
|
||||
"proto-dhcpv4",
|
||||
"proto-dns",
|
||||
"proto-ipv4",
|
||||
"proto-ipv6",
|
||||
"socket-dns",
|
||||
"socket-icmp",
|
||||
"socket-raw",
|
||||
@@ -65,6 +67,7 @@ smoltcp = { version = "0.12.0", default-features = false, features = [
|
||||
] }
|
||||
static_cell = "2.1.1"
|
||||
rust-mqtt = { version = "0.3.0", default-features = false, features = ["no_std"] }
|
||||
defmt-rtt = "1.0.0"
|
||||
|
||||
[build-dependencies]
|
||||
dotenvy = "0.15.7"
|
||||
|
||||
@@ -5,6 +5,7 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed={}", dotenv_path.display());
|
||||
}
|
||||
|
||||
// Pass WIFI credentials into firmware
|
||||
if let Ok(ssid) = std::env::var("SSID") {
|
||||
println!("cargo:rustc-env=SSID={}", ssid);
|
||||
}
|
||||
@@ -12,8 +13,16 @@ fn main() {
|
||||
println!("cargo:rustc-env=PASSWORD={}", password);
|
||||
}
|
||||
|
||||
// Export BROKER_IP and PORT as string envs also (optional, for debugging)
|
||||
if let Ok(ip) = std::env::var("BROKER_IP") {
|
||||
println!("cargo:rustc-env=BROKER_IP={}", ip);
|
||||
}
|
||||
if let Ok(port) = std::env::var("BROKER_PORT") {
|
||||
println!("cargo:rustc-env=BROKER_PORT={}", port);
|
||||
}
|
||||
|
||||
linker_be_nice();
|
||||
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
|
||||
// make sure linkall.x is the last linker script
|
||||
println!("cargo:rustc-link-arg=-Tlinkall.x");
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ use esp_wifi::{
|
||||
wifi::{ClientConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiState},
|
||||
};
|
||||
use log::info;
|
||||
mod mqtt;
|
||||
use projekt_final::mqtt::client::mqtt_task;
|
||||
use defmt_rtt as _;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@@ -90,8 +91,8 @@ async fn main(spawner: Spawner) -> ! {
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
// Stack is ready - Add rust-mqtt task here
|
||||
// spawner.spawn(mqtt_task(stack)).ok();
|
||||
spawner.spawn(mqtt_task(stack)).expect("failed to spawn MQTT task");
|
||||
info!("MQTT task started");
|
||||
|
||||
loop {
|
||||
Timer::after(Duration::from_secs(60)).await;
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod mqtt;
|
||||
|
||||
@@ -1,53 +1,83 @@
|
||||
use embassy_time::{Duration, Timer};
|
||||
// src/mqtt/client.rs
|
||||
use embassy_net::{tcp::TcpSocket, Stack};
|
||||
use esp_wifi::wifi::WifiDevice;
|
||||
use rust_mqtt::client::client::MqttClient;
|
||||
use rust_mqtt::packet::v5::{connect::ConnectPacket, publish::PublishPacket};
|
||||
use rust_mqtt::utils::rng_generator::CountingRng32;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use log::info;
|
||||
use rust_mqtt::client::client::MqttClient;
|
||||
use rust_mqtt::client::client_config::{ClientConfig, MqttVersion};
|
||||
use rust_mqtt::packet::v5::publish_packet::QualityOfService;
|
||||
use rust_mqtt::utils::rng_generator::CountingRng;
|
||||
use static_cell::ConstStaticCell;
|
||||
|
||||
use crate::mqtt::config::mqtt_broker_endpoint;
|
||||
|
||||
// TCP socket buffers (for embassy-net TcpSocket)
|
||||
static TCP_RX_BUFFER: ConstStaticCell<[u8; 2048]> = ConstStaticCell::new([0; 2048]);
|
||||
static TCP_TX_BUFFER: ConstStaticCell<[u8; 2048]> = ConstStaticCell::new([0; 2048]);
|
||||
|
||||
// MQTT client buffers (separate from the TcpSocket’s buffers)
|
||||
static MQTT_TX_BUF: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0; 1024]);
|
||||
static MQTT_RX_BUF: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0; 1024]);
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn mqtt_task(stack: &'static Stack<WifiDevice<'static>>) {
|
||||
pub async fn mqtt_task(stack: Stack<'static>) {
|
||||
info!("MQTT task starting...");
|
||||
|
||||
static mut RX_BUFFER: [u8; 2048] = [0; 2048];
|
||||
static mut TX_BUFFER: [u8; 2048] = [0; 2048];
|
||||
let tcp_rx = TCP_RX_BUFFER.take();
|
||||
let tcp_tx = TCP_TX_BUFFER.take();
|
||||
let mut socket = TcpSocket::new(stack, tcp_rx, tcp_tx);
|
||||
|
||||
let mut socket = TcpSocket::new(stack, unsafe { &mut RX_BUFFER }, unsafe { &mut TX_BUFFER });
|
||||
|
||||
// TODO: replace with your broker IP
|
||||
use embassy_net::Ipv4Address;
|
||||
let broker_ip = Ipv4Address::new(192, 168, 1, 100);
|
||||
let broker_port = 1883;
|
||||
|
||||
if let Err(e) = socket.connect((broker_ip, broker_port)).await {
|
||||
info!("TCP connect failed: {:?}", e);
|
||||
return;
|
||||
}
|
||||
info!("Connected to MQTT broker");
|
||||
|
||||
let rng = CountingRng32::new();
|
||||
let mut client: MqttClient<_, _, 1024, 1024> = MqttClient::new(socket, rng);
|
||||
|
||||
let connect = ConnectPacket::new("esp32-client");
|
||||
if client.connect(connect).await.is_ok() {
|
||||
info!("MQTT CONNECT sent");
|
||||
}
|
||||
|
||||
let publish = PublishPacket::new("esp32/topic", b"hello from esp32", 0);
|
||||
if client.publish(publish).await.is_ok() {
|
||||
info!("MQTT PUBLISH sent");
|
||||
}
|
||||
|
||||
// Keep polling
|
||||
loop {
|
||||
match client.poll().await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
info!("MQTT error: {:?}", e);
|
||||
break;
|
||||
}
|
||||
match socket.connect(mqtt_broker_endpoint()).await {
|
||||
Ok(_) => info!("Connected TCP to MQTT broker"),
|
||||
Err(e) => {
|
||||
info!("TCP connect failed: {:?}", e);
|
||||
return;
|
||||
}
|
||||
Timer::after(Duration::from_secs(5)).await;
|
||||
}
|
||||
|
||||
let mqtt_tx = MQTT_TX_BUF.take();
|
||||
let mqtt_rx = MQTT_RX_BUF.take();
|
||||
|
||||
// Config
|
||||
let rng = CountingRng(0);
|
||||
let mut cfg: ClientConfig<'static, 8, _> = ClientConfig::new(MqttVersion::MQTTv5, rng);
|
||||
cfg.keep_alive = 60;
|
||||
cfg.add_client_id("esp32-client");
|
||||
// cfg.add_username("user");
|
||||
// cfg.add_password("pass");
|
||||
|
||||
let mqtt_tx_len = mqtt_tx.len();
|
||||
let mqtt_rx_len = mqtt_rx.len();
|
||||
|
||||
let mut client = MqttClient::new(socket, mqtt_tx, mqtt_tx_len, mqtt_rx, mqtt_rx_len, cfg);
|
||||
|
||||
// Connect
|
||||
match client.connect_to_broker().await {
|
||||
Ok(_) => info!("MQTT CONNACK received"),
|
||||
Err(reason) => {
|
||||
info!("MQTT connect failed: {:?}", reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Publish simple message
|
||||
match client
|
||||
.send_message(
|
||||
"esp32/topic",
|
||||
b"hello from esp32",
|
||||
QualityOfService::QoS1,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => info!("MQTT PUBLISH sent"),
|
||||
Err(reason) => info!("MQTT publish failed: {:?}", reason),
|
||||
}
|
||||
|
||||
loop {
|
||||
if let Err(reason) = client.send_ping().await {
|
||||
info!("MQTT ping failed: {:?}", reason);
|
||||
break;
|
||||
}
|
||||
Timer::after(Duration::from_secs(30)).await;
|
||||
}
|
||||
}
|
||||
|
||||
122
final/src/mqtt/config.rs
Normal file
122
final/src/mqtt/config.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
// src/mqtt/config.rs
|
||||
#![allow(dead_code)]
|
||||
|
||||
use embassy_net::{IpAddress, Ipv4Address, Ipv6Address};
|
||||
|
||||
// Compile-time values injected by build.rs
|
||||
const BROKER_IP: &str = env!("BROKER_IP");
|
||||
const BROKER_PORT: &str = env!("BROKER_PORT");
|
||||
|
||||
pub fn mqtt_broker_endpoint() -> (IpAddress, u16) {
|
||||
(parse_ip(BROKER_IP), parse_port(BROKER_PORT))
|
||||
}
|
||||
|
||||
fn parse_port(s: &str) -> u16 {
|
||||
let p: u16 = s
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("BROKER_PORT must be a valid u16 (1..=65535)"));
|
||||
assert!(p != 0, "BROKER_PORT cannot be 0");
|
||||
p
|
||||
}
|
||||
|
||||
fn parse_ip(s: &str) -> IpAddress {
|
||||
if s.contains(':') {
|
||||
IpAddress::Ipv6(parse_ipv6(s))
|
||||
} else {
|
||||
IpAddress::Ipv4(parse_ipv4(s))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ipv4(s: &str) -> Ipv4Address {
|
||||
let mut it = s.split('.');
|
||||
let a = parse_octet(it.next(), 1);
|
||||
let b = parse_octet(it.next(), 2);
|
||||
let c = parse_octet(it.next(), 3);
|
||||
let d = parse_octet(it.next(), 4);
|
||||
assert!(it.next().is_none(), "Too many IPv4 octets");
|
||||
Ipv4Address::new(a, b, c, d)
|
||||
}
|
||||
|
||||
fn parse_octet(part: Option<&str>, idx: usize) -> u8 {
|
||||
let p = part.unwrap_or_else(|| panic!("IPv4 missing octet {}", idx));
|
||||
let v: u16 = p
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("Invalid IPv4 octet {}: {}", idx, p));
|
||||
assert!(v <= 255, "IPv4 octet {} out of range: {}", idx, v);
|
||||
v as u8
|
||||
}
|
||||
|
||||
// Minimal IPv6 parser with '::' compression. Does not handle IPv4-embedded IPv6.
|
||||
fn parse_ipv6(s: &str) -> Ipv6Address {
|
||||
assert!(
|
||||
!s.contains('.'),
|
||||
"IPv4-embedded IPv6 like ::ffff:192.0.2.1 not supported; \
|
||||
use pure hex IPv6"
|
||||
);
|
||||
|
||||
let has_double = s.contains("::");
|
||||
let (left_s, right_s) = if has_double {
|
||||
let mut sp = s.splitn(2, "::");
|
||||
(sp.next().unwrap_or(""), sp.next().unwrap_or(""))
|
||||
} else {
|
||||
(s, "")
|
||||
};
|
||||
|
||||
let mut left = [0u16; 8];
|
||||
let mut right = [0u16; 8];
|
||||
let mut ll = 0usize;
|
||||
let mut rl = 0usize;
|
||||
|
||||
if !left_s.is_empty() {
|
||||
for part in left_s.split(':') {
|
||||
left[ll] = parse_group(part);
|
||||
ll += 1;
|
||||
assert!(ll <= 8, "Too many IPv6 groups on the left");
|
||||
}
|
||||
}
|
||||
|
||||
if !right_s.is_empty() {
|
||||
for part in right_s.split(':') {
|
||||
right[rl] = parse_group(part);
|
||||
rl += 1;
|
||||
assert!(rl <= 8, "Too many IPv6 groups on the right");
|
||||
}
|
||||
}
|
||||
|
||||
let zeros = if has_double {
|
||||
assert!(ll + rl < 8, "Invalid IPv6 '::' usage");
|
||||
8 - (ll + rl)
|
||||
} else {
|
||||
assert!(ll == 8, "IPv6 must have 8 groups without '::'");
|
||||
0
|
||||
};
|
||||
|
||||
let mut g = [0u16; 8];
|
||||
let mut idx = 0usize;
|
||||
|
||||
for i in 0..ll {
|
||||
g[idx] = left[i];
|
||||
idx += 1;
|
||||
}
|
||||
for _ in 0..zeros {
|
||||
g[idx] = 0;
|
||||
idx += 1;
|
||||
}
|
||||
for i in 0..rl {
|
||||
g[idx] = right[i];
|
||||
idx += 1;
|
||||
}
|
||||
assert!(idx == 8, "IPv6 did not resolve to 8 groups");
|
||||
|
||||
Ipv6Address::new(g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7])
|
||||
}
|
||||
|
||||
fn parse_group(part: &str) -> u16 {
|
||||
assert!(
|
||||
!part.is_empty(),
|
||||
"Empty IPv6 group (use '::' instead for compression)"
|
||||
);
|
||||
assert!(part.len() <= 4, "IPv6 group too long: {}", part);
|
||||
u16::from_str_radix(part, 16)
|
||||
.unwrap_or_else(|_| panic!("Invalid IPv6 hex group: {}", part))
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
// src/mqtt/mod.rs
|
||||
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
|
||||
Reference in New Issue
Block a user