+19
Cargo.lock
+19
Cargo.lock
···
246
246
]
247
247
248
248
[[package]]
249
+
name = "cobs"
250
+
version = "0.2.3"
251
+
source = "registry+https://github.com/rust-lang/crates.io-index"
252
+
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
253
+
254
+
[[package]]
249
255
name = "codespan-reporting"
250
256
version = "0.11.1"
251
257
source = "registry+https://github.com/rust-lang/crates.io-index"
···
902
908
"pio",
903
909
"pio-proc",
904
910
"portable-atomic",
911
+
"postcard",
905
912
"rand",
906
913
"reqwless",
907
914
"serde",
···
1412
1419
"atomic-polyfill",
1413
1420
"hash32 0.2.1",
1414
1421
"rustc_version 0.4.0",
1422
+
"serde",
1415
1423
"spin",
1416
1424
"stable_deref_trait",
1417
1425
]
···
1818
1826
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
1819
1827
dependencies = [
1820
1828
"critical-section",
1829
+
]
1830
+
1831
+
[[package]]
1832
+
name = "postcard"
1833
+
version = "1.0.10"
1834
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1835
+
checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e"
1836
+
dependencies = [
1837
+
"cobs",
1838
+
"heapless 0.7.17",
1839
+
"serde",
1821
1840
]
1822
1841
1823
1842
[[package]]
+1
Cargo.toml
+1
Cargo.toml
+9
-15
src/badge_display/mod.rs
+9
-15
src/badge_display/mod.rs
···
9
9
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
10
10
use embassy_rp::gpio;
11
11
use embassy_rp::gpio::Input;
12
-
use embassy_sync::{
13
-
blocking_mutex::{self, raw::CriticalSectionRawMutex},
14
-
mutex,
15
-
};
12
+
use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex};
16
13
use embassy_time::{Delay, Duration, Timer};
17
14
use embedded_graphics::{
18
15
image::Image,
19
16
mono_font::{ascii::*, MonoTextStyle},
20
-
pixelcolor::{BinaryColor, Rgb565},
17
+
pixelcolor::BinaryColor,
21
18
prelude::*,
22
19
primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle},
23
20
text::Text,
···
30
27
use gpio::Output;
31
28
use heapless::String;
32
29
use tinybmp::Bmp;
30
+
use uc8151::asynch::Uc8151;
33
31
use uc8151::LUT;
34
32
use uc8151::WIDTH;
35
-
use uc8151::{asynch::Uc8151, UpdateRegion};
36
33
use {defmt_rtt as _, panic_probe as _};
37
34
38
35
use crate::{env::env_value, helpers::easy_format, Spi0Bus};
···
45
42
blocking_mutex::Mutex::new(RefCell::new(String::<8>::new()));
46
43
pub static TEMP: AtomicU8 = AtomicU8::new(0);
47
44
pub static HUMIDITY: AtomicU8 = AtomicU8::new(0);
48
-
pub static HOUR: AtomicU8 = AtomicU8::new(10);
49
-
pub static MINUTE: AtomicU8 = AtomicU8::new(57);
50
-
pub static SECOND: AtomicU8 = AtomicU8::new(0);
51
45
52
46
#[embassy_executor::task]
53
47
pub async fn run_the_display(
···
118
112
}
119
113
120
114
//Updates the top bar
121
-
//Runs every 30 cycles/15 seconds and first run
122
-
if cycles_since_last_clear % 30 == 0 || first_run {
115
+
//Runs every 60 cycles/30 seconds and first run
116
+
if cycles_since_last_clear % 60 == 0 || first_run {
123
117
let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed);
124
118
let temp = TEMP.load(core::sync::atomic::Ordering::Relaxed);
125
119
let humidity = HUMIDITY.load(core::sync::atomic::Ordering::Relaxed);
126
-
let top_text: String<64> =
127
-
easy_format::<64>(format_args!("{}F {}% Count: {}", temp, humidity, count));
120
+
let top_text: String<64> = easy_format::<64>(format_args!(
121
+
"{}F {}% Wifi found: {}",
122
+
temp, humidity, count
123
+
));
128
124
let top_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24));
129
125
top_bounds
130
126
.into_styled(
···
149
145
info!("Error updating display");
150
146
}
151
147
}
152
-
153
-
WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed);
154
148
}
155
149
156
150
//Runs every 120 cycles/60 seconds and first run
+77
-12
src/main.rs
+77
-12
src/main.rs
···
4
4
5
5
#![no_std]
6
6
#![no_main]
7
+
use badge_display::display_image::DisplayImage;
8
+
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE, RTC_TIME_STRING, WIFI_COUNT};
9
+
use core::fmt::Write;
7
10
use core::str::from_utf8;
8
-
use core::time;
9
-
10
-
use badge_display::display_image::DisplayImage;
11
-
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE, RTC_TIME_STRING};
12
11
use cyw43_driver::setup_cyw43;
13
-
use cyw43_pio::PioSpi;
14
12
use defmt::info;
15
13
use defmt::*;
16
14
use embassy_executor::Spawner;
···
18
16
use embassy_net::tcp::client::{TcpClient, TcpClientState};
19
17
use embassy_net::{Stack, StackResources};
20
18
use embassy_rp::clocks::RoscRng;
19
+
use embassy_rp::flash::Async;
21
20
use embassy_rp::gpio;
22
21
use embassy_rp::gpio::Input;
23
22
use embassy_rp::peripherals::{DMA_CH0, PIO0, SPI0};
···
27
26
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
28
27
use embassy_sync::mutex::Mutex;
29
28
use embassy_time::{Duration, Timer};
29
+
use embedded_hal_1::digital::OutputPin;
30
30
use env::env_value;
31
31
use gpio::{Level, Output, Pull};
32
-
use heapless::Vec;
32
+
use heapless::{String, Vec};
33
33
use helpers::easy_format;
34
34
use rand::RngCore;
35
35
use reqwless::client::{HttpClient, TlsConfig, TlsVerify};
36
36
use reqwless::request::Method;
37
+
use save::{read_postcard_from_flash, save_postcard_to_flash, Save};
37
38
use serde::Deserialize;
38
39
use static_cell::StaticCell;
39
40
use temp_sensor::run_the_temp_sensor;
···
43
44
mod cyw43_driver;
44
45
mod env;
45
46
mod helpers;
47
+
mod save;
46
48
mod temp_sensor;
47
49
48
50
type Spi0Bus = Mutex<NoopRawMutex, Spi<'static, SPI0, spi::Async>>;
49
51
52
+
const BSSID_LEN: usize = 1_000;
53
+
const ADDR_OFFSET: u32 = 0x100000;
54
+
const SAVE_OFFSET: u32 = 0x00;
55
+
56
+
const FLASH_SIZE: usize = 2 * 1024 * 1024;
57
+
50
58
#[embassy_executor::main]
51
59
async fn main(spawner: Spawner) {
52
60
let p = embassy_rp::init(Default::default());
61
+
let mut user_led = Output::new(p.PIN_22, Level::High);
62
+
user_led.set_high();
63
+
53
64
let (net_device, mut control) = setup_cyw43(
54
65
p.PIO0, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, spawner,
55
66
)
···
74
85
let _btn_up = Input::new(p.PIN_15, Pull::Down);
75
86
let _btn_down = Input::new(p.PIN_11, Pull::Down);
76
87
let _btn_a = Input::new(p.PIN_12, Pull::Down);
77
-
let _btn_b = Input::new(p.PIN_13, Pull::Down);
88
+
let btn_b = Input::new(p.PIN_13, Pull::Down);
78
89
let btn_c = Input::new(p.PIN_14, Pull::Down);
79
90
80
91
let spi = Spi::new(
···
242
253
}
243
254
}
244
255
245
-
//
246
-
256
+
//Set up saving
257
+
let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH3);
258
+
let mut save: Save = read_postcard_from_flash(ADDR_OFFSET, &mut flash, SAVE_OFFSET).unwrap();
259
+
WIFI_COUNT.store(save.wifi_counted, core::sync::atomic::Ordering::Relaxed);
247
260
//Task spawning
248
261
spawner.must_spawn(run_the_temp_sensor(p.I2C0, p.PIN_5, p.PIN_4));
249
262
spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset));
250
263
251
264
//Input loop
265
+
let cycle = Duration::from_millis(100);
266
+
let mut current_cycle = 0;
267
+
//5 minutes
268
+
let reset_cycle = 300_000;
269
+
//Turn off led to signify that the badge is ready
270
+
user_led.set_low();
271
+
252
272
loop {
253
273
//Change Image Button
254
274
if btn_c.is_high() {
···
258
278
CURRENT_IMAGE.store(new_image.as_u8(), core::sync::atomic::Ordering::Relaxed);
259
279
CHANGE_IMAGE.store(true, core::sync::atomic::Ordering::Relaxed);
260
280
Timer::after(Duration::from_millis(500)).await;
281
+
current_cycle += 500;
282
+
continue;
283
+
}
284
+
285
+
if btn_b.is_high() {
286
+
user_led.toggle();
287
+
Timer::after(Duration::from_millis(500)).await;
288
+
current_cycle += 500;
261
289
continue;
262
290
}
263
291
···
275
303
rtc_time_string.borrow_mut().push_str("No Wifi").unwrap();
276
304
});
277
305
}
278
-
279
-
Timer::after(Duration::from_millis(100)).await;
306
+
if current_cycle == 0 {
307
+
let mut scanner = control.scan(Default::default()).await;
308
+
while let Some(bss) = scanner.next().await {
309
+
process_bssid(bss.bssid, &mut save.wifi_counted, &mut save.bssid);
310
+
let ssid = core::str::from_utf8(&bss.ssid).unwrap();
311
+
info!("ssid: {}", ssid);
312
+
}
313
+
save_postcard_to_flash(ADDR_OFFSET, &mut flash, SAVE_OFFSET, &save).unwrap();
314
+
WIFI_COUNT.store(save.wifi_counted, core::sync::atomic::Ordering::Relaxed);
315
+
info!("wifi_counted: {}", save.wifi_counted);
316
+
}
317
+
if current_cycle >= reset_cycle {
318
+
current_cycle = 0;
319
+
}
320
+
current_cycle += 1;
321
+
Timer::after(cycle).await;
280
322
}
281
323
}
282
324
···
316
358
struct TimeApiResponse<'a> {
317
359
datetime: &'a str,
318
360
day_of_week: u8,
319
-
// other fields as needed
361
+
}
362
+
363
+
fn process_bssid(bssid: [u8; 6], wifi_counted: &mut u32, bssids: &mut Vec<String<17>, BSSID_LEN>) {
364
+
let bssid_str = format_bssid(bssid);
365
+
if !bssids.contains(&bssid_str) {
366
+
*wifi_counted += 1;
367
+
info!("bssid: {:x}", bssid_str);
368
+
let result = bssids.push(bssid_str);
369
+
if result.is_err() {
370
+
info!("bssid list full");
371
+
bssids.clear();
372
+
}
373
+
}
374
+
}
375
+
376
+
fn format_bssid(bssid: [u8; 6]) -> String<17> {
377
+
let mut s = String::new();
378
+
for (i, byte) in bssid.iter().enumerate() {
379
+
if i != 0 {
380
+
let _ = s.write_char(':');
381
+
}
382
+
core::fmt::write(&mut s, format_args!("{:02x}", byte)).unwrap();
383
+
}
384
+
s
320
385
}
+62
src/save.rs
+62
src/save.rs
···
1
+
use crate::FLASH_SIZE;
2
+
use embassy_rp::flash::{Async, ERASE_SIZE};
3
+
use embassy_rp::peripherals::FLASH;
4
+
use heapless::{String, Vec};
5
+
use postcard::{from_bytes, to_slice};
6
+
use serde::{Deserialize, Serialize};
7
+
use {defmt_rtt as _, panic_probe as _};
8
+
9
+
const BSSID_LEN: usize = 1_000;
10
+
11
+
pub fn save_postcard_to_flash(
12
+
base_offset: u32,
13
+
flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>,
14
+
offset: u32,
15
+
data: &Save,
16
+
) -> Result<(), &'static str> {
17
+
let mut buf = [0u8; ERASE_SIZE];
18
+
19
+
let mut write_buf = [0u8; ERASE_SIZE];
20
+
let written = to_slice(data, &mut write_buf).map_err(|_| "Serialization error")?;
21
+
22
+
if written.len() > ERASE_SIZE {
23
+
return Err("Data too large for flash sector");
24
+
}
25
+
26
+
flash
27
+
.blocking_erase(
28
+
base_offset + offset,
29
+
base_offset + offset + ERASE_SIZE as u32,
30
+
)
31
+
.map_err(|_| "Erase error")?;
32
+
33
+
buf[..written.len()].copy_from_slice(&written);
34
+
35
+
flash
36
+
.blocking_write(base_offset + offset, &buf)
37
+
.map_err(|_| "Write error")?;
38
+
39
+
Ok(())
40
+
}
41
+
42
+
pub fn read_postcard_from_flash(
43
+
base_offset: u32,
44
+
flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>,
45
+
offset: u32,
46
+
) -> Result<Save, &'static str> {
47
+
let mut buf = [0u8; ERASE_SIZE];
48
+
49
+
flash
50
+
.blocking_read(base_offset + offset, &mut buf)
51
+
.map_err(|_| "Read error")?;
52
+
53
+
let data = from_bytes::<Save>(&buf).map_err(|_| "Deserialization error")?;
54
+
55
+
Ok(data)
56
+
}
57
+
58
+
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
59
+
pub struct Save {
60
+
pub wifi_counted: u32,
61
+
pub bssid: Vec<String<17>, BSSID_LEN>,
62
+
}