Logo [🇩🇪 Deutsch](modbus-registers.de.md) | 🇬🇧 English | [🇫🇷 Français](modbus-registers.fr.md) | [🇪🇸 Español](modbus-registers.es.md) # MODBUS Register Map Definition v1.0 ## 1. Introduction This document defines the MODBUS registers for the universal slave nodes of the irrigation system. ### 1.1. Addressing Philosophy All registers are defined in a single, continuous list per register type (`Input` or `Holding`). A "Category" column logically assigns the function. The addresses are grouped in blocks to leave room for future extensions and to increase readability. * **`0x0000 - 0x000F`**: Valve control & status * **`0x0010 - 0x001F`**: Digital outputs (LEDs / relays) * **`0x0020 - 0x002F`**: Digital inputs (buttons / sensors) * **`0x00F0 - 0x00FF`**: General device configuration & status * **`0x0100 - 0x01FF`**: Firmware update mechanism ### 1.2. Used Function Codes * **`0x03` (Read Holding Registers):** For reading `4xxxx` registers. * **`0x04` (Read Input Registers):** For reading `3xxxx` registers. * **`0x06` (Write Single Register):** For writing a single `4xxxx` register. * **`0x10` (Write Multiple Registers):** For writing multiple `4xxxx` registers at once. ## 2. Input Registers (3xxxx, Read-Only) | Address (hex) | Name | Category | Description | | :--- | :--- | :--- | :--- | | **0x0000** | `VALVE_STATE_MOVEMENT` | Valve | Combined status register. **High-Byte**: Movement (`0`=Idle, `1`=Opening, `2`=Closing, `3`=Error). **Low-Byte**: State (`0`=Closed, `1`=Open). | | **0x0001** | `MOTOR_CURRENT_MA` | Valve | Current motor current in milliamperes (mA). | | **0x0020** | `DIGITAL_INPUTS_STATE` | Inputs | Bitmask of the digital inputs. Bit 0: Input 1, Bit 1: Input 2. `1`=Active. | | **0x0021** | `BUTTON_EVENTS` | Inputs | Event flags for buttons (Clear-on-Read). Bit 0: Button 1 pressed. Bit 1: Button 2 pressed. | | **0x00F0** | `FIRMWARE_VERSION_MAJOR_MINOR` | System | e.g. `0x0102` for v1.2. | | **0x00F1** | `FIRMWARE_VERSION_PATCH` | System | e.g. `3` for v1.2.3. | | **0x00F2** | `DEVICE_STATUS` | System | `0`=OK, `1`=General error. | | **0x00F3** | `UPTIME_SECONDS_LOW` | System | Lower 16 bits of the uptime in seconds. | | **0x00F4** | `UPTIME_SECONDS_HIGH` | System | Upper 16 bits of the uptime. | | **0x0100** | `FWU_LAST_CHUNK_CRC` | Firmware-Update | Contains the CRC16 of the last data chunk received in the buffer. | ## 3. Holding Registers (4xxxx, Read/Write) | Address (hex) | Name | Category | Description | | :--- | :--- | :--- | :--- | | **0x0000** | `VALVE_COMMAND` | Valve | `1`=Open, `2`=Close, `0`=Stop movement. | | **0x0001** | `MAX_OPENING_TIME_S` | Valve | Safety timeout in seconds for the opening process. | | **0x0002** | `MAX_CLOSING_TIME_S` | Valve | Safety timeout in seconds for the closing process. | | **0x0010** | `DIGITAL_OUTPUTS_STATE` | Outputs | Bitmask for reading and writing the outputs. Bit 0: Output 1, Bit 1: Output 2. `1`=ON, `0`=OFF. | | **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout of the fail-safe watchdog in seconds. `0`=Disabled. | | **0x0100** | `FWU_COMMAND` | Firmware-Update | `1`: **Verify Chunk**: The last transmitted chunk was found to be valid by the client. The slave should now write it to flash. `2`: **Finalize Update**: All chunks have been transmitted. Finalize installation and restart. | | **0x0101** | `FWU_CHUNK_OFFSET_LOW` | Firmware-Update | Lower 16 bits of the 32-bit offset to which the next chunk is to be written. | | **0x0102** | `FWU_CHUNK_OFFSET_HIGH` | Firmware-Update | Upper 16 bits of the 32-bit offset. | | **0x0103** | `FWU_CHUNK_SIZE` | Firmware-Update | Size of the next chunk in bytes (max. 256). | | **0x0180** | `FWU_DATA_BUFFER` | Firmware-Update | **Start address** of a 128x16-bit buffer (256 bytes). Corresponds to registers `40384` to `40511`. | ## 4. Detailed Firmware Update Process This process is stateless and robust against transmission errors. 1. **Client:** Selects a chunk (max. 256 bytes) from the firmware file and calculates its CRC16. 2. **Client:** Writes the target offset (e.g. `0`) to `FWU_CHUNK_OFFSET_...` and the size to `FWU_CHUNK_SIZE`. 3. **Client:** Writes the chunk data to the `FWU_DATA_BUFFER` (from address `0x0180`). 4. **Slave:** Receives the data, places it in the RAM buffer and calculates the CRC. The result is provided in `FWU_LAST_CHUNK_CRC` (`30256`). 5. **Client:** Reads `FWU_LAST_CHUNK_CRC` and compares the value with the self-calculated CRC. * **Error:** Go back to step 3 to send the same chunk again. * **Success:** Continues with the next step. 6. **Client:** Writes the command `1` ("Verify Chunk") to `FWU_COMMAND` (`40256`). 7. **Slave:** Receives the command, takes the verified chunk from the RAM buffer and writes it to the correct location in the flash memory. 8. **Client:** Continues with the next chunk (back to step 1 with new offset and data). 9. **Last Chunk:** After the last chunk has been transferred and written to flash with command `1`, the client writes the command `2` ("Finalize Update") to `FWU_COMMAND`. 10. **Slave:** Performs final checks and restarts so that MCUBoot can perform the installation. ## Appendix: QDY30A Level Sensor Registers These registers belong to the external level sensor and can also be addressed on the bus. According to the manufacturer, these are Holding Registers (`4xxxx`) that are read with function code `0x03`. | Address (hex) | Name | R/W | Description | | :--- | :--- | :-- | :--- | | **0x0000** | `NODE_ADDRESS` | R/W | Device address of the sensor (1-255). | | **0x0001** | `BAUDRATE` | R/W | `0`=1200, `1`=2400, `2`=4800, `3`=9600, `4`=19200, `5`=38400, `6`=57600, `7`=115200. | | **0x0002** | `UNIT` | R/W | `0`=None, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. | | **0x0003** | `DECIMAL_PLACES` | R/W | Number of decimal places for the measured value (0-3). | | **0x0004** | `CURRENT_MEASUREMENT` | R | The scaled measured value as a signed 16-bit integer. | | **0x0005** | `MEASURING_RANGE_ZERO_POINT` | R/W | Raw value for the zero point of the scale. | | **0x0006** | `MEASURING_RANGE_END_POINT` | R/W | Raw value for the end point of the scale. |