+220
-8
Cargo.lock
+220
-8
Cargo.lock
···
48
source = "registry+https://github.com/rust-lang/crates.io-index"
49
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
50
51
[[package]]
52
name = "core-foundation"
53
version = "0.10.0"
···
64
source = "registry+https://github.com/rust-lang/crates.io-index"
65
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
66
67
[[package]]
68
name = "defmt"
69
version = "0.3.100"
···
105
"thiserror",
106
]
107
108
[[package]]
109
name = "embedded-hal"
110
version = "1.0.0"
···
120
source = "registry+https://github.com/rust-lang/crates.io-index"
121
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
122
dependencies = [
123
-
"embedded-hal",
124
]
125
126
[[package]]
···
129
source = "registry+https://github.com/rust-lang/crates.io-index"
130
checksum = "f9a0f04f8886106faf281c47b6a0e4054a369baedaf63591fdb8da9761f3f379"
131
dependencies = [
132
-
"embedded-hal",
133
"embedded-hal-nb",
134
]
135
···
139
source = "registry+https://github.com/rust-lang/crates.io-index"
140
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
141
dependencies = [
142
-
"embedded-hal",
143
-
"nb",
144
]
145
146
[[package]]
···
149
source = "registry+https://github.com/rust-lang/crates.io-index"
150
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
151
152
[[package]]
153
name = "gpio-cdev"
154
version = "0.6.0"
···
169
"byteorder",
170
]
171
172
[[package]]
173
name = "heapless"
174
version = "0.9.2"
···
215
checksum = "2a8a605c95f708c78554738a12153b213f107d3bd5323f7ce32d6deb3faafb40"
216
dependencies = [
217
"cast",
218
-
"embedded-hal",
219
"embedded-hal-nb",
220
"gpio-cdev",
221
"i2cdev",
222
-
"nb",
223
"nix 0.27.1",
224
"serialport",
225
"spidev",
226
"sysfs_gpio",
227
]
228
229
[[package]]
230
name = "mach2"
231
version = "0.4.3"
···
235
"libc",
236
]
237
238
[[package]]
239
name = "memoffset"
240
version = "0.6.5"
···
253
"autocfg",
254
]
255
256
[[package]]
257
name = "nb"
258
version = "1.1.0"
···
296
"libc",
297
]
298
299
[[package]]
300
name = "pin-utils"
301
version = "0.1.0"
···
351
version = "0.1.0"
352
dependencies = [
353
"defmt 1.0.1",
354
-
"heapless",
355
"sachy-fmt",
356
]
357
···
371
version = "0.1.0"
372
dependencies = [
373
"defmt 1.0.1",
374
-
"embedded-hal",
375
"embedded-hal-async",
376
"embedded-hal-mock",
377
"linux-embedded-hal",
378
]
379
380
[[package]]
381
name = "scopeguard"
382
version = "1.2.0"
···
408
source = "registry+https://github.com/rust-lang/crates.io-index"
409
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
410
411
[[package]]
412
name = "spidev"
413
version = "0.6.1"
···
480
source = "registry+https://github.com/rust-lang/crates.io-index"
481
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
482
483
[[package]]
484
name = "windows-sys"
485
version = "0.52.0"
···
48
source = "registry+https://github.com/rust-lang/crates.io-index"
49
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
50
51
+
[[package]]
52
+
name = "chrono"
53
+
version = "0.4.42"
54
+
source = "registry+https://github.com/rust-lang/crates.io-index"
55
+
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
56
+
dependencies = [
57
+
"num-traits",
58
+
]
59
+
60
[[package]]
61
name = "core-foundation"
62
version = "0.10.0"
···
73
source = "registry+https://github.com/rust-lang/crates.io-index"
74
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
75
76
+
[[package]]
77
+
name = "critical-section"
78
+
version = "1.2.0"
79
+
source = "registry+https://github.com/rust-lang/crates.io-index"
80
+
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
81
+
82
[[package]]
83
name = "defmt"
84
version = "0.3.100"
···
120
"thiserror",
121
]
122
123
+
[[package]]
124
+
name = "document-features"
125
+
version = "0.2.12"
126
+
source = "registry+https://github.com/rust-lang/crates.io-index"
127
+
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
128
+
dependencies = [
129
+
"litrs",
130
+
]
131
+
132
+
[[package]]
133
+
name = "embassy-net"
134
+
version = "0.7.1"
135
+
source = "registry+https://github.com/rust-lang/crates.io-index"
136
+
checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2"
137
+
dependencies = [
138
+
"document-features",
139
+
"embassy-net-driver",
140
+
"embassy-sync",
141
+
"embassy-time",
142
+
"embedded-io-async",
143
+
"embedded-nal-async",
144
+
"heapless 0.8.0",
145
+
"managed",
146
+
"smoltcp",
147
+
]
148
+
149
+
[[package]]
150
+
name = "embassy-net-driver"
151
+
version = "0.2.0"
152
+
source = "registry+https://github.com/rust-lang/crates.io-index"
153
+
checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d"
154
+
155
+
[[package]]
156
+
name = "embassy-sync"
157
+
version = "0.7.2"
158
+
source = "registry+https://github.com/rust-lang/crates.io-index"
159
+
checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b"
160
+
dependencies = [
161
+
"cfg-if",
162
+
"critical-section",
163
+
"embedded-io-async",
164
+
"futures-core",
165
+
"futures-sink",
166
+
"heapless 0.8.0",
167
+
]
168
+
169
+
[[package]]
170
+
name = "embassy-time"
171
+
version = "0.5.0"
172
+
source = "registry+https://github.com/rust-lang/crates.io-index"
173
+
checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65"
174
+
dependencies = [
175
+
"cfg-if",
176
+
"critical-section",
177
+
"document-features",
178
+
"embassy-time-driver",
179
+
"embedded-hal 0.2.7",
180
+
"embedded-hal 1.0.0",
181
+
"embedded-hal-async",
182
+
"futures-core",
183
+
]
184
+
185
+
[[package]]
186
+
name = "embassy-time-driver"
187
+
version = "0.2.1"
188
+
source = "registry+https://github.com/rust-lang/crates.io-index"
189
+
checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6"
190
+
dependencies = [
191
+
"document-features",
192
+
]
193
+
194
+
[[package]]
195
+
name = "embedded-hal"
196
+
version = "0.2.7"
197
+
source = "registry+https://github.com/rust-lang/crates.io-index"
198
+
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
199
+
dependencies = [
200
+
"nb 0.1.3",
201
+
"void",
202
+
]
203
+
204
[[package]]
205
name = "embedded-hal"
206
version = "1.0.0"
···
216
source = "registry+https://github.com/rust-lang/crates.io-index"
217
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
218
dependencies = [
219
+
"embedded-hal 1.0.0",
220
]
221
222
[[package]]
···
225
source = "registry+https://github.com/rust-lang/crates.io-index"
226
checksum = "f9a0f04f8886106faf281c47b6a0e4054a369baedaf63591fdb8da9761f3f379"
227
dependencies = [
228
+
"embedded-hal 1.0.0",
229
"embedded-hal-nb",
230
]
231
···
235
source = "registry+https://github.com/rust-lang/crates.io-index"
236
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
237
dependencies = [
238
+
"embedded-hal 1.0.0",
239
+
"nb 1.1.0",
240
+
]
241
+
242
+
[[package]]
243
+
name = "embedded-io"
244
+
version = "0.6.1"
245
+
source = "registry+https://github.com/rust-lang/crates.io-index"
246
+
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
247
+
248
+
[[package]]
249
+
name = "embedded-io-async"
250
+
version = "0.6.1"
251
+
source = "registry+https://github.com/rust-lang/crates.io-index"
252
+
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
253
+
dependencies = [
254
+
"embedded-io",
255
+
]
256
+
257
+
[[package]]
258
+
name = "embedded-nal"
259
+
version = "0.9.0"
260
+
source = "registry+https://github.com/rust-lang/crates.io-index"
261
+
checksum = "c56a28be191a992f28f178ec338a0bf02f63d7803244add736d026a471e6ed77"
262
+
dependencies = [
263
+
"nb 1.1.0",
264
+
]
265
+
266
+
[[package]]
267
+
name = "embedded-nal-async"
268
+
version = "0.8.0"
269
+
source = "registry+https://github.com/rust-lang/crates.io-index"
270
+
checksum = "76959917cd2b86f40a98c28dd5624eddd1fa69d746241c8257eac428d83cb211"
271
+
dependencies = [
272
+
"embedded-io-async",
273
+
"embedded-nal",
274
]
275
276
[[package]]
···
279
source = "registry+https://github.com/rust-lang/crates.io-index"
280
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
281
282
+
[[package]]
283
+
name = "futures-core"
284
+
version = "0.3.31"
285
+
source = "registry+https://github.com/rust-lang/crates.io-index"
286
+
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
287
+
288
+
[[package]]
289
+
name = "futures-sink"
290
+
version = "0.3.31"
291
+
source = "registry+https://github.com/rust-lang/crates.io-index"
292
+
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
293
+
294
[[package]]
295
name = "gpio-cdev"
296
version = "0.6.0"
···
311
"byteorder",
312
]
313
314
+
[[package]]
315
+
name = "heapless"
316
+
version = "0.8.0"
317
+
source = "registry+https://github.com/rust-lang/crates.io-index"
318
+
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
319
+
dependencies = [
320
+
"hash32",
321
+
"stable_deref_trait",
322
+
]
323
+
324
[[package]]
325
name = "heapless"
326
version = "0.9.2"
···
367
checksum = "2a8a605c95f708c78554738a12153b213f107d3bd5323f7ce32d6deb3faafb40"
368
dependencies = [
369
"cast",
370
+
"embedded-hal 1.0.0",
371
"embedded-hal-nb",
372
"gpio-cdev",
373
"i2cdev",
374
+
"nb 1.1.0",
375
"nix 0.27.1",
376
"serialport",
377
"spidev",
378
"sysfs_gpio",
379
]
380
381
+
[[package]]
382
+
name = "litrs"
383
+
version = "1.0.0"
384
+
source = "registry+https://github.com/rust-lang/crates.io-index"
385
+
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
386
+
387
[[package]]
388
name = "mach2"
389
version = "0.4.3"
···
393
"libc",
394
]
395
396
+
[[package]]
397
+
name = "managed"
398
+
version = "0.8.0"
399
+
source = "registry+https://github.com/rust-lang/crates.io-index"
400
+
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
401
+
402
[[package]]
403
name = "memoffset"
404
version = "0.6.5"
···
417
"autocfg",
418
]
419
420
+
[[package]]
421
+
name = "nb"
422
+
version = "0.1.3"
423
+
source = "registry+https://github.com/rust-lang/crates.io-index"
424
+
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
425
+
dependencies = [
426
+
"nb 1.1.0",
427
+
]
428
+
429
[[package]]
430
name = "nb"
431
version = "1.1.0"
···
469
"libc",
470
]
471
472
+
[[package]]
473
+
name = "num-traits"
474
+
version = "0.2.19"
475
+
source = "registry+https://github.com/rust-lang/crates.io-index"
476
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
477
+
dependencies = [
478
+
"autocfg",
479
+
]
480
+
481
[[package]]
482
name = "pin-utils"
483
version = "0.1.0"
···
533
version = "0.1.0"
534
dependencies = [
535
"defmt 1.0.1",
536
+
"heapless 0.9.2",
537
"sachy-fmt",
538
]
539
···
553
version = "0.1.0"
554
dependencies = [
555
"defmt 1.0.1",
556
+
"embedded-hal 1.0.0",
557
"embedded-hal-async",
558
"embedded-hal-mock",
559
"linux-embedded-hal",
560
]
561
562
+
[[package]]
563
+
name = "sachy-sntp"
564
+
version = "0.1.0"
565
+
dependencies = [
566
+
"chrono",
567
+
"defmt 1.0.1",
568
+
"embassy-net",
569
+
"embassy-time",
570
+
"sachy-fmt",
571
+
]
572
+
573
[[package]]
574
name = "scopeguard"
575
version = "1.2.0"
···
601
source = "registry+https://github.com/rust-lang/crates.io-index"
602
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
603
604
+
[[package]]
605
+
name = "smoltcp"
606
+
version = "0.12.0"
607
+
source = "registry+https://github.com/rust-lang/crates.io-index"
608
+
checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
609
+
dependencies = [
610
+
"bitflags 1.3.2",
611
+
"byteorder",
612
+
"cfg-if",
613
+
"heapless 0.8.0",
614
+
"managed",
615
+
]
616
+
617
[[package]]
618
name = "spidev"
619
version = "0.6.1"
···
686
source = "registry+https://github.com/rust-lang/crates.io-index"
687
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
688
689
+
[[package]]
690
+
name = "void"
691
+
version = "1.0.2"
692
+
source = "registry+https://github.com/rust-lang/crates.io-index"
693
+
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
694
+
695
[[package]]
696
name = "windows-sys"
697
version = "0.52.0"
+9
-2
Cargo.toml
+9
-2
Cargo.toml
···
1
[workspace]
2
resolver = "3"
3
-
members = ["sachy-battery","sachy-bthome", "sachy-fmt", "sachy-fnv", "sachy-shtc3"]
4
5
[workspace.package]
6
authors = ["Sachymetsu <sachymetsu@tutamail.com>"]
···
8
repository = "https://tangled.org/sachy.dev/sachy-embed-core/"
9
license = "MIT OR Apache-2.0"
10
version = "0.1.0"
11
-
rust-version = "1.88.0"
···
1
[workspace]
2
resolver = "3"
3
+
members = ["sachy-battery","sachy-bthome", "sachy-fmt", "sachy-fnv", "sachy-shtc3", "sachy-sntp"]
4
5
[workspace.package]
6
authors = ["Sachymetsu <sachymetsu@tutamail.com>"]
···
8
repository = "https://tangled.org/sachy.dev/sachy-embed-core/"
9
license = "MIT OR Apache-2.0"
10
version = "0.1.0"
11
+
rust-version = "1.89.0"
12
+
13
+
[workspace.dependencies]
14
+
embassy-futures = { version = "0.1" }
15
+
embassy-time = { version = "0.5" }
16
+
embassy-sync = { version = "0.7" }
17
+
embassy-net = { version = "0.7" }
18
+
defmt = { version = "1" }
+1
-1
sachy-fmt/Cargo.toml
+1
-1
sachy-fmt/Cargo.toml
+26
sachy-sntp/Cargo.toml
+26
sachy-sntp/Cargo.toml
···
···
1
+
[package]
2
+
name = "sachy-sntp"
3
+
authors.workspace = true
4
+
edition.workspace = true
5
+
repository.workspace = true
6
+
license.workspace = true
7
+
version.workspace = true
8
+
rust-version.workspace = true
9
+
10
+
[dependencies]
11
+
chrono = { version = "0.4.41", default-features = false, optional = true }
12
+
defmt = { workspace = true, optional = true }
13
+
embassy-net = { workspace = true, features = [
14
+
"udp",
15
+
"proto-ipv4",
16
+
"medium-ip",
17
+
"medium-ethernet",
18
+
], optional = true }
19
+
embassy-time = { workspace = true, optional = true }
20
+
sachy-fmt = { path = "../sachy-fmt" }
21
+
22
+
[features]
23
+
default = ["chrono", "embassy-net"]
24
+
chrono = ["dep:chrono"]
25
+
embassy-net = ["dep:embassy-net", "dep:embassy-time"]
26
+
defmt = ["dep:defmt"]
+209
sachy-sntp/src/lib.rs
+209
sachy-sntp/src/lib.rs
···
···
1
+
#![no_std]
2
+
3
+
#[derive(Debug, PartialEq, Eq)]
4
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5
+
pub enum SntpError {
6
+
InvalidPacket,
7
+
InvalidVersion,
8
+
InvalidMode,
9
+
KissOfDeath,
10
+
InvalidTime,
11
+
NetFailure,
12
+
NetTimeout,
13
+
}
14
+
15
+
pub struct SntpRequest;
16
+
17
+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
18
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19
+
/// SNTP timestamp.
20
+
pub struct SntpTimestamp(u64);
21
+
22
+
impl SntpTimestamp {
23
+
pub fn microseconds(&self) -> u64 {
24
+
(self.0 >> 32) * 1_000_000 + ((self.0 & 0xFFFFFFFF) * 1_000_000 / 0x100000000)
25
+
}
26
+
27
+
/// Returns true if the most significant bit is set.
28
+
///
29
+
/// Relevant documentation from RFC 2030:
30
+
///
31
+
/// ```text
32
+
/// Note that, since some time in 1968 (second 2,147,483,648) the most
33
+
/// significant bit (bit 0 of the integer part) has been set and that the
34
+
/// 64-bit field will overflow some time in 2036 (second 4,294,967,296).
35
+
/// Should NTP or SNTP be in use in 2036, some external means will be
36
+
/// necessary to qualify time relative to 1900 and time relative to 2036
37
+
/// (and other multiples of 136 years). There will exist a 200-picosecond
38
+
/// interval, henceforth ignored, every 136 years when the 64-bit field
39
+
/// will be 0, which by convention is interpreted as an invalid or
40
+
/// unavailable timestamp.
41
+
/// As the NTP timestamp format has been in use for the last 17 years,
42
+
/// it remains a possibility that it will be in use 40 years from now
43
+
/// when the seconds field overflows. As it is probably inappropriate
44
+
/// to archive NTP timestamps before bit 0 was set in 1968, a
45
+
/// convenient way to extend the useful life of NTP timestamps is the
46
+
/// following convention: If bit 0 is set, the UTC time is in the
47
+
/// range 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC on 1
48
+
/// January 1900. If bit 0 is not set, the time is in the range 2036-
49
+
/// 2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February
50
+
/// 2036. Note that when calculating the correspondence, 2000 is not a
51
+
/// leap year. Note also that leap seconds are not counted in the
52
+
/// reckoning.
53
+
///```
54
+
pub fn msb_set(&self) -> bool {
55
+
self.0 & (1 << 63) != 0
56
+
}
57
+
58
+
/// Microseconds since the UNIX epoch.
59
+
pub fn utc_micros(&self) -> i64 {
60
+
let ntp_epoch_micros = self.microseconds() as i64;
61
+
let offset: i64 = if self.msb_set() {
62
+
-2208988800000000
63
+
} else {
64
+
2085978496000000
65
+
};
66
+
67
+
ntp_epoch_micros + offset
68
+
}
69
+
70
+
#[cfg(feature = "chrono")]
71
+
pub fn try_to_naive_datetime(self) -> Result<chrono::NaiveDateTime, SntpError> {
72
+
self.try_into()
73
+
}
74
+
}
75
+
76
+
impl SntpRequest {
77
+
pub const SNTP_PACKET_SIZE: usize = 48;
78
+
79
+
pub const fn create_packet() -> [u8; Self::SNTP_PACKET_SIZE] {
80
+
let mut packet = [0u8; Self::SNTP_PACKET_SIZE];
81
+
packet[0] = (3 << 6) | (4 << 3) | 3;
82
+
packet
83
+
}
84
+
85
+
pub fn create_packet_from_buffer(packet: &mut [u8]) -> Result<(), SntpError> {
86
+
if packet.len() != Self::SNTP_PACKET_SIZE {
87
+
return Err(SntpError::InvalidPacket);
88
+
}
89
+
90
+
packet[0] = (3 << 6) | (4 << 3) | 3;
91
+
92
+
Ok(())
93
+
}
94
+
95
+
pub fn read_timestamp(packet: &[u8]) -> Result<SntpTimestamp, SntpError> {
96
+
if packet.len() == Self::SNTP_PACKET_SIZE {
97
+
let header = packet[0];
98
+
let version = (header & 0x38) >> 3;
99
+
100
+
if version != 4 {
101
+
return Err(SntpError::InvalidVersion);
102
+
}
103
+
104
+
let mode = header & 0x7;
105
+
106
+
if !(4..=5).contains(&mode) {
107
+
return Err(SntpError::InvalidMode);
108
+
}
109
+
110
+
let kiss_of_death = packet[1] == 0;
111
+
112
+
if kiss_of_death {
113
+
return Err(SntpError::KissOfDeath);
114
+
}
115
+
116
+
let timestamp = SntpTimestamp(read_be_u64(&packet[40..48]));
117
+
118
+
return Ok(timestamp);
119
+
}
120
+
121
+
Err(SntpError::InvalidPacket)
122
+
}
123
+
124
+
#[cfg(feature = "chrono")]
125
+
pub fn as_naive_datetime(raw_time: SntpTimestamp) -> Result<chrono::NaiveDateTime, SntpError> {
126
+
raw_time.try_into()
127
+
}
128
+
}
129
+
130
+
#[cfg(feature = "chrono")]
131
+
impl TryFrom<SntpTimestamp> for chrono::NaiveDateTime {
132
+
type Error = SntpError;
133
+
134
+
fn try_from(timestamp: SntpTimestamp) -> Result<Self, Self::Error> {
135
+
Ok(chrono::DateTime::<chrono::Utc>::try_from(timestamp)?.naive_utc())
136
+
}
137
+
}
138
+
139
+
#[cfg(feature = "chrono")]
140
+
impl TryFrom<SntpTimestamp> for chrono::DateTime<chrono::Utc> {
141
+
type Error = SntpError;
142
+
143
+
fn try_from(timestamp: SntpTimestamp) -> Result<Self, Self::Error> {
144
+
chrono::DateTime::<chrono::Utc>::from_timestamp_micros(timestamp.utc_micros())
145
+
.ok_or(SntpError::InvalidTime)
146
+
}
147
+
}
148
+
149
+
#[inline]
150
+
fn read_be_u64(input: &[u8]) -> u64 {
151
+
let (int_bytes, _) = input.split_at(core::mem::size_of::<u64>());
152
+
u64::from_be_bytes(int_bytes.try_into().unwrap())
153
+
}
154
+
155
+
#[cfg(feature = "embassy-net")]
156
+
pub trait SntpSocket {
157
+
fn resolve_time(
158
+
&mut self,
159
+
addrs: &[embassy_net::IpAddress],
160
+
) -> impl Future<Output = Result<SntpTimestamp, SntpError>>;
161
+
}
162
+
163
+
#[cfg(feature = "embassy-net")]
164
+
impl SntpSocket for embassy_net::udp::UdpSocket<'_> {
165
+
async fn resolve_time(
166
+
&mut self,
167
+
addrs: &[embassy_net::IpAddress],
168
+
) -> Result<SntpTimestamp, SntpError> {
169
+
use embassy_time::{Duration, WithTimeout};
170
+
use sachy_fmt::{debug, error};
171
+
172
+
if addrs.is_empty() {
173
+
return Err(SntpError::NetFailure);
174
+
}
175
+
176
+
debug!("SNTP address list: {}", addrs);
177
+
178
+
let addr = addrs[0];
179
+
180
+
debug!("Binding to port 123");
181
+
self.bind(123).map_err(|_| SntpError::NetFailure)?;
182
+
183
+
debug!("Sending SNTP request");
184
+
self.send_to_with(
185
+
SntpRequest::SNTP_PACKET_SIZE,
186
+
embassy_net::IpEndpoint::new(addr, 123),
187
+
SntpRequest::create_packet_from_buffer,
188
+
)
189
+
.await
190
+
.map_err(|e| {
191
+
error!("Failed to send: {}", e);
192
+
SntpError::NetFailure
193
+
})??;
194
+
195
+
debug!("Waiting for a response...");
196
+
let res = self
197
+
.recv_from_with(|buf, _from| SntpRequest::read_timestamp(buf))
198
+
.with_timeout(Duration::from_secs(5))
199
+
.await
200
+
.map_err(|_| SntpError::NetTimeout)
201
+
.flatten();
202
+
203
+
debug!("Received: {}", &res);
204
+
debug!("Closing SNTP port...");
205
+
self.close();
206
+
207
+
res
208
+
}
209
+
}