ask433/timer/macros.rs
1/// Declares a static global `ASK_DRIVER` instance protected by a `critical_section` mutex.
2///
3/// This macro creates a `static` singleton `ASK_DRIVER` suitable for use in
4/// interrupt-based environments, where both the main thread and an ISR need
5/// to safely access the shared driver state.
6///
7/// # Arguments
8/// - `$tx`: The concrete type of the TX pin (must implement `OutputPin`)
9/// - `$rx`: The concrete type of the RX pin (must implement `InputPin`)
10/// - `$ptt`: The concrete type of the PRR pin (must implement `OutputPin`). **NOTE**: While you
11/// might not have a PTT pin, it is imperative that you pass a type here.
12///
13/// # Example
14/// ```rust
15/// use ask433::init_ask_driver;
16/// use embedded_hal_mock::eh1::digital::{Mock as Pin};
17/// init_ask_driver!(Pin, Pin, Pin);
18/// ```
19#[cfg_attr(feature = "timer-isr", macro_export)]
20macro_rules! init_ask_driver {
21 ($tx:ty, $rx:ty, $ptt:ty) => {
22 #[allow(unused)]
23 pub static ASK_DRIVER: $crate::critical_section::Mutex<
24 core::cell::RefCell<Option<$crate::driver::AskDriver<$tx, $rx, $ptt>>>,
25 > = $crate::critical_section::Mutex::new(core::cell::RefCell::new(None));
26 };
27}
28
29/// Initializes the global `ASK_DRIVER` singleton with a new driver instance.
30///
31/// This macro wraps construction of the `AskDriver` and stores it inside the
32/// globally declared `ASK_DRIVER` created by `init_ask_driver!`.
33///
34/// # Arguments
35/// - `$tx`: The TX pin (must implement `OutputPin`)
36/// - `$rx`: The RX pin (must implement `InputPin`)
37/// - `$ptt`: The optional PTT pin (must implement `OutputPin`)
38/// - `$tpb`: The number of ticks per bit (e.g., 8 for a radio at 2 kbps and an interrupt at ~2MHz)
39/// - `$ptt_inverted`: The optional boolean specifying whether the PTT pin should be inverted
40/// - `$rx_inverted`: The optional boolean specifying whether the RX pin should be inverted
41///
42/// # Example
43/// ```rust
44/// use embedded_hal_mock::eh1::digital::{Mock as Pin, Transaction as Tx, State as St};
45/// use ask433::{init_ask_driver, setup_ask_driver};
46///
47/// init_ask_driver!(Pin, Pin, Pin);
48///
49/// fn main() {
50/// # let tx = Pin::new(&[Tx::set(St::Low)]);
51/// # let rx = Pin::new(&[]);
52/// setup_ask_driver!(tx, rx, None, 8, None, None);
53/// # critical_section::with(|cs| {
54/// # if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
55/// # driver.tx.done();
56/// # driver.rx.done();
57/// # }
58/// # });
59/// }
60/// ```
61///
62/// # Notes
63/// - Must be called inside a critical section-aware context (safe in `main()`).
64/// - Requires `init_ask_driver!` to have been used earlier.
65#[cfg_attr(feature = "timer-isr", macro_export)]
66macro_rules! setup_ask_driver {
67 ( $tx:expr, $rx:expr, $ptt:expr, $ticks_per_bit:expr, $ptt_inverted:expr, $rx_inverted:expr ) => {
68 $crate::critical_section::with(|cs| {
69 let _ = ASK_DRIVER
70 .borrow(cs)
71 .replace(Some($crate::driver::AskDriver::new(
72 $tx,
73 $rx,
74 $ptt,
75 $ticks_per_bit,
76 $ptt_inverted,
77 $rx_inverted,
78 )));
79 });
80 };
81}
82
83/// Calls `tick()` on the global `ASK_DRIVER` if it has been initialized.
84///
85/// This macro is intended to be invoked from a timer ISR or scheduler to
86/// advance the ASK state machine at regular intervals (e.g., every 62.5 µs).
87///
88/// # Example
89/// ```rust,ignore
90/// use ask433::tick_ask_timer;
91///
92/// #[interrupt]
93/// fn TIM2() {
94/// tick_ask_timer!();
95/// }
96/// ```
97///
98/// # Notes
99/// - This macro assumes `ASK_DRIVER` was declared with `init_ask_driver!`
100/// and initialized via `setup_ask_driver!`.
101/// - Safe to call repeatedly — will silently do nothing if the driver hasn't been set up yet.
102#[cfg_attr(feature = "timer-isr", macro_export)]
103macro_rules! tick_ask_timer {
104 () => {
105 $crate::critical_section::with(|cs| {
106 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
107 driver.tick();
108 }
109 });
110 };
111}
112
113/// Attempts to receive a completed message from the global ASK driver instance.
114///
115/// This macro checks whether a valid and complete message is available using
116/// `driver.availabile()`, and if so, returns a reference to the decoded message
117/// payload slice. If no message is available, or the driver is currently transmitting,
118/// it returns `None`.
119///
120/// # Requirements
121/// - The global `ASK_DRIVER` instance must have been initialized using
122/// [`init_ask_driver!`](crate::init_ask_driver) and [`setup_ask_driver!`](crate::setup_ask_driver).
123/// - Must be called in a context where `critical_section` is available and safe to use.
124///
125/// # Returns
126/// - `Some(&[u8])`: A reference to the message payload if available
127/// - `None`: If no message is currently available
128///
129/// # Example
130/// ```rust
131/// use ask433::{receive_from_ask, init_ask_driver};
132/// # use embedded_hal_mock::eh1::digital::{Mock as Pin};
133///
134/// init_ask_driver!(Pin, Pin, Pin);
135///
136/// fn main() {
137/// // ...
138/// if let Some(msg) = receive_from_ask!() {
139/// // Process message payload
140/// }
141/// }
142/// ```
143#[cfg_attr(feature = "timer-isr", macro_export)]
144macro_rules! receive_from_ask {
145 () => {
146 $crate::critical_section::with(|cs| {
147 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
148 if driver.availabile() {
149 driver.receive()
150 } else {
151 None
152 }
153 } else {
154 None
155 }
156 })
157 };
158}
159
160/// Sends a message from the global ASK driver using a heapless `Vec<u8>`.
161///
162/// This macro supports two usage patterns:
163/// 1. Repeating a single element: `send_from_ask!(0xAA; 10)`
164/// 2. Sending an explicit sequence of bytes: `send_from_ask!(0x01, 0x02, 0x03)`
165///
166/// The message is internally collected into a `heapless::Vec<u8, ASK_MAX_MESSAGE_LEN_USIZE>`
167/// and passed to the `send()` method on the global driver.
168///
169/// # Requirements
170/// - The driver must be initialized via `init_global_ask_driver!` and `setup_global_ask_driver!`.
171///
172/// # Panics
173/// - Will panic at runtime if the message exceeds `ASK_MAX_MESSAGE_LEN`
174///
175/// # Example
176/// ```rust,ignore
177/// use ask433::{send_from_ask, init_ask_driver};
178/// # use embedded_hal_mock::eh1::digital::{Mock as Pin};
179///
180/// init_ask_driver!(Pin, Pin, Pin);
181///
182/// fn main() {
183///
184/// // ...
185///
186/// let sent1 = send_from_ask!(0xAB; 4); // send [0xAB, 0xAB, 0xAB, 0xAB]
187/// let sent2 = send_from_ask!(1, 2, 3, 4, 5); // send [1, 2, 3, 4, 5]
188/// let sent3 = send_from_ask!("Hello, World!"); // send [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21]
189/// }
190/// ```
191#[cfg(not(feature = "std"))]
192#[cfg_attr(all(feature = "timer-isr", not(feature = "std")), macro_export)]
193macro_rules! send_from_ask {
194 ($elem:expr) => {
195 $crate::critical_section::with(|cs| {
196 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
197 let message = $crate::heapless::Vec::from_slice($elem.as_bytes()).unwrap();
198 driver.send(message)
199 } else {
200 false
201 }
202 })
203 };
204 ($elem:expr; $n:expr) => {
205 $crate::critical_section::with(|cs| {
206 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
207 let mut message = $crate::heapless::Vec::new();
208 message.extend([$elem; $n]);
209 driver.send(message)
210 } else {
211 false
212 }
213 })
214 };
215 ($($x:expr),+ $(,)?) => {
216 $crate::critical_section::with(|cs| {
217 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
218 let mut message = $crate::heapless::Vec::new();
219 for item in [$($x),+] {
220 let _ = message.push(item);
221 }
222 driver.send(message)
223 } else {
224 false
225 }
226 })
227 };
228}
229
230/// Sends a message from the global ASK driver using a heapless `Vec<u8>`.
231///
232/// This macro supports two usage patterns:
233/// 1. Repeating a single element: `send_from_ask!(0xAA; 10)`
234/// 2. Sending an explicit sequence of bytes: `send_from_ask!(0x01, 0x02, 0x03)`
235///
236/// The message is internally collected into a `heapless::Vec<u8, ASK_MAX_MESSAGE_LEN_USIZE>`
237/// and passed to the `send()` method on the global driver.
238///
239/// # Requirements
240/// - The driver must be initialized via `init_ask_driver!` and `setup_ask_driver!`.
241///
242/// # Panics
243/// - Will panic at runtime if the message exceeds `ASK_MAX_MESSAGE_LEN`
244///
245/// # Example
246/// ```rust
247/// use ask433::{send_from_ask, init_ask_driver};
248/// # use embedded_hal_mock::eh1::digital::{Mock as Pin};
249///
250/// init_ask_driver!(Pin, Pin, Pin);
251///
252/// fn main() {
253///
254/// // ...
255///
256/// let sent1: bool = send_from_ask![0xAB; 4]; // send [0xAB, 0xAB, 0xAB, 0xAB]
257/// let sent2: bool = send_from_ask![1, 2, 3, 4, 5]; // send [1, 2, 3, 4, 5]
258/// let sent3: bool = send_from_ask!("Hello, World!"); // send [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21]
259///
260/// }
261/// ```
262#[cfg(feature = "std")]
263#[cfg_attr(all(feature = "timer-isr", feature = "std"), macro_export)]
264macro_rules! send_from_ask {
265 ($elem:expr) => {
266 $crate::critical_section::with(|cs| {
267 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
268 let message = ::std::vec::Vec::from($elem.as_bytes());
269 driver.send(message)
270 } else {
271 false
272 }
273 })
274 };
275 ($elem:expr; $n:expr) => {
276 $crate::critical_section::with(|cs| {
277 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
278 let mut message = ::std::vec::Vec::new();
279 message.extend([$elem; $n]);
280 driver.send(message)
281 } else {
282 false
283 }
284 })
285 };
286 ($($x:expr),+ $(,)?) => {
287 $crate::critical_section::with(|cs| {
288 if let Some(driver) = ASK_DRIVER.borrow(cs).borrow_mut().as_mut() {
289 let mut message = ::std::vec::Vec::new();
290 for item in [$($x),+] {
291 message.push(item);
292 }
293 driver.send(message)
294 } else {
295 false
296 }
297 })
298 };
299}
300
301// see src/lib.rs for tests