Modbus RTU (Example of load cell module)

1) Frame structure (RTU)

Field Size Notes
Address 1 byte Slave ID: 1…247 (0 = broadcast; 248–255 reserved).
Function code 1 byte e.g., 0x03 Read Holding Regs; 0x06 Write Single Reg.
Data 0…252 B Depends on function: start address, quantity, payload, byte count…
CRC-16 2 bytes Little-endian on the wire: CRC low byte first, then high byte. Polynomial 0xA001, init 0xFFFF.

Framing by silence:3.5 character times between frames; ≤ 1.5 character times between bytes within a frame.


2) Common function codes

Code Meaning
0x01 Read Coils
0x02 Read Discrete Inputs
0x03 Read Holding Registers
0x04 Read Input Registers
0x05 Write Single Coil
0x06 Write Single Register
0x0F Write Multiple Coils
0x10 Write Multiple Registers

3) Bit & byte ordering

Bit significance (LSB/MSB) is within a single byte.

Example: 0x0B = 0000 1011₂ → LSB (bit 0) = 1, MSB (bit 7) = 0.
Endianness talks about the order of bytes when storing or sending a multi-byte value (16-bit, 32-bit, etc.). It is not about the order of bits inside a byte.

Endianness definitions (byte order for multi-byte values)

Big-endian (“network order”)

Stores/sends the most significant byte first (the “big end”).
16-bit example: value 0x1234 → bytes on the wire/in memory: 12 34
32-bit example: 0x12345678 → 12 34 56 78

Little-endian

Stores/sends the least significant byte first (the “little end”).
16-bit example: 0x1234 → 34 12
32-bit example: 0x12345678 → 78 56 34 12

Bit, Byte, Word(Modbus register) (don’t mix them!)

Bit: 1 or 0.
Byte: 8 bits (e.g., 0x0B = 00001011₂).
Word (Modbus register): 16 bits = 2 bytes.
Double word (DWORD): 32 bits = 2 words = 4 bytes (uses two consecutive registers).
Quad word (QWORD): 64 bits = 4 words = 8 bytes (four consecutive registers).

Example (32-bit unsigned 0x12345678)

High-word first:
- Register N (high word) = 0x1234 → bytes 12 34
- Register N+1 (low word) = 0x5678 → bytes 56 78
- On the wire (data payload): 12 34 56 78
Low-word first (word-swap):
- Register N (low word) = 0x5678 → bytes 56 78
- Register N+1 (high word) = 0x1234 → bytes 12 34
- On the wire (data payload): 56 78 12 34

4) Hex/Bin/Dec + two’s complement refresher

4.1 Quick Hex–Binary–Decimal

Dec 8-bit Binary Hex
0 0000 0000 00
10 0000 1010 0A
15 0000 1111 0F
16 0001 0000 10
255 1111 1111 FF

Notation: 0x..=hex, 0b..=binary.

4.2 Two’s complement (signed integers)

  • Positive N: store normal binary.
  • Negative −N: invert all bits of N, then add 1 (width-fixed).

8-bit example:
+5 = 0000 0101 → invert → 1111 1010 → +1 → 1111 1011 = −5

32-bit example:
+5 = 00000000 00000000 00000000 00000101
invert → 11111111 11111111 11111111 11111010
+1 → 11111111 11111111 11111111 11111011 = −5

If your device returns an unsigned 32-bit that actually encodes a signed value, then when MSB=1:
signed = unsigned − 2^32.


5) 32-bit example (value 500)

500(dec) = 0x000001F4

  • High-word first (big-endian words): 00 00 01 F4
  • Low-word first (word-swapped): 01 F4 00 00 ← common on some devices

Your note 01 F4 00 00 is consistent with “low-word first.”


6) Standard request/response templates

6.1 Read Holding Registers (0x03)

Request

1
[Addr][03][StartHi][StartLo][QtyHi][QtyLo][CRCLo][CRCHi]

Response

1
[Addr][03][ByteCount][(Reg1Hi)(Reg1Lo)...(RegNHi)(RegNLo)][CRCLo][CRCHi]

ByteCount = 2*N registers.

6.2 Write Single Register (0x06)

Request

1
[Addr][06][RegHi][RegLo][DataHi][DataLo][CRCLo][CRCHi]

Response
Echoes the request.


7) Example of load-cell frames

A) Activate setup (close write protection)

1
01 06 00 17 00 01 F8 0E
  • Addr 01, Func 06
  • Reg 0x0017 ← data 0x0001
  • CRC = 0x0EF8 on wire as F8 0E

B) Deactivate setup (open write protection)

1
01 06 00 17 00 00 39 CE
  • CRC = 0xCE39 on wire as 39 CE

C) Read load cell (2 regs from 0x0000)

Request

1
01 03 00 00 00 02 C4 0B
  • CRC = 0x0BC4 on wire as C4 0B

Example response

1
01 03 04 12 5D 00 00 6E 99
  • ByteCount 04 → 2 registers
  • Data words: 0x125D and 0x0000
  • If device uses low-word first for 32-bit: bytes 12 5D 00 000x0000125D = 4701.
  • CRC = 0x996E on wire as 6E 99

32-bit word order is device-specific; for non-zero high words, the wrong order will scramble the value.


8) Calibration sequence

Registers are device-specific. Below uses your notes; confirm against the module manual.

Step Meaning Frame (hex) Notes
1 Close write protection (enter setup) 01 06 00 17 00 01 F8 0E Enables config mode
2 Zero (no-load) calibration 01 06 00 16 00 01 ?? ?? 0x0016 with 0x0001 (CRC depends on bytes)
3 Span with 500 g placed 01 06 00 06 01 F4 ?? ?? 0x01F4 = 500 decimal
4 Open write protection (exit) 01 06 00 17 00 00 39 CE Back to normal

9) “Check parameters” request

Request (read 17 regs from 0x0007)

1
01 03 00 07 00 11 34 07
  • CRC = 0x0734 on wire as 34 07

Response header

1
01 03 22 ...
  • 0x22 = 34 data bytes = 17 × 2 (as expected).

10) ESP32C6 + TFT + load-cell wiring

Your original pin list looked like mixed header labels. Here’s a clean mapping you can actually wire.

10.1 Load-cell module (UART/TTL)

  • Module TX → ESP32C6 GPIO17 (RX)
  • Module RX → ESP32C6 GPIO16 (TX)
  • GND ↔ GND; power per module spec.

If the module speaks RS-485, add a transceiver (e.g., MAX485/THVD-series) and control DE/RE; TTL direct wiring won’t work.

10.2 ST7735 TFT (SPI) + 4 keys (example pins)

TFT pin ESP32C6 pin (example) Note
VCC 3V3 Supply
GND GND Ground
SCL (SPI_CLK) GPIO19 SPI SCK
SDA (SPI_MOSI) GPIO18 SPI MOSI
CS GPIO21 Chip-select
DC GPIO2 Data/Command
RST GPIO1 Reset (or tie via RC)
BLK (PWM-capable GPIO) Backlight (tie high or PWM)
K1,K2,K3,K4 (keys) **GPIO0/23/20/22 Any free GPIOs with pull-ups/downs as needed

ESP32-class chips let you remap SPI/UART pins; feel free to choose others if these clash.


11) Battery ADC note (A6 mapping)

  • If you map 0–2.1 V → 0–4.2 V, you’re likely using a ~2:1 divider ahead of the ADC.
  • Ensure the ADC pin never exceeds its absolute max (and set attenuation appropriately).
  • Calibrate: V_batt ≈ ADC_volts × (R_top + R_bottom) / R_bottom.

12) Common pitfalls

  • CRC order: always send CRC Low byte first, then High.
  • Word order for 32-bit: not in the spec—confirm high-word-first vs low-word-first.
  • Timing: respect ≥3.5 char silent gap between frames (and ≤1.5 char gaps within).
  • Broadcast address (0): slaves do not reply.
  • Register numbering: doc “40001” ≠ wire 0x40001; it’s usually wire offset 0x0000.

Appendix A) Tiny CRC16(Modbus) snippet (optional)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def crc16_modbus(data: bytes) -> int:
crc = 0xFFFF
for b in data:
crc ^= b
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc # integer 0..0xFFFF

# Example:
# frame = bytes.fromhex("01 03 00 00 00 02")
# crc = crc16_modbus(frame)
# print(f"{crc & 0xFF:02X} {crc >> 8:02X}") # prints "C4 0B"
打赏
  • © 2020-2025 Yu Xia
  • Powered by Hexo Theme Ayer
    • PV:
    • UV:

Buy me a cup of coffee~

支付宝
微信