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}