ipv4 or ipv6 parsed from .env file

This commit is contained in:
Priec
2025-10-04 22:03:45 +02:00
parent cfcdb56fe8
commit 948602bd1e
5 changed files with 144 additions and 19 deletions

View File

@@ -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
View 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))
}

View File

@@ -1,2 +1,4 @@
// src/mqtt/mod.rs
pub mod client;
pub mod config;