tangled
alpha
login
or
join now
baileytownsend.dev
/
rusty-badger
0
fork
atom
A personal rust firmware for the Badger 2040 W
0
fork
atom
overview
issues
pulls
pipelines
RTC POC
baileytownsend.dev
1 year ago
5ebd170a
3b930be0
+123
-66
2 changed files
expand all
collapse all
unified
split
src
badge_display
mod.rs
main.rs
+68
-65
src/badge_display/mod.rs
···
1
pub mod display_image;
2
3
-
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU8};
0
0
0
0
4
use defmt::*;
5
use display_image::get_current_image;
6
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
7
use embassy_rp::gpio;
8
use embassy_rp::gpio::Input;
0
0
0
0
9
use embassy_time::{Delay, Duration, Timer};
10
use embedded_graphics::{
11
image::Image,
···
34
pub static CURRENT_IMAGE: AtomicU8 = AtomicU8::new(0);
35
pub static CHANGE_IMAGE: AtomicBool = AtomicBool::new(true);
36
pub static WIFI_COUNT: AtomicU32 = AtomicU32::new(0);
0
0
0
0
37
pub static HOUR: AtomicU8 = AtomicU8::new(10);
38
pub static MINUTE: AtomicU8 = AtomicU8::new(57);
39
pub static SECOND: AtomicU8 = AtomicU8::new(0);
40
-
pub static WRITE_NEW_TIME: AtomicBool = AtomicBool::new(true);
41
42
#[embassy_executor::task]
43
pub async fn run_the_display(
···
53
54
display.reset().await;
55
56
-
// Initialise display. Using the default LUT speed setting
57
-
let _ = display.setup(LUT::Fast).await;
58
59
// Note we're setting the Text color to `Off`. The driver is set up to treat Off as Black so that BMPs work as expected.
60
let character_style = MonoTextStyle::new(&FONT_9X18_BOLD, BinaryColor::Off);
···
89
// Draw the text box.
90
name_and_detail_box.draw(&mut display).unwrap();
91
92
-
let _ = display.update().await;
93
0
94
let cycle: Duration = Duration::from_millis(500);
0
95
let mut first_run = true;
96
-
let mut text: String<16> = String::<16>::new();
97
-
let cycles_to_skip = 30;
98
let mut cycles_since_last_clear = 0;
99
100
-
let mut time_text: String<16> = String::<16>::new();
0
101
102
-
loop {
103
-
//if cycles are even, update the time values
104
if cycles_since_last_clear % 2 == 0 {
105
-
update_time_values_from_cycles();
106
}
107
108
-
if WRITE_NEW_TIME.load(core::sync::atomic::Ordering::Relaxed) {
109
-
let hour = HOUR.load(core::sync::atomic::Ordering::Relaxed);
110
-
let minute = MINUTE.load(core::sync::atomic::Ordering::Relaxed);
111
-
let second = SECOND.load(core::sync::atomic::Ordering::Relaxed);
112
-
113
-
let _ = core::fmt::write(
114
-
&mut time_text,
115
-
format_args!("{:02}:{:02}:{:02}", hour, minute, second),
116
-
);
117
-
118
-
let time_bounds = Rectangle::new(Point::new(0, 24), Size::new(WIDTH, 16));
119
-
time_bounds
120
.into_styled(
121
PrimitiveStyleBuilder::default()
122
.stroke_color(BinaryColor::Off)
···
127
.draw(&mut display)
128
.unwrap();
129
130
-
Text::new(time_text.as_str(), Point::new(8, 32), character_style)
131
.draw(&mut display)
132
.unwrap();
133
0
134
let result = display
135
-
.partial_update(time_bounds.try_into().unwrap())
136
.await;
137
match result {
138
Ok(_) => {}
···
140
info!("Error updating display");
141
}
142
}
143
-
WRITE_NEW_TIME.store(false, core::sync::atomic::Ordering::Relaxed);
0
0
144
}
145
146
-
if cycles_since_last_clear >= cycles_to_skip || first_run {
147
-
let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed);
148
-
let _ = core::fmt::write(&mut text, format_args!("Count: {}", count));
149
-
let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24));
150
-
count_bounds
0
0
0
0
0
0
0
151
.into_styled(
152
PrimitiveStyleBuilder::default()
153
.stroke_color(BinaryColor::Off)
···
158
.draw(&mut display)
159
.unwrap();
160
161
-
Text::new(text.as_str(), Point::new(8, 16), character_style)
162
-
.draw(&mut display)
163
-
.unwrap();
0
0
0
0
0
0
0
0
0
164
165
-
// // Draw the text box.
166
let result = display
167
-
.partial_update(count_bounds.try_into().unwrap())
168
.await;
169
match result {
170
Ok(_) => {}
···
172
info!("Error updating display");
173
}
174
}
175
-
text.clear();
176
-
// let _ = display.clear(Rgb565::WHITE.into());
177
-
// let _ = display.update().await;
178
-
WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed);
179
-
cycles_since_last_clear = 0;
180
}
0
0
181
182
if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) {
183
let current_image = get_current_image();
···
203
}
204
205
cycles_since_last_clear += 1;
0
0
0
206
first_run = false;
0
207
Timer::after(cycle).await;
208
}
209
}
210
-
211
-
fn update_time_values_from_cycles() {
212
-
let current_second = SECOND.load(core::sync::atomic::Ordering::Relaxed);
213
-
let current_minute = MINUTE.load(core::sync::atomic::Ordering::Relaxed);
214
-
let current_hour = HOUR.load(core::sync::atomic::Ordering::Relaxed);
215
-
216
-
let new_second = (current_second + 1) % 60;
217
-
let new_minute = if new_second == 0 {
218
-
WRITE_NEW_TIME.store(true, core::sync::atomic::Ordering::Relaxed);
219
-
(current_minute + 1) % 60
220
-
} else {
221
-
current_minute
222
-
};
223
-
let new_hour = if new_minute == 0 && new_second == 0 {
224
-
WRITE_NEW_TIME.store(true, core::sync::atomic::Ordering::Relaxed);
225
-
(current_hour + 1) % 24
226
-
} else {
227
-
current_hour
228
-
};
229
-
230
-
SECOND.store(new_second, core::sync::atomic::Ordering::Relaxed);
231
-
MINUTE.store(new_minute, core::sync::atomic::Ordering::Relaxed);
232
-
HOUR.store(new_hour, core::sync::atomic::Ordering::Relaxed);
233
-
}
···
1
pub mod display_image;
2
3
+
use core::{
4
+
cell::RefCell,
5
+
sync::atomic::{AtomicBool, AtomicU32, AtomicU8},
6
+
};
7
+
use cortex_m::interrupt::CriticalSection;
8
use defmt::*;
9
use display_image::get_current_image;
10
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
11
use embassy_rp::gpio;
12
use embassy_rp::gpio::Input;
13
+
use embassy_sync::{
14
+
blocking_mutex::{self, raw::CriticalSectionRawMutex},
15
+
mutex,
16
+
};
17
use embassy_time::{Delay, Duration, Timer};
18
use embedded_graphics::{
19
image::Image,
···
42
pub static CURRENT_IMAGE: AtomicU8 = AtomicU8::new(0);
43
pub static CHANGE_IMAGE: AtomicBool = AtomicBool::new(true);
44
pub static WIFI_COUNT: AtomicU32 = AtomicU32::new(0);
45
+
46
+
pub static RTC_TIME_STRING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<String<8>>> =
47
+
blocking_mutex::Mutex::new(RefCell::new(String::<8>::new()));
48
+
49
pub static HOUR: AtomicU8 = AtomicU8::new(10);
50
pub static MINUTE: AtomicU8 = AtomicU8::new(57);
51
pub static SECOND: AtomicU8 = AtomicU8::new(0);
0
52
53
#[embassy_executor::task]
54
pub async fn run_the_display(
···
64
65
display.reset().await;
66
67
+
// Initialise display with speed
68
+
let _ = display.setup(LUT::Medium).await;
69
70
// Note we're setting the Text color to `Off`. The driver is set up to treat Off as Black so that BMPs work as expected.
71
let character_style = MonoTextStyle::new(&FONT_9X18_BOLD, BinaryColor::Off);
···
100
// Draw the text box.
101
name_and_detail_box.draw(&mut display).unwrap();
102
103
+
// let _ = display.update().await;
104
105
+
//Each cycle is half a second
106
let cycle: Duration = Duration::from_millis(500);
107
+
108
let mut first_run = true;
109
+
//New start every 120 cycles or 60 seconds
110
+
let cycles_to_clear_at: i32 = 120;
111
let mut cycles_since_last_clear = 0;
112
113
+
loop {
114
+
//Timed based display events
115
116
+
//Runs every second;
0
117
if cycles_since_last_clear % 2 == 0 {
118
+
// update_time_values_from_cycles();
119
}
120
121
+
//Updates the top bar
122
+
//Runs every 30 cycles/15 seconds and first run
123
+
if cycles_since_last_clear % 30 == 0 || first_run {
124
+
let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed);
125
+
let count_text: String<16> = easy_format::<16>(format_args!("Count: {}", count));
126
+
let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24));
127
+
count_bounds
0
0
0
0
0
128
.into_styled(
129
PrimitiveStyleBuilder::default()
130
.stroke_color(BinaryColor::Off)
···
135
.draw(&mut display)
136
.unwrap();
137
138
+
Text::new(count_text.as_str(), Point::new(8, 16), character_style)
139
.draw(&mut display)
140
.unwrap();
141
142
+
// // Draw the text box.
143
let result = display
144
+
.partial_update(count_bounds.try_into().unwrap())
145
.await;
146
match result {
147
Ok(_) => {}
···
149
info!("Error updating display");
150
}
151
}
152
+
// let _ = display.clear(Rgb565::WHITE.into());
153
+
// let _ = display.update().await;
154
+
WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed);
155
}
156
157
+
//Runs every 120 cycles/60 seconds and first run
158
+
if cycles_since_last_clear == 0 {
159
+
let mut time_text: String<8> = String::<8>::new();
160
+
161
+
let time_box_rectangle_location = Point::new(0, 96);
162
+
RTC_TIME_STRING.lock(|x| {
163
+
time_text.push_str(x.borrow().as_str()).unwrap();
164
+
});
165
+
166
+
//The bounds of the box for time and refresh area
167
+
let time_bounds = Rectangle::new(time_box_rectangle_location, Size::new(88, 24));
168
+
time_bounds
169
.into_styled(
170
PrimitiveStyleBuilder::default()
171
.stroke_color(BinaryColor::Off)
···
176
.draw(&mut display)
177
.unwrap();
178
179
+
//Adding a y offset to the box location to fit inside the box
180
+
Text::new(
181
+
time_text.as_str(),
182
+
(
183
+
time_box_rectangle_location.x + 8,
184
+
time_box_rectangle_location.y + 16,
185
+
)
186
+
.into(),
187
+
character_style,
188
+
)
189
+
.draw(&mut display)
190
+
.unwrap();
191
0
192
let result = display
193
+
.partial_update(time_bounds.try_into().unwrap())
194
.await;
195
match result {
196
Ok(_) => {}
···
198
info!("Error updating display");
199
}
200
}
0
0
0
0
0
201
}
202
+
203
+
//Manually triggered display events
204
205
if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) {
206
let current_image = get_current_image();
···
226
}
227
228
cycles_since_last_clear += 1;
229
+
if cycles_since_last_clear >= cycles_to_clear_at {
230
+
cycles_since_last_clear = 0;
231
+
}
232
first_run = false;
233
+
// info!("Display Cycle: {}", cycles_since_last_clear);
234
Timer::after(cycle).await;
235
}
236
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
+55
-1
src/main.rs
···
5
#![no_std]
6
#![no_main]
7
use badge_display::display_image::DisplayImage;
8
-
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE};
9
use defmt::info;
10
use embassy_executor::Spawner;
11
use embassy_rp::gpio;
12
use embassy_rp::gpio::Input;
13
use embassy_rp::peripherals::SPI0;
0
14
use embassy_rp::spi::Spi;
15
use embassy_rp::spi::{self};
16
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
···
29
TextBox,
30
};
31
use gpio::{Level, Output, Pull};
0
32
use static_cell::StaticCell;
33
use {defmt_rtt as _, panic_probe as _};
34
···
90
// control.gpio_set(0, true).await;
91
spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset));
92
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
93
//Input loop
94
loop {
95
//Change Image Button
···
102
Timer::after(Duration::from_millis(500)).await;
103
continue;
104
}
0
0
0
0
0
0
0
0
0
105
Timer::after(Duration::from_millis(100)).await;
106
}
107
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
5
#![no_std]
6
#![no_main]
7
use badge_display::display_image::DisplayImage;
8
+
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE, RTC_TIME_STRING};
9
use defmt::info;
10
use embassy_executor::Spawner;
11
use embassy_rp::gpio;
12
use embassy_rp::gpio::Input;
13
use embassy_rp::peripherals::SPI0;
14
+
use embassy_rp::rtc::{DateTime, DayOfWeek};
15
use embassy_rp::spi::Spi;
16
use embassy_rp::spi::{self};
17
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
···
30
TextBox,
31
};
32
use gpio::{Level, Output, Pull};
33
+
use helpers::easy_format;
34
use static_cell::StaticCell;
35
use {defmt_rtt as _, panic_probe as _};
36
···
92
// control.gpio_set(0, true).await;
93
spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset));
94
95
+
//rtc setup
96
+
let mut rtc = embassy_rp::rtc::Rtc::new(p.RTC);
97
+
if !rtc.is_running() {
98
+
info!("Start RTC");
99
+
let now = DateTime {
100
+
year: 2000,
101
+
month: 1,
102
+
day: 1,
103
+
day_of_week: DayOfWeek::Saturday,
104
+
hour: 0,
105
+
minute: 0,
106
+
second: 0,
107
+
};
108
+
rtc.set_datetime(now).unwrap();
109
+
}
110
+
111
//Input loop
112
loop {
113
//Change Image Button
···
120
Timer::after(Duration::from_millis(500)).await;
121
continue;
122
}
123
+
124
+
let now = rtc.now();
125
+
match now {
126
+
Ok(time) => set_display_time(time),
127
+
Err(_) => {
128
+
info!("Error getting time");
129
+
}
130
+
}
131
+
132
Timer::after(Duration::from_millis(100)).await;
133
}
134
}
135
+
136
+
fn set_display_time(time: DateTime) {
137
+
let mut am = true;
138
+
let twelve_hour = if time.hour > 12 {
139
+
am = false;
140
+
time.hour - 12
141
+
} else if time.hour == 0 {
142
+
12
143
+
} else {
144
+
time.hour
145
+
};
146
+
147
+
let am_pm = if am { "AM" } else { "PM" };
148
+
149
+
let formatted_time = easy_format::<8>(format_args!(
150
+
"{:02}:{:02} {}",
151
+
twelve_hour, time.minute, am_pm
152
+
));
153
+
154
+
RTC_TIME_STRING.lock(|rtc_time_string| {
155
+
rtc_time_string.borrow_mut().clear();
156
+
rtc_time_string
157
+
.borrow_mut()
158
+
.push_str(formatted_time.as_str())
159
+
.unwrap();
160
+
});
161
+
}