Skip to main content

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