Skip to main content

Modbus RTU Registers - GEOSNAKE C6 Firmware

πŸ“‘ Communication overview​

  • Protocol: Modbus RTU
  • Speed: 9600, 19200, 38400, 57600, 115200 bps (configurable)
  • Format: 8 data, 1 stop, no parity
  • Slave ID: 1-247 (configurable, default: 1)
  • Supported functions: FC03 (Read Holding Registers), FC06 (Write Single Register), FC05 (Write Single Coil)

πŸ”’ Signed data format (signed values)​

Many registers contain negative values (temperature, acceleration, magnetic field, angles). These values are stored in two's complement format:

Signed 32-bit values (2 registers)​

Most measured values (acceleration, magnetic field, angles) are stored as a signed int32 split into 2 registers:

  • High word (upper 16 bits): first register
  • Low word (lower 16 bits): second register

Reconstructing a signed int32:

FUNCTION read_signed_int32(high_reg, low_reg):
value = (high_reg << 16) | low_reg

// Convert to signed (two's complement)
IF value > 0x7FFFFFFF THEN // if bit 31 = 1 (negative number)
value = value - 0x100000000
END IF

RETURN value
END FUNCTION

Example:

  • high=0xFFFF, low=0xFFFE β†’ 0xFFFFFFFE β†’ -2 (signed)
  • high=0x0000, low=0x0064 β†’ 0x00000064 β†’ +100 (signed)

Signed 16-bit values (1 register)​

Some values (e.g. temperature 0x066E) are a signed int16 in a single register:

Signed int16 conversion:

FUNCTION read_signed_int16(reg):
IF reg > 32767 THEN // if bit 15 = 1 (negative number)
RETURN reg - 65536
END IF

RETURN reg
END FUNCTION

Example:

  • 0x09F6 (2550) β†’ +2550 β†’ +25.50Β°C (divided by 100)
  • 0xFBFF (64511) β†’ -1025 β†’ -10.25Β°C (divided by 100)

πŸ—ΊοΈ Register map​

πŸ“Š Basic registers (0x0000-0x000F)​

AddressRegisterDescriptionTypeR/W
0x0000StatusStatus registeruint16R
0x0001FW VersionFirmware versionuint16R
0x0002HW VersionHardware versionuint16R

πŸ”§ Alternative baudrate and address (0x0080-0x0082)​

AddressRegisterDescriptionTypeR/WUnit
0x0080Baudrate HighBaudrate high word (32-bit baudrate)uint16R/Wbps
0x0081Baudrate LowBaudrate low word (32-bit baudrate)uint16R/Wbps
0x0082Device AddressDevice Modbus address (alternative)uint16R/W1-255

Note: Registers 0x0080-0x0082 are an alternative way of setting the baudrate and address. The baudrate is stored as a 32-bit value split into 2 registers. Supported values: 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 bps.

πŸ“Š ADXL355 Accelerometer​

βš™οΈ ADXL355 configuration (0x0600-0x0603)​

AddressRegisterDescriptionTypeR/WUnit
0x0600ADXL StatusADXL355 measurement statusuint16R/W0=idle, 1=start, 2=done
0x0601ADXL PointsNumber of samples per measurementuint16R/W24-1024
0x0602ADXL RangeAccelerometer rangeuint16R/W0=Β±2g, 1=Β±4g, 2=Β±8g
0x0603ADXL ODROutput Data Rateuint16R/WHz
0x0609ADXL Temp RawRaw ADXL355 temperatureuint16Rraw ADC

ADXL355 parameter explanation​

ADXL Status (0x0600):

  • 0 = Idle - no measurement in progress
  • 1 = Start - starts a new measurement
  • 2 = Done - measurement completed, data available

ADXL Points (0x0601):

  • Number of samples for statistical processing (average, minimum, maximum)
  • Range: 24-1024 samples
  • More samples = more accurate results, but a longer measurement
  • Typical value: 100-500 samples

ADXL Range (0x0602):

  • 0 = Β±2g - highest precision, for small accelerations
  • 1 = Β±4g - medium range
  • 2 = Β±8g - largest range, for large accelerations
  • Important: The range affects the conversion of RAW values (see below)

ADXL ODR (0x0603) - Output Data Rate:

  • Sensor sampling frequency in Hz
  • Higher ODR = faster measurement, but higher noise
  • Lower ODR = slower measurement, but lower noise
  • Typical values: 125, 250, 500, 1000, 2000, 4000 Hz

ADXL Temp Raw (0x0609):

  • Raw value from the ADXL355 temperature ADC sensor
  • Conversion formula: Temp_Β°C = (RAW - 1852) / (-9.05)
  • Example: RAW = 1852 β†’ 0Β°C, RAW = 1942 β†’ -10Β°C, RAW = 1762 β†’ +10Β°C
  • For a more accurate temperature use register 0x066E (already converted to Β°C with 0.01Β°C resolution)

πŸ“Š ADXL355 data - Average (0x060A-0x060F)​

AddressRegisterDescriptionTypeR/WUnit
0x060AADXL Avg X HighX-axis average (high byte)int32_hRraw
0x060BADXL Avg X LowX-axis average (low byte)int32_lRraw
0x060CADXL Avg Y HighY-axis average (high byte)int32_hRraw
0x060DADXL Avg Y LowY-axis average (low byte)int32_lRraw
0x060EADXL Avg Z HighZ-axis average (high byte)int32_hRraw
0x060FADXL Avg Z LowZ-axis average (low byte)int32_lRraw

Converting ADXL355 RAW values to g units​

Signed values: Each axis is stored as a signed int32 in 2 registers. See the "Signed data format" section.

The conversion depends on the configured range (register 0x0602):

// First read the range from register 0x0602
range = READ_HOLDING_REGISTER(address=0x0602, slave_id=1)

// Then read the RAW value (signed int32 from 2 registers)
raw_value = read_signed_int32(high_reg, low_reg)

// Conversion by range:
IF range == 0 THEN // Β±2g range
g = raw_value Γ— 3.9e-6
ELSE IF range == 1 THEN // Β±4g range
g = raw_value Γ— 7.8e-6
ELSE IF range == 2 THEN // Β±8g range
g = raw_value Γ— 15.6e-6
END IF

Calculation example:

  • Range = 0 (Β±2g), RAW = 256410 β†’ g = 256410 Γ— 3.9e-6 = 1.00g
  • Range = 1 (Β±4g), RAW = 256410 β†’ g = 256410 Γ— 7.8e-6 = 2.00g
  • Range = 2 (Β±8g), RAW = -128205 β†’ g = -128205 Γ— 15.6e-6 = -2.00g

Relationship between range and resolution:

  • Β±2g: 1 LSB = 3.9 Β΅g (highest resolution)
  • Β±4g: 1 LSB = 7.8 Β΅g
  • Β±8g: 1 LSB = 15.6 Β΅g (lowest resolution)

πŸ“‰ ADXL355 data - Minimum (0x0610-0x0615)​

AddressRegisterDescriptionTypeR/WUnit
0x0610ADXL Min X HighX-axis minimum (high byte)int32_hRraw
0x0611ADXL Min X LowX-axis minimum (low byte)int32_lRraw
0x0612ADXL Min Y HighY-axis minimum (high byte)int32_hRraw
0x0613ADXL Min Y LowY-axis minimum (low byte)int32_lRraw
0x0614ADXL Min Z HighZ-axis minimum (high byte)int32_hRraw
0x0615ADXL Min Z LowZ-axis minimum (low byte)int32_lRraw

Signed values: See the "Signed data format" section. Conversion: See the "Converting ADXL355 RAW values to g units" section above - the conversion depends on the range (0x0602)

πŸ“ˆ ADXL355 data - Maximum (0x0616-0x061B)​

AddressRegisterDescriptionTypeR/WUnit
0x0616ADXL Max X HighX-axis maximum (high byte)int32_hRraw
0x0617ADXL Max X LowX-axis maximum (low byte)int32_lRraw
0x0618ADXL Max Y HighY-axis maximum (high byte)int32_hRraw
0x0619ADXL Max Y LowY-axis maximum (low byte)int32_lRraw
0x061AADXL Max Z HighZ-axis maximum (high byte)int32_hRraw
0x061BADXL Max Z LowZ-axis maximum (low byte)int32_lRraw

Signed values: See the "Signed data format" section. Conversion: See the "Converting ADXL355 RAW values to g units" section above - the conversion depends on the range (0x0602)

🧲 MLX90393 Magnetometer​

βš™οΈ MLX90393 configuration (0x0604-0x0605, 0x0630-0x0632)​

AddressRegisterDescriptionTypeR/WUnit
0x0604MLX StatusMLX90393 measurement statusuint16R/W0=idle, 1=start, 2=done
0x0605MLX PointsNumber of samples per MLX measurementuint16R/W1-1024
0x0630MLX GainSensor gainuint16R/W0-7
0x0631MLX OSROversamplinguint16R/W0-3
0x0632MLX FilterDigital filteruint16R/W0-7

MLX90393 parameter explanation​

MLX Status (0x0604):

  • 0 = Idle - no measurement in progress
  • 1 = Start - starts a new measurement
  • 2 = Done - measurement completed, data available

MLX Points (0x0605):

  • Number of samples for statistical processing (average)
  • Range: 1-1024 samples
  • More samples = more accurate results, but a longer measurement
  • Typical value: 10-100 samples

MLX Gain (0x0630) - Gain:

  • 0 = 1.0x (default gain)
  • 1 = 1.33x
  • 2 = 1.67x
  • 3 = 2.0x
  • 4 = 2.5x
  • 5 = 3.0x
  • 6 = 4.0x
  • 7 = 5.0x
  • Higher gain = higher sensitivity for weak magnetic fields
  • Lower gain = larger measurement range, less saturation

MLX OSR (0x0631) - Oversampling Ratio:

  • 0 = OSR_0 (fastest measurement, highest noise)
  • 1 = OSR_1
  • 2 = OSR_2
  • 3 = OSR_3 (slowest measurement, lowest noise)
  • Higher OSR = better signal-to-noise ratio, but a slower measurement

MLX Filter (0x0632) - Digital filter:

  • 0 = FILTER_0 (no filtering)
  • 1 = FILTER_1
  • 2 = FILTER_2
  • 3 = FILTER_3
  • 4 = FILTER_4
  • 5 = FILTER_5 (default)
  • 6 = FILTER_6
  • 7 = FILTER_7 (maximum filtering)
  • Higher filter = smoother data, but a slower response to changes

🧲 MLX90393 data - Average (0x061C-0x0621)​

AddressRegisterDescriptionTypeR/WUnit
0x061CMLX Avg X HighX-axis average (high byte)int32_hRΒ΅T
0x061DMLX Avg X LowX-axis average (low byte)int32_lRΒ΅T
0x061EMLX Avg Y HighY-axis average (high byte)int32_hRΒ΅T
0x061FMLX Avg Y LowY-axis average (low byte)int32_lRΒ΅T
0x0620MLX Avg Z HighZ-axis average (high byte)int32_hRΒ΅T
0x0621MLX Avg Z LowZ-axis average (low byte)int32_lRΒ΅T

Signed values: Each axis is stored as a signed int32 in 2 registers. See the "Signed data format" section. Units: The values are directly in Β΅T (microtesla), they do not require any further conversion.

πŸ”§ System configuration​

βš™οΈ System registers (0x0622-0x0628)​

AddressRegisterDescriptionTypeR/WValues
0x0622Device AddressDevice Modbus addressuint16R/W1-247
0x0623Baudrate IndexBaudrate indexuint16R/W1-5*
0x0624WiFi StatusWiFi statusuint16R/W0=OFF, 1=ON
0x0625BT StatusBluetooth statusuint16R/W0=OFF, 1=ON
0x0626Restart TriggerDevice restartuint16R/W0=NONE, 1=RESTART
0x0627HTTP StatusHTTP sending statusuint16R/W0=OFF, 1=ON
0x0628Debug LevelDebug output leveluint16R/W0-3*

*Baudrate indexes:

  • 1 = 9600 bps
  • 2 = 19200 bps
  • 3 = 38400 bps
  • 4 = 57600 bps
  • 5 = 115200 bps

*Debug levels:

  • 0 = OFF (no debug messages)
  • 1 = ERRORS (errors only)
  • 2 = INFO (errors + information)
  • 3 = VERBOSE (everything, including details)

πŸ“ˆ ADXL355 multiplied data (0x0650-0x0661)​

AddressRegisterDescriptionTypeR/WUnit
0x0650ADXL Min Mult X HighX minimum multiplied (high)int32_hR-
0x0651ADXL Min Mult X LowX minimum multiplied (low)int32_lR-
0x0652ADXL Min Mult Y HighY minimum multiplied (high)int32_hR-
0x0653ADXL Min Mult Y LowY minimum multiplied (low)int32_lR-
0x0654ADXL Min Mult Z HighZ minimum multiplied (high)int32_hR-
0x0655ADXL Min Mult Z LowZ minimum multiplied (low)int32_lR-
0x0656ADXL Max Mult X HighX maximum multiplied (high)int32_hR-
0x0657ADXL Max Mult X LowX maximum multiplied (low)int32_lR-
0x0658ADXL Max Mult Y HighY maximum multiplied (high)int32_hR-
0x0659ADXL Max Mult Y LowY maximum multiplied (low)int32_lR-
0x065AADXL Max Mult Z HighZ maximum multiplied (high)int32_hR-
0x065BADXL Max Mult Z LowZ maximum multiplied (low)int32_lR-
0x065CADXL Avg Mult X HighX average multiplied (high)int32_hR-
0x065DADXL Avg Mult X LowX average multiplied (low)int32_lR-
0x065EADXL Avg Mult Y HighY average multiplied (high)int32_hR-
0x065FADXL Avg Mult Y LowY average multiplied (low)int32_lR-
0x0660ADXL Avg Mult Z HighZ average multiplied (high)int32_hR-
0x0661ADXL Avg Mult Z LowZ average multiplied (low)int32_lR-

Signed values: See the "Signed data format" section. Multiplied values: These registers contain values in g units multiplied by a factor of 10000000 (10 million) for higher precision during transmission.

  • To get the value in g: g = signed_int32 / 10000000
  • Example: signed_int32 = 10000000 β†’ 1.0g, signed_int32 = -5000000 β†’ -0.5g Note: These values are already in physical g units (not RAW), they do not need conversion by range.

πŸ”„ OTA Firmware Update (0x0300-0x0307, 0x0400+)​

AddressRegisterDescriptionTypeR/WValues
0x0300OTA ControlControl commandsuint16W0=idle, 1=start, 3=end, 4=abort
0x0301OTA StatusCurrent stateuint16R0=idle, 1=ready, 2=writing, 3=success, 4=error
0x0302OTA Size HighFW size (high word)uint16R/Wbytes
0x0303OTA Size LowFW size (low word)uint16R/Wbytes
0x0304OTA Written HighBytes written (high word)uint16Rbytes
0x0305OTA Written LowBytes written (low word)uint16Rbytes
0x0306OTA Error CodeError codeuint16R-
0x0307OTA Chunk SizeMax chunk sizeuint16R240 bytes
0x0400+OTA DataFirmware data (FC16)bytesWFirmware chunks

πŸ“ ADXL355 angles (0x0662-0x066E)​

AddressRegisterDescriptionTypeR/WUnit
0x0662AngleX HighAngleX angle (high byte)int32_hR0.0001Β°
0x0663AngleX LowAngleX angle (low byte)int32_lR0.0001Β°
0x0664AngleY HighAngleY angle (high byte)int32_hR0.0001Β°
0x0665AngleY LowAngleY angle (low byte)int32_lR0.0001Β°
0x0666AngleZ HighAngleZ angle (high byte)int32_hR0.0001Β°
0x0667AngleZ LowAngleZ angle (low byte)int32_lR0.0001Β°
0x0668Pitch Calc HighCalculated pitch (high byte)int32_hR0.0001Β°
0x0669Pitch Calc LowCalculated pitch (low byte)int32_lR0.0001Β°
0x066ARoll Calc HighCalculated roll (high byte)int32_hR0.0001Β°
0x066BRoll Calc LowCalculated roll (low byte)int32_lR0.0001Β°
0x066CTilt Calc HighCalculated tilt (high byte)int32_hR0.0001Β°
0x066DTilt Calc LowCalculated tilt (low byte)int32_lR0.0001Β°
0x066ETemp CelsiusADXL355 temperatureint16R0.01Β°C

Angles (0x0662-0x066D): All angles are a signed int32 (2 registers). See the "Signed data format" section.

  • Registers 0x0662-0x0667: Old angles (angleX/Y/Z)
  • Registers 0x0668-0x066D: New calculated angles (pitch/roll/tilt magnitude)
  • Resolution: 0.0001Β° (divide the signed int32 by 10000)
  • Example: signed_int32 = 450000 β†’ 45.0000Β°, signed_int32 = -123456 β†’ -12.3456Β°

Temperature (0x066E): Signed int16 (1 register), resolution 0.01Β°C. See the "Signed data format - Signed 16-bit values" section.

  • Example: 2550 β†’ +25.50Β°C, -1025 β†’ -10.25Β°C

πŸ†” Device identifiers (0x0700-0x070F)​

AddressRegisterDescriptionTypeR/WUnit
0x0700Chip ID [63:48]Chip ID highest worduint16R-
0x0701Chip ID [47:32]Chip ID high worduint16R-
0x0702Chip ID [31:16]Chip ID low worduint16R-
0x0703Chip ID [15:0]Chip ID lowest worduint16R-
0x0704MAC [47:32]MAC address high worduint16R-
0x0705MAC [31:16]MAC address middle worduint16R-
0x0706MAC [15:0]MAC address low worduint16R-
0x0707CPU CoresNumber of CPU coresuint16Rcores
0x0708CPU FreqCPU frequencyuint16RMHz
0x0709Flash SizeFlash memory sizeuint16RKB
0x070AFree HeapFree RAMuint16RKB
0x070BUptime HighUptime high worduint16Rs
0x070CUptime LowUptime low worduint16Rs
0x070DSketch SizeFirmware sizeuint16RKB
0x070EFree SketchFree space for firmwareuint16RKB
0x070FChip RevisionESP32-C6 chip revisionuint16R-

πŸ“‘ WiFi client IP address (0x0710-0x0713)​

AddressRegisterDescriptionTypeR/WUnit
0x0710WiFi IP [31:24]Client IP address (highest byte)uint16R-
0x0711WiFi IP [23:16]Client IP addressuint16R-
0x0712WiFi IP [15:8]Client IP addressuint16R-
0x0713WiFi IP [7:0]Client IP address (lowest byte)uint16R-

Note: If the device is not connected to WiFi as a client (STA or APSTA), the registers return 0.

πŸ” Supported Modbus functions​

FC03 - Read Holding Registers​

Reading values from registers. All registers in the table above.

Example of reading the device ID:

Request: [01] [03] [07 00] [00 04] [CRC]
ID FC ADDR COUNT CRC
Response: [01] [03] [08] [12 34] [56 78] [9A BC] [DE F0] [CRC]
ID FC BC REG1 REG2 REG3 REG4 CRC

FC06 - Write Single Register​

Writing to selected registers:

AddressRegisterDescriptionRange
0x0080Baudrate HighBaudrate high word32-bit value
0x0081Baudrate LowBaudrate low word32-bit value
0x0082Address AltModbus ID (alternative)1-255
0x0600ADXL ControlStart ADXL355 measurement0=NONE, 1=START
0x0601ADXL PointsADXL355 sample count24-1024
0x0602ADXL RangeAccelerometer range0-2
0x0603ADXL ODROutput Data Rateper sensor
0x0604MLX ControlStart MLX90393 measurement0=NONE, 1=START
0x0605MLX PointsMLX90393 sample count1-1024
0x0622AddressModbus ID1-247
0x0623Baudrate IndexBaudrate index1-5
0x0624WiFiWiFi configuration0=OFF, 1=ON
0x0625BluetoothBluetooth configuration0=OFF, 1=ON
0x0626RestartDevice restart0=NONE, 1=RESTART
0x0627HTTPHTTP sending0=OFF, 1=ON
0x0628Debug LevelDebug level0-3
0x0630MLX GainMLX90393 gain0-7
0x0631MLX OSRMLX90393 oversampling0-3
0x0632MLX FilterMLX90393 filter0-7
0x0300OTA ControlOTA control0/1/3/4
0x0302OTA Size HighOTA size highbytes
0x0303OTA Size LowOTA size lowbytes

Example of setting the baudrate (index):

Request: [01] [06] [06 23] [00 05] [CRC]  ; Set 115200 bps (index 5)
ID FC ADDR VALUE CRC
Response: [01] [06] [06 23] [00 05] [CRC] ; Echo

Example of starting an ADXL355 measurement:

Request: [01] [06] [06 00] [00 01] [CRC]  ; Start measurement
ID FC ADDR VALUE CRC
Response: [01] [06] [06 00] [00 01] [CRC] ; Echo

FC05 - Write Single Coil​

Coil control (start measurement):

AddressCoilDescriptionValues
0x000DStart MeasurementStart ADXL + MLX measurement0x0000=NONE, 0xFF00=START

Example of starting a measurement:

Request: [01] [05] [00 0D] [FF 00] [CRC]  ; Start measurement
ID FC ADDR VALUE CRC
Response: [01] [05] [00 0D] [FF 00] [CRC] ; Echo

Note: FC05 at address 0x0D starts a measurement of both sensors (ADXL355 and MLX90393) at the same time.

πŸ”§ Troubleshooting​

Common errors​

  1. Invalid register address - The register does not exist or is not implemented
  2. Timeout - Check the baudrate and Modbus ID
  3. CRC error - Problems with the communication cable or interference
  4. Invalid Modbus ID - Check the slave address setting

πŸ” Diagnostics​

# Communication test
modpoll -m rtu -b 19200 -p none -s 1 -r 1 -c 1 /dev/ttyUSB0

# Read the device ID (4 registers)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0700 -c 4 /dev/ttyUSB0

# Read the MAC address (3 registers)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0704 -c 3 /dev/ttyUSB0

# Set the baudrate to 115200 (via index)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0623 -c 1 5 /dev/ttyUSB0

# Read the debug level
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0628 -c 1 /dev/ttyUSB0

# Set the debug level to VERBOSE (3)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0628 -c 1 3 /dev/ttyUSB0

# Start an ADXL355 measurement
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0600 -c 1 1 /dev/ttyUSB0

# Read the ADXL355 measurement status
modpoll -m rtu -b 19200 -p none -s 1 -r 0x0600 -c 1 /dev/ttyUSB0

# Read the ADXL355 average values (6 registers X,Y,Z)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x060A -c 6 /dev/ttyUSB0

# Read the MLX90393 average values (6 registers X,Y,Z)
modpoll -m rtu -b 19200 -p none -s 1 -r 0x061C -c 6 /dev/ttyUSB0

⚠️ Safety notes​

  • Device identifiers are read-only
  • Changing the baudrate requires restarting the communication (not restarting the device)
  • Changing the Modbus address (0x0622 or 0x0082) takes effect immediately
  • WiFi/BT changes require a device restart (register 0x0626)
  • Use appropriate timeout values (min. 1000ms)
  • Always verify the current values by reading before writing
  • Registers 0x0080-0x0082 provide alternative access to the baudrate/address

πŸ“‹ Summary of the main addresses​

AreaAddressDescription
Status0x0000-0x0002Status, FW, HW version
Baudrate Alt0x0080-0x0082Alternative baudrate/address
ADXL Config0x0600-0x0603ADXL355 configuration
MLX Config0x0604-0x0605, 0x0630-0x0632MLX90393 configuration
ADXL Data0x060A-0x061BADXL355 average, min, max
MLX Data0x061C-0x0621MLX90393 average
System0x0622-0x0628Address, baudrate, WiFi, BT, HTTP, debug
ADXL Mult0x0650-0x0661ADXL multiplied values
ADXL Angles0x0662-0x066EAngles and temperature
Device ID0x0700-0x070FChip ID, MAC, CPU info
WiFi IP0x0710-0x0713Client IP address
OTA0x0300-0x0307OTA firmware update