1#![no_std]
2pub trait RegisterRead {
5 fn get(&self, addr: u16) -> u16;
6 fn is_valid(&self, addr: u16) -> bool;
7}
8pub trait BitRead {
10 fn get(&self, addr: u16) -> bool;
11 fn is_valid(&self, addr: u16) -> bool;
12}
13
14pub struct Coil<const N: usize> {
16 regs: [bool; N],
17}
18impl<const N: usize> Coil<N> {
19 pub const fn new() -> Self {
20 Self { regs: [false; N] }
21 }
22 pub fn set_bit(&mut self, addr: u16, val: bool) {
23 let i = addr as usize;
24 if i < N {
25 self.regs[i] = val;
26 }
27 }
28 pub fn as_bits(&self) -> &[bool] {
29 &self.regs
30 }
31}
32impl<const N: usize> BitRead for Coil<N> {
33 fn get(&self, addr: u16) -> bool {
34 let i = addr as usize;
35 if i < N {
36 self.regs[i]
37 } else {
38 false
39 }
40 }
41 fn is_valid(&self, addr: u16) -> bool {
42 (addr as usize) < N
43 }
44}
45
46pub struct Ists<const N: usize> {
48 regs: [bool; N],
49}
50impl<const N: usize> Ists<N> {
51 pub const fn new() -> Self {
52 Self { regs: [false; N] }
53 }
54 pub fn set_bit(&mut self, addr: u16, val: bool) {
55 let i = addr as usize;
56 if i < N {
57 self.regs[i] = val;
58 }
59 }
60}
61impl<const N: usize> BitRead for Ists<N> {
62 fn get(&self, addr: u16) -> bool {
63 let i = addr as usize;
64 if i < N {
65 self.regs[i]
66 } else {
67 false
68 }
69 }
70 fn is_valid(&self, addr: u16) -> bool {
71 (addr as usize) < N
72 }
73}
74
75pub struct Ireg<const N: usize> {
77 regs: [u16; N],
78}
79impl<const N: usize> Ireg<N> {
80 pub const fn new() -> Self {
81 Self { regs: [0; N] }
82 }
83 pub fn set(&mut self, addr: u16, val: u16) {
84 let i = addr as usize;
85 if i < N {
86 self.regs[i] = val;
87 }
88 }
89}
90impl<const N: usize> RegisterRead for Ireg<N> {
91 fn get(&self, addr: u16) -> u16 {
92 let i = addr as usize;
93 if i < N {
94 self.regs[i]
95 } else {
96 0
97 }
98 }
99 fn is_valid(&self, addr: u16) -> bool {
100 (addr as usize) < N
101 }
102}
103
104pub struct Hreg<const N: usize> {
106 regs: [u16; N],
107}
108
109impl<const N: usize> Hreg<N> {
110 pub const fn new() -> Self {
111 Self { regs: [0; N] }
112 }
113
114 pub fn set(&mut self, addr: u16, val: u16) {
115 let i = addr as usize;
116 if i < N {
117 self.regs[i] = val;
118 }
119 }
120
121 pub fn as_slice(&self) -> &[u16] {
122 &self.regs
123 }
124}
125
126impl<const N: usize> RegisterRead for Hreg<N> {
128 fn get(&self, addr: u16) -> u16 {
129 let i = addr as usize;
130 if i < N {
131 self.regs[i]
132 } else {
133 0
134 }
135 }
136 fn is_valid(&self, addr: u16) -> bool {
137 (addr as usize) < N
138 }
139}
140
141static mut SEED: u32 = 0x1234_5678;
147
148#[inline]
149fn xorshift32_next() -> u32 {
150 unsafe {
152 let mut x = SEED;
153 x ^= x << 13;
154 x ^= x >> 17;
155 x ^= x << 5;
156 SEED = x;
157 x
158 }
159}
160
161pub fn random(start_val: u16, end_val: u16) -> u16 {
163 let (lo, hi) = if start_val <= end_val {
164 (start_val, end_val)
165 } else {
166 (end_val, start_val)
167 };
168
169 let span = (hi as u32).wrapping_sub(lo as u32).wrapping_add(1);
170 let r = xorshift32_next() % span;
171 (lo as u32 + r) as u16
172}
173
174#[derive(Clone, Copy, Debug)]
175pub struct Req03 {
176 pub unit_id: u8,
177 pub start_addr: u16,
178 pub quantity: u16,
179}
180
181pub mod exc {
182 pub const ILLEGAL_FUNCTION: u8 = 0x01;
183 pub const ILLEGAL_DATA_ADDRESS: u8 = 0x02;
184 pub const ILLEGAL_DATA_VALUE: u8 = 0x03;
185}
186
187pub fn crc16_modbus(data: &[u8]) -> u16 {
189 let mut crc: u16 = 0xFFFF;
190 for &b in data {
191 crc ^= b as u16;
192 for _ in 0..8 {
193 if (crc & 0x0001) != 0 {
194 crc = (crc >> 1) ^ 0xA001;
195 } else {
196 crc >>= 1;
197 }
198 }
199 }
200 crc
201}
202
203pub fn build_resp_bit_reads<const MAX_QTY: usize, B: BitRead>(
210 out: &mut [u8],
211 unit_id: u8,
212 func: u8, start_addr: u16,
214 quantity: u16,
215 bits: &B,
216) -> usize {
217 let qty = quantity as usize;
218 let byte_cnt = (qty + 7) / 8;
219
220 out[0] = unit_id;
221 out[1] = func;
222 out[2] = byte_cnt as u8;
223
224 for i in 0..byte_cnt {
226 out[3 + i] = 0;
227 }
228
229 for j in 0..qty {
230 let addr = start_addr.wrapping_add(j as u16);
231 let bit = bits.get(addr);
232 if bit {
233 let byte_i = j / 8;
234 let bit_i = j % 8;
235 out[3 + byte_i] |= 1u8 << bit_i; }
237 }
238
239 let body_len = 3 + byte_cnt;
240 let crc = crc16_modbus(&out[..body_len]);
241 out[body_len] = (crc & 0xFF) as u8;
242 out[body_len + 1] = (crc >> 8) as u8;
243 body_len + 2
244}
245
246pub fn build_resp04<const MAX_QTY: usize, R: RegisterRead>(
248 out: &mut [u8],
249 unit_id: u8,
250 start_addr: u16,
251 quantity: u16,
252 regs: &R,
253) -> usize {
254 let qty = quantity as usize;
256
257 out[0] = unit_id;
258 out[1] = 0x04;
259 out[2] = (qty as u8) * 2;
260
261 for i in 0..qty {
262 let addr = start_addr.wrapping_add(i as u16);
263 let v = regs.get(addr);
264 let base = 3 + i * 2;
265 out[base] = (v >> 8) as u8; out[base + 1] = (v & 0xFF) as u8;
267 }
268
269 let body_len = 3 + qty * 2;
270 let crc = crc16_modbus(&out[..body_len]);
271 out[body_len] = (crc & 0xFF) as u8;
272 out[body_len + 1] = (crc >> 8) as u8;
273 body_len + 2
274}
275
276pub fn parse_req03(frame: &[u8; 8]) -> Option<Req03> {
278 if frame[1] != 0x03 {
279 return None;
280 }
281 let expected_crc = u16::from_le_bytes([frame[6], frame[7]]);
282 let calc_crc = crc16_modbus(&frame[..6]);
283 if expected_crc != calc_crc {
284 return None;
285 }
286
287 let start_addr = u16::from_be_bytes([frame[2], frame[3]]);
288 let quantity = u16::from_be_bytes([frame[4], frame[5]]);
289
290 Some(Req03 {
291 unit_id: frame[0],
292 start_addr,
293 quantity,
294 })
295}
296
297pub fn build_resp03<const MAX_QTY: usize, R: RegisterRead>(
299 out: &mut [u8],
300 unit_id: u8,
301 start_addr: u16,
302 quantity: u16,
303 regs: &R,
304) -> usize {
305 let qty = quantity as usize;
306 out[0] = unit_id;
308 out[1] = 0x03;
309 out[2] = (qty as u8) * 2;
310
311 for i in 0..qty {
312 let addr = start_addr.wrapping_add(i as u16);
313 let v = regs.get(addr);
314 let base = 3 + i * 2;
315 out[base] = (v >> 8) as u8; out[base + 1] = (v & 0xFF) as u8;
317 }
318
319 let body_len = 3 + qty * 2;
320 let crc = crc16_modbus(&out[..body_len]);
321 out[body_len] = (crc & 0xFF) as u8; out[body_len + 1] = (crc >> 8) as u8; body_len + 2
324}
325
326pub fn build_exception_resp<const BUF: usize>(
328 out: &mut [u8; BUF],
329 unit_id: u8,
330 function_exception: u8, exception_code: u8,
332) -> usize {
333 out[0] = unit_id;
334 out[1] = function_exception;
335 out[2] = exception_code;
336
337 let crc = crc16_modbus(&out[..3]);
338 out[3] = (crc & 0xFF) as u8;
339 out[4] = (crc >> 8) as u8;
340 5
341}
342
343pub struct ModbusCtx<'a, H, I, C, D> {
345 pub holdings: &'a mut H, pub inputs: &'a mut I, pub coils: &'a mut C, pub ists: &'a mut D, }
350
351impl<'a, H, I, C, D> ModbusCtx<'a, H, I, C, D>
352where
353 H: RegisterRead,
354 I: RegisterRead,
355 C: BitRead,
356 D: BitRead,
357{
358 pub fn pharse_pdu<const MAX_QTY: usize>(
363 &self,
364 req8: &[u8; 8],
365 out_tx: &mut [u8],
366 out_exc: &mut [u8; 5],
367 ) -> usize {
368 let unit_id = req8[0];
369 let func = req8[1];
370
371 let expected_crc = u16::from_le_bytes([req8[6], req8[7]]);
373 let calc_crc = crc16_modbus(&req8[..6]);
374 if expected_crc != calc_crc {
375 return build_exception_resp_fixed::<5>(
377 out_exc,
378 unit_id,
379 func | 0x80,
380 exc::ILLEGAL_DATA_VALUE,
381 );
382 }
383
384 let start_addr = u16::from_be_bytes([req8[2], req8[3]]);
386 let quantity = u16::from_be_bytes([req8[4], req8[5]]);
387
388 if quantity == 0 || (quantity as usize) > MAX_QTY {
390 return build_exception_resp_fixed::<5>(
391 out_exc,
392 unit_id,
393 func | 0x80,
394 exc::ILLEGAL_DATA_VALUE,
395 );
396 }
397
398 match func {
399 0x01 => {
400 let end = start_addr.wrapping_add(quantity.saturating_sub(1));
402 if !self.coils.is_valid(start_addr) || !self.coils.is_valid(end) {
403 return build_exception_resp_fixed::<5>(
404 out_exc,
405 unit_id,
406 func | 0x80,
407 exc::ILLEGAL_DATA_ADDRESS,
408 );
409 }
410 build_resp_bit_reads::<MAX_QTY, _>(
411 out_tx, unit_id, 0x01, start_addr, quantity, self.coils,
412 )
413 }
414
415 0x02 => {
416 let end = start_addr.wrapping_add(quantity.saturating_sub(1));
418 if !self.ists.is_valid(start_addr) || !self.ists.is_valid(end) {
419 return build_exception_resp_fixed::<5>(
420 out_exc,
421 unit_id,
422 func | 0x80,
423 exc::ILLEGAL_DATA_ADDRESS,
424 );
425 }
426 build_resp_bit_reads::<MAX_QTY, _>(
427 out_tx, unit_id, 0x02, start_addr, quantity, self.ists,
428 )
429 }
430
431 0x03 => {
432 let end = start_addr.wrapping_add(quantity.saturating_sub(1));
434 if !self.holdings.is_valid(start_addr) || !self.holdings.is_valid(end) {
435 return build_exception_resp_fixed::<5>(
436 out_exc,
437 unit_id,
438 func | 0x80,
439 exc::ILLEGAL_DATA_ADDRESS,
440 );
441 }
442 build_resp_regs::<MAX_QTY, _>(
443 out_tx,
444 unit_id,
445 0x03,
446 start_addr,
447 quantity,
448 self.holdings,
449 )
450 }
451
452 0x04 => {
453 let end = start_addr.wrapping_add(quantity.saturating_sub(1));
455 if !self.inputs.is_valid(start_addr) || !self.inputs.is_valid(end) {
456 return build_exception_resp_fixed::<5>(
457 out_exc,
458 unit_id,
459 func | 0x80,
460 exc::ILLEGAL_DATA_ADDRESS,
461 );
462 }
463 build_resp_regs::<MAX_QTY, _>(
464 out_tx,
465 unit_id,
466 0x04,
467 start_addr,
468 quantity,
469 self.inputs,
470 )
471 }
472
473 _ => {
474 build_exception_resp_fixed::<5>(
476 out_exc,
477 unit_id,
478 func | 0x80,
479 exc::ILLEGAL_FUNCTION,
480 )
481 }
482 }
483 }
484}
485
486fn build_exception_resp_fixed<const BUF: usize>(
488 out_exc: &mut [u8; 5],
489 unit_id: u8,
490 function_exception: u8,
491 exception_code: u8,
492) -> usize {
493 out_exc[0] = unit_id;
494 out_exc[1] = function_exception;
495 out_exc[2] = exception_code;
496
497 let crc = crc16_modbus(&out_exc[..3]);
498 out_exc[3] = (crc & 0xFF) as u8;
499 out_exc[4] = (crc >> 8) as u8;
500 5
501}
502fn build_resp_regs<const MAX_QTY: usize, R: RegisterRead>(
505 out_tx: &mut [u8],
506 unit_id: u8,
507 func: u8,
508 start_addr: u16,
509 quantity: u16,
510 regs: &R,
511) -> usize {
512 let qty = quantity as usize;
513
514 out_tx[0] = unit_id;
515 out_tx[1] = func;
516 out_tx[2] = (qty as u8) * 2;
517
518 for i in 0..qty {
519 let addr = start_addr.wrapping_add(i as u16);
520 let v = regs.get(addr);
521 let base = 3 + i * 2;
522 out_tx[base] = (v >> 8) as u8;
523 out_tx[base + 1] = (v & 0xFF) as u8;
524 }
525
526 let body_len = 3 + qty * 2;
527 let crc = crc16_modbus(&out_tx[..body_len]);
528 out_tx[body_len] = (crc & 0xFF) as u8;
529 out_tx[body_len + 1] = (crc >> 8) as u8;
530
531 body_len + 2
532}