tm1637_embedded_hal/
formatters.rs

1//! Format numbers into byte arrays.
2//!
3//! Functions for converting numbers ([`i8`]s, [`i16`]s, [`i32`]s or [`f32`]s) into arrays of bytes
4//! that can be sent to a TM1637 display.
5//!
6//! There are versions of these functions that are meant for 4-digit displays
7//! and for 6-digit displays. The 6-digit versions take into account that the
8//! order of the bytes does not drectly correlate with the order of the physical
9//! digits.
10//!
11//! All numbers are aligned to the right.
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! tm.write_bytes_raw(0, i16_to_4digits(1234));
17//! ```
18//!
19//! This module is only available when the `formatters` feature of this
20//! library is activated.
21
22use crate::mappings::{DigitBits, UpsideDownDigitBits};
23
24/// Formats a [`i16`] clamped between `-999` and `9999`, for a `4-digit display`.
25///
26/// # Example
27///
28/// A counter that goes from `-100` to `100`:
29///
30/// ```rust, ignore
31/// let mut tm = TM1637::builder(clk_pin, dio_pin, delay)
32///     .brightness(Brightness::L3)
33///     .build();
34///
35/// tm.init().ok();
36///
37/// for i in -100..100 {
38///     let segs = i16_to_4digits(i);
39///     tm.write_segments_raw(0, &segs).ok();
40///
41///     delay.delay_ms(100u16);
42/// }
43/// ```
44pub fn i16_to_4digits(n: i16) -> [u8; 4] {
45    let mut bytes: [u8; 4] = [0; 4];
46    let mut m: i16 = n.clamp(-999, 9999).abs();
47
48    for position in (0..4).rev() {
49        bytes[position as usize] = DigitBits::from_digit((m % 10) as u8) as u8;
50
51        m /= 10;
52
53        if m == 0 {
54            if n < 0 {
55                bytes[position as usize - 1] = 0b01000000; // minus sign
56            }
57            break;
58        };
59    }
60
61    bytes
62}
63
64/// Formats a [`i32`] clamped between `-99999` and `999999`, for a `6-digit display`.
65pub fn i32_to_6digits(n: i32) -> [u8; 6] {
66    let mut b: [u8; 6] = [0; 6];
67    let mut m: i32 = n.clamp(-99999, 999999).abs();
68
69    for position in (0..6).rev() {
70        b[position as usize] = DigitBits::from_digit((m % 10) as u8) as u8;
71
72        m /= 10;
73
74        if m == 0 {
75            if !n.is_positive() {
76                b[position as usize - 1] = 0b01000000; // minus sign
77            }
78            break;
79        };
80    }
81
82    // Swizzle bytes around to fit the order of 6-digit displays
83    [b[2], b[1], b[0], b[5], b[4], b[3]]
84}
85
86/// Formats a [`i8`] clamped between `-9` and `99`, appending the degrees symbol `(°)`
87/// and an `uppercase C`, for a `4-digit display`.
88pub fn celsius_to_4digits(n: i8) -> [u8; 4] {
89    let mut m: i8 = n.clamp(-9, 99);
90
91    // 3rd and 4th bytes are the degrees symbol (°) and uppercase C
92    let mut b: [u8; 4] = [0, 0, 0x63, 0x39];
93
94    for position in (0..2).rev() {
95        b[position as usize] = DigitBits::from_digit((m.abs() % 10) as u8) as u8;
96
97        m /= 10;
98
99        if m == 0 {
100            if !n.is_positive() {
101                b[position as usize - 1] = 0b01000000; // minus sign
102            }
103            break;
104        };
105    }
106    b
107}
108
109/// Formats a [`i16`] clamped between `-99` and `999`, appending the degrees symbol `(°)`,
110/// for a `4-digit display`.
111pub fn degrees_to_4digits(n: i16) -> [u8; 4] {
112    let mut m: i16 = n.clamp(-99, 999);
113
114    // 4th byte is the degrees symbol (°)
115    let mut b: [u8; 4] = [0, 0, 0, 0x63];
116
117    for position in (0..3).rev() {
118        b[position as usize] = DigitBits::from_digit((m.abs() % 10) as u8) as u8;
119
120        m /= 10;
121
122        if m == 0 {
123            if !n.is_positive() {
124                b[position as usize - 1] = 0b01000000; // minus sign
125            }
126            break;
127        };
128    }
129    b
130}
131
132/// Formats two [`u8`]s between `0` and `99`, with an optional colon between them.
133///
134/// This will only work for `4-digit displays` where there's a physical colon,
135/// and that colon acts as the decimal dot between the 2nd and 3rd digit.
136///
137/// # Example
138///
139/// Let's create a clock displaying `12:34` with a blinking colon:
140///
141/// ```rust, ignore
142/// let mut tm = TM1637::builder(clk_pin, dio_pin, delay)
143///     .brightness(Brightness::L3)
144///     .build();
145///
146/// tm.init().ok();
147///
148/// for hour in 12..24 {
149///     for minute in 34..60 {
150///         for second in 0..120 {
151///             let blink = second % 2 == 0;
152///             let segs = clock_to_4digits(hour, minute, blink);
153///
154///             tm.write_segments_raw(0, &segs).ok();
155///
156///             delay.delay_ms(500);
157///         }
158///     }
159/// }
160/// ```
161pub fn clock_to_4digits(hour: u8, minute: u8, colon: bool) -> [u8; 4] {
162    let mut b: [u8; 4] = [0, 0, 0, 0];
163
164    if hour >= 10 {
165        b[0] = DigitBits::from_digit(hour / 10) as u8;
166    }
167    b[1] = DigitBits::from_digit(hour % 10) as u8;
168
169    if colon {
170        b[1] |= 0b1000_0000
171    }
172    b[2] = DigitBits::from_digit(minute / 10) as u8;
173    b[3] = DigitBits::from_digit(minute % 10) as u8;
174
175    b
176}
177
178/// Formats a [`i16`] clamped between `-999` and `9999`, for an `upside-down 4-digit display`.
179pub fn i16_to_upsidedown_4digits(n: i16) -> [u8; 4] {
180    let mut bytes: [u8; 4] = [0; 4];
181    let mut m: i16 = n.clamp(-999, 9999).abs();
182
183    for position in 0..4 {
184        bytes[position as usize] = UpsideDownDigitBits::from_digit((m % 10) as u8) as u8;
185
186        m /= 10;
187
188        if m == 0 {
189            if !n.is_positive() {
190                bytes[position as usize + 1] = 0b01000000; // minus sign
191            }
192            break;
193        };
194    }
195
196    bytes
197}
198
199/// Formats a [`f32`] with the given amount of decimal digits, for a `6-digit display`.
200pub fn f32_to_6digits(n: f32, decimals: u8) -> [u8; 6] {
201    use core::ops::Mul;
202
203    let mut b: [u8; 6] = [0; 6];
204    let decimal_position = 5 - decimals;
205
206    let mut m: i32 = ((n.mul(10i32.pow(decimals as u32) as f32)
207        + if n.is_sign_positive() {
208            0.5_f32
209        } else {
210            -0.5_f32
211        }) as i32)
212        .clamp(-99999, 999999)
213        .abs();
214
215    for position in (0..6).rev() {
216        b[position as usize] = DigitBits::from_digit((m % 10) as u8) as u8;
217
218        m /= 10;
219
220        if position == decimal_position {
221            // Add a dot here
222            b[position as usize] |= 0b1000_0000;
223        }
224        if m == 0 && position <= decimal_position {
225            // Add the minus sign only when the digit with the decimal point
226            // has been done; do not break earlier.
227            if !n.is_sign_positive() {
228                b[position as usize - 1] = 0b01000000; // minus sign
229            }
230            break;
231        };
232    }
233
234    // Swizzle bytes around to fit the order of 6-digit displays
235    [b[2], b[1], b[0], b[5], b[4], b[3]]
236}