ipv4 or ipv6 parsed from .env file
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/mqtt/client.rs
|
||||
use embassy_net::{tcp::TcpSocket, Ipv4Address, Stack};
|
||||
use embassy_net::{tcp::TcpSocket, Stack};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use log::info;
|
||||
use rust_mqtt::client::client::MqttClient;
|
||||
@@ -8,6 +8,8 @@ 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]);
|
||||
@@ -20,18 +22,11 @@ static MQTT_RX_BUF: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0; 1024]
|
||||
pub async fn mqtt_task(stack: Stack<'static>) {
|
||||
info!("MQTT task starting...");
|
||||
|
||||
// Safely take buffers (only allowed once)
|
||||
let tcp_rx = TCP_RX_BUFFER.take();
|
||||
let tcp_tx = TCP_TX_BUFFER.take();
|
||||
|
||||
// Pass Stack by value to embassy-net TcpSocket
|
||||
let mut socket = TcpSocket::new(stack, tcp_rx, tcp_tx);
|
||||
|
||||
// TODO broker details
|
||||
let broker_ip = Ipv4Address::new(192, 168, 1, 100);
|
||||
let broker_port = 1883;
|
||||
|
||||
match socket.connect((broker_ip, broker_port)).await {
|
||||
match socket.connect(mqtt_broker_endpoint()).await {
|
||||
Ok(_) => info!("Connected TCP to MQTT broker"),
|
||||
Err(e) => {
|
||||
info!("TCP connect failed: {:?}", e);
|
||||
@@ -39,24 +34,20 @@ pub async fn mqtt_task(stack: Stack<'static>) {
|
||||
}
|
||||
}
|
||||
|
||||
// Safely take MQTT buffers
|
||||
let mqtt_tx = MQTT_TX_BUF.take();
|
||||
let mqtt_rx = MQTT_RX_BUF.take();
|
||||
|
||||
let mqtt_tx_len = mqtt_tx.len();
|
||||
let mqtt_rx_len = mqtt_rx.len();
|
||||
|
||||
// RNG
|
||||
// Config
|
||||
let rng = CountingRng(0);
|
||||
|
||||
// Build config
|
||||
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");
|
||||
|
||||
// Build MQTT client
|
||||
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
|
||||
@@ -82,7 +73,6 @@ pub async fn mqtt_task(stack: Stack<'static>) {
|
||||
Err(reason) => info!("MQTT publish failed: {:?}", reason),
|
||||
}
|
||||
|
||||
// Keep alive
|
||||
loop {
|
||||
if let Err(reason) = client.send_ping().await {
|
||||
info!("MQTT ping failed: {:?}", reason);
|
||||
|
||||
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