aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-12-04 13:06:27 +0000
committerdiogo464 <[email protected]>2025-12-04 13:06:27 +0000
commit945f38391052773c6b16e54006a434ff7c9f5d98 (patch)
tree1f3360316f7304e26a788582bd488726bd8259d2
parentb3e47f9c9268e01533a809f83b4f3ecd379c4b22 (diff)
added more examples
-rw-r--r--.envrc.template1
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock98
-rw-r--r--Cargo.toml1
-rw-r--r--examples/button.rs38
-rw-r--r--examples/common/mod.rs18
-rw-r--r--examples/common/std_async_tcp.rs125
-rw-r--r--examples/constant-temperature.rs136
-rw-r--r--examples/random-temperature.rs43
9 files changed, 329 insertions, 132 deletions
diff --git a/.envrc.template b/.envrc.template
new file mode 100644
index 0000000..f6a7db7
--- /dev/null
+++ b/.envrc.template
@@ -0,0 +1 @@
export MQTT_ADDRESS=""
diff --git a/.gitignore b/.gitignore
index f2564bc..786bc7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
1.DS_Store 1.DS_Store
2.envrc
2target/ 3target/
diff --git a/Cargo.lock b/Cargo.lock
index 61ede9e..5bc99f3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -161,6 +161,7 @@ dependencies = [
161 "embedded-io-async", 161 "embedded-io-async",
162 "embedded-mqtt", 162 "embedded-mqtt",
163 "heapless 0.9.2", 163 "heapless 0.9.2",
164 "rand",
164 "serde", 165 "serde",
165 "serde-json-core", 166 "serde-json-core",
166 "static_cell", 167 "static_cell",
@@ -331,6 +332,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
331checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 332checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
332 333
333[[package]] 334[[package]]
335name = "getrandom"
336version = "0.3.4"
337source = "registry+https://github.com/rust-lang/crates.io-index"
338checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
339dependencies = [
340 "cfg-if",
341 "libc",
342 "r-efi",
343 "wasip2",
344]
345
346[[package]]
334name = "hash32" 347name = "hash32"
335version = "0.3.1" 348version = "0.3.1"
336source = "registry+https://github.com/rust-lang/crates.io-index" 349source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -368,6 +381,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
368checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 381checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
369 382
370[[package]] 383[[package]]
384name = "libc"
385version = "0.2.178"
386source = "registry+https://github.com/rust-lang/crates.io-index"
387checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
388
389[[package]]
371name = "litrs" 390name = "litrs"
372version = "1.0.0" 391version = "1.0.0"
373source = "registry+https://github.com/rust-lang/crates.io-index" 392source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -401,6 +420,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
401checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 420checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
402 421
403[[package]] 422[[package]]
423name = "ppv-lite86"
424version = "0.2.21"
425source = "registry+https://github.com/rust-lang/crates.io-index"
426checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
427dependencies = [
428 "zerocopy",
429]
430
431[[package]]
404name = "proc-macro-error-attr2" 432name = "proc-macro-error-attr2"
405version = "2.0.0" 433version = "2.0.0"
406source = "registry+https://github.com/rust-lang/crates.io-index" 434source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -441,6 +469,41 @@ dependencies = [
441] 469]
442 470
443[[package]] 471[[package]]
472name = "r-efi"
473version = "5.3.0"
474source = "registry+https://github.com/rust-lang/crates.io-index"
475checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
476
477[[package]]
478name = "rand"
479version = "0.9.2"
480source = "registry+https://github.com/rust-lang/crates.io-index"
481checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
482dependencies = [
483 "rand_chacha",
484 "rand_core",
485]
486
487[[package]]
488name = "rand_chacha"
489version = "0.9.0"
490source = "registry+https://github.com/rust-lang/crates.io-index"
491checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
492dependencies = [
493 "ppv-lite86",
494 "rand_core",
495]
496
497[[package]]
498name = "rand_core"
499version = "0.9.3"
500source = "registry+https://github.com/rust-lang/crates.io-index"
501checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
502dependencies = [
503 "getrandom",
504]
505
506[[package]]
444name = "ryu" 507name = "ryu"
445version = "1.0.20" 508version = "1.0.20"
446source = "registry+https://github.com/rust-lang/crates.io-index" 509source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -564,3 +627,38 @@ name = "void"
564version = "1.0.2" 627version = "1.0.2"
565source = "registry+https://github.com/rust-lang/crates.io-index" 628source = "registry+https://github.com/rust-lang/crates.io-index"
566checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 629checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
630
631[[package]]
632name = "wasip2"
633version = "1.0.1+wasi-0.2.4"
634source = "registry+https://github.com/rust-lang/crates.io-index"
635checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
636dependencies = [
637 "wit-bindgen",
638]
639
640[[package]]
641name = "wit-bindgen"
642version = "0.46.0"
643source = "registry+https://github.com/rust-lang/crates.io-index"
644checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
645
646[[package]]
647name = "zerocopy"
648version = "0.8.31"
649source = "registry+https://github.com/rust-lang/crates.io-index"
650checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
651dependencies = [
652 "zerocopy-derive",
653]
654
655[[package]]
656name = "zerocopy-derive"
657version = "0.8.31"
658source = "registry+https://github.com/rust-lang/crates.io-index"
659checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
660dependencies = [
661 "proc-macro2",
662 "quote",
663 "syn",
664]
diff --git a/Cargo.toml b/Cargo.toml
index d37e957..6a9ea4f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,3 +22,4 @@ embassy-sync = { version = "0.7.2" }
22static_cell = "2.1.1" 22static_cell = "2.1.1"
23embedded-io-async = { version = "0.6", features = ["std"] } 23embedded-io-async = { version = "0.6", features = ["std"] }
24critical-section = { version = "1", features = ["std"] } 24critical-section = { version = "1", features = ["std"] }
25rand = "0.9.2"
diff --git a/examples/button.rs b/examples/button.rs
new file mode 100644
index 0000000..cfef3e3
--- /dev/null
+++ b/examples/button.rs
@@ -0,0 +1,38 @@
1mod common;
2
3use common::AsyncTcp;
4use embassy_executor::{Executor, Spawner};
5use static_cell::StaticCell;
6
7static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new();
8
9#[embassy_executor::task]
10async fn main_task(spawner: Spawner) {
11 let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS"));
12
13 let mut device = embassy_ha::Device::new(
14 RESOURCES.init(Default::default()),
15 embassy_ha::DeviceConfig {
16 device_id: "example-device-id",
17 device_name: "Example Device Name",
18 manufacturer: "Example Device Manufacturer",
19 model: "Example Device Model",
20 },
21 );
22
23 let button = device.create_button("button-sensor-id", "Button Name");
24
25 spawner.must_spawn(button_task(button));
26
27 device.run(&mut stream).await;
28}
29
30#[embassy_executor::task]
31async fn button_task(mut button: embassy_ha::Button<'static>) {
32 loop {
33 button.pressed().await;
34 println!("The button has been pressed");
35 }
36}
37
38example_main!();
diff --git a/examples/common/mod.rs b/examples/common/mod.rs
new file mode 100644
index 0000000..fd61e9d
--- /dev/null
+++ b/examples/common/mod.rs
@@ -0,0 +1,18 @@
1mod std_async_tcp;
2use embassy_executor::Executor;
3use static_cell::StaticCell;
4pub use std_async_tcp::AsyncTcp;
5
6pub static EXECUTOR: StaticCell<Executor> = StaticCell::new();
7
8#[macro_export]
9macro_rules! example_main {
10 () => {
11 fn main() {
12 let executor = common::EXECUTOR.init(Executor::new());
13 executor.run(|spawner| {
14 spawner.must_spawn(main_task(spawner));
15 });
16 }
17 };
18}
diff --git a/examples/common/std_async_tcp.rs b/examples/common/std_async_tcp.rs
new file mode 100644
index 0000000..bd97fa9
--- /dev/null
+++ b/examples/common/std_async_tcp.rs
@@ -0,0 +1,125 @@
1use std::{
2 io::{Read, Write},
3 net::{TcpStream, ToSocketAddrs},
4 sync::{Arc, Mutex},
5 thread::JoinHandle,
6};
7
8use embassy_sync::waitqueue::AtomicWaker;
9
10pub struct AsyncTcp {
11 write_handle: JoinHandle<()>,
12 write_buffer: Arc<Mutex<Vec<u8>>>,
13 read_buffer: Arc<Mutex<Vec<u8>>>,
14 waker: Arc<AtomicWaker>,
15}
16
17impl AsyncTcp {
18 pub fn connect(addr: impl ToSocketAddrs) -> Self {
19 let stream = TcpStream::connect(addr).expect("failed to connect to remote");
20 let mut read_stream = stream.try_clone().unwrap();
21 let mut write_stream = stream;
22
23 let read_buffer: Arc<Mutex<Vec<u8>>> = Default::default();
24 let write_buffer: Arc<Mutex<Vec<u8>>> = Default::default();
25
26 let waker = Arc::new(AtomicWaker::new());
27
28 let write_handle = std::thread::spawn({
29 let write_buffer = write_buffer.clone();
30 move || {
31 loop {
32 let buffer = {
33 let mut buffer = write_buffer.lock().unwrap();
34 std::mem::take(&mut *buffer)
35 };
36 if !buffer.is_empty() {
37 println!("writing {} bytes", buffer.len());
38 write_stream.write_all(&buffer).unwrap();
39 write_stream.flush().unwrap();
40 } else {
41 std::thread::park();
42 }
43 }
44 }
45 });
46
47 std::thread::spawn({
48 let read_buffer = read_buffer.clone();
49 let waker = waker.clone();
50 move || {
51 let mut scratch = [0u8; 1024];
52 loop {
53 let n = read_stream.read(&mut scratch).unwrap();
54 if n == 0 {
55 panic!("EOF");
56 }
57
58 {
59 let mut buffer = read_buffer.lock().unwrap();
60 buffer.extend_from_slice(&scratch[..n]);
61 waker.wake();
62 }
63 }
64 }
65 });
66
67 Self {
68 write_handle,
69 write_buffer,
70 read_buffer,
71 waker,
72 }
73 }
74}
75
76impl embedded_io_async::ErrorType for AsyncTcp {
77 type Error = std::io::Error;
78}
79
80impl embedded_io_async::Write for AsyncTcp {
81 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
82 {
83 let mut buffer = self.write_buffer.lock().unwrap();
84 buffer.extend_from_slice(buf);
85 }
86 self.write_handle.thread().unpark();
87 Ok(buf.len())
88 }
89}
90
91impl embedded_io_async::Read for AsyncTcp {
92 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
93 struct WaitForWaker<'a>(&'a AtomicWaker, bool);
94
95 impl<'a> Future for WaitForWaker<'a> {
96 type Output = ();
97
98 fn poll(
99 mut self: std::pin::Pin<&mut Self>,
100 cx: &mut std::task::Context<'_>,
101 ) -> std::task::Poll<Self::Output> {
102 if self.1 {
103 std::task::Poll::Ready(())
104 } else {
105 self.as_mut().1 = true;
106 self.0.register(cx.waker());
107 std::task::Poll::Pending
108 }
109 }
110 }
111
112 loop {
113 {
114 let mut buffer = self.read_buffer.lock().unwrap();
115 if !buffer.is_empty() {
116 let copy_n = buf.len().min(buffer.len());
117 buf[..copy_n].copy_from_slice(&buffer[..copy_n]);
118 buffer.drain(..copy_n);
119 return Ok(copy_n);
120 }
121 }
122 WaitForWaker(&self.waker, false).await
123 }
124 }
125}
diff --git a/examples/constant-temperature.rs b/examples/constant-temperature.rs
index 27e1076..fed76e5 100644
--- a/examples/constant-temperature.rs
+++ b/examples/constant-temperature.rs
@@ -1,138 +1,15 @@
1use std::{ 1mod common;
2 io::{Read, Write},
3 net::{TcpStream, ToSocketAddrs},
4 sync::{Arc, Mutex},
5 thread::JoinHandle,
6};
7 2
3use common::AsyncTcp;
8use embassy_executor::{Executor, Spawner}; 4use embassy_executor::{Executor, Spawner};
9use embassy_sync::waitqueue::AtomicWaker;
10use embassy_time::Timer; 5use embassy_time::Timer;
11use static_cell::StaticCell; 6use static_cell::StaticCell;
12 7
13static EXECUTOR: StaticCell<Executor> = StaticCell::new();
14static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new(); 8static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new();
15 9
16struct AsyncTcp {
17 write_handle: JoinHandle<()>,
18 write_buffer: Arc<Mutex<Vec<u8>>>,
19 read_buffer: Arc<Mutex<Vec<u8>>>,
20 waker: Arc<AtomicWaker>,
21}
22
23impl AsyncTcp {
24 fn connect(addr: impl ToSocketAddrs) -> Self {
25 let stream = TcpStream::connect(addr).expect("failed to connect to remote");
26 let mut read_stream = stream.try_clone().unwrap();
27 let mut write_stream = stream;
28
29 let read_buffer: Arc<Mutex<Vec<u8>>> = Default::default();
30 let write_buffer: Arc<Mutex<Vec<u8>>> = Default::default();
31
32 let waker = Arc::new(AtomicWaker::new());
33
34 let write_handle = std::thread::spawn({
35 let write_buffer = write_buffer.clone();
36 move || {
37 loop {
38 let buffer = {
39 let mut buffer = write_buffer.lock().unwrap();
40 std::mem::take(&mut *buffer)
41 };
42 if !buffer.is_empty() {
43 println!("writing {} bytes", buffer.len());
44 write_stream.write_all(&buffer).unwrap();
45 write_stream.flush().unwrap();
46 } else {
47 std::thread::park();
48 }
49 }
50 }
51 });
52
53 std::thread::spawn({
54 let read_buffer = read_buffer.clone();
55 let waker = waker.clone();
56 move || {
57 let mut scratch = [0u8; 1024];
58 loop {
59 let n = read_stream.read(&mut scratch).unwrap();
60 if n == 0 {
61 panic!("EOF");
62 }
63
64 {
65 let mut buffer = read_buffer.lock().unwrap();
66 buffer.extend_from_slice(&scratch[..n]);
67 waker.wake();
68 }
69 }
70 }
71 });
72
73 Self {
74 write_handle,
75 write_buffer,
76 read_buffer,
77 waker,
78 }
79 }
80}
81
82impl embedded_io_async::ErrorType for AsyncTcp {
83 type Error = std::io::Error;
84}
85
86impl embedded_io_async::Write for AsyncTcp {
87 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
88 {
89 let mut buffer = self.write_buffer.lock().unwrap();
90 buffer.extend_from_slice(buf);
91 }
92 self.write_handle.thread().unpark();
93 Ok(buf.len())
94 }
95}
96
97impl embedded_io_async::Read for AsyncTcp {
98 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
99 struct WaitForWaker<'a>(&'a AtomicWaker, bool);
100
101 impl<'a> Future for WaitForWaker<'a> {
102 type Output = ();
103
104 fn poll(
105 mut self: std::pin::Pin<&mut Self>,
106 cx: &mut std::task::Context<'_>,
107 ) -> std::task::Poll<Self::Output> {
108 if self.1 {
109 std::task::Poll::Ready(())
110 } else {
111 self.as_mut().1 = true;
112 self.0.register(cx.waker());
113 std::task::Poll::Pending
114 }
115 }
116 }
117
118 loop {
119 {
120 let mut buffer = self.read_buffer.lock().unwrap();
121 if !buffer.is_empty() {
122 let copy_n = buf.len().min(buffer.len());
123 buf[..copy_n].copy_from_slice(&buffer[..copy_n]);
124 buffer.drain(..copy_n);
125 return Ok(copy_n);
126 }
127 }
128 WaitForWaker(&self.waker, false).await
129 }
130 }
131}
132
133#[embassy_executor::task] 10#[embassy_executor::task]
134async fn main_task(spawner: Spawner) { 11async fn main_task(spawner: Spawner) {
135 let mut stream = AsyncTcp::connect("mqtt.d464.sh:1883"); 12 let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS"));
136 13
137 let mut device = embassy_ha::Device::new( 14 let mut device = embassy_ha::Device::new(
138 RESOURCES.init(Default::default()), 15 RESOURCES.init(Default::default()),
@@ -163,9 +40,4 @@ async fn temperature(mut sensor: embassy_ha::TemperatureSensor<'static>) {
163 } 40 }
164} 41}
165 42
166fn main() { 43example_main!();
167 let executor = EXECUTOR.init(Executor::new());
168 executor.run(|spawner| {
169 spawner.must_spawn(main_task(spawner));
170 });
171}
diff --git a/examples/random-temperature.rs b/examples/random-temperature.rs
new file mode 100644
index 0000000..2bcae57
--- /dev/null
+++ b/examples/random-temperature.rs
@@ -0,0 +1,43 @@
1mod common;
2
3use common::AsyncTcp;
4use embassy_executor::{Executor, Spawner};
5use embassy_time::Timer;
6use static_cell::StaticCell;
7
8static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new();
9
10#[embassy_executor::task]
11async fn main_task(spawner: Spawner) {
12 let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS"));
13
14 let mut device = embassy_ha::Device::new(
15 RESOURCES.init(Default::default()),
16 embassy_ha::DeviceConfig {
17 device_id: "example-device-id",
18 device_name: "Example Device Name",
19 manufacturer: "Example Device Manufacturer",
20 model: "Example Device Model",
21 },
22 );
23
24 let temperature_sensor = device.create_temperature_sensor(
25 "temperature-sensor-id",
26 "Temperature Sensor Name",
27 embassy_ha::TemperatureUnit::Celcius,
28 );
29
30 spawner.must_spawn(temperature(temperature_sensor));
31
32 device.run(&mut stream).await;
33}
34
35#[embassy_executor::task]
36async fn temperature(mut sensor: embassy_ha::TemperatureSensor<'static>) {
37 loop {
38 sensor.publish(rand::random_range(0.0..50.0));
39 Timer::after_secs(1).await;
40 }
41}
42
43example_main!();