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