SISCALL PRO
Hardware & Firmware Reference Microcontroller Firmware & Modbus Specification
📖 Full Index Spec 📊 Register Map đŸ—ī¸ Architecture ⚡ Action Codes 📖 Register Ref đŸ’ģ Complete C Code

Siscall Pro — Microcontroller Firmware & Logic Specification

Universal engineering reference for hardware developers, PCB designers, and embedded firmware engineers. Provides the complete Modbus RTU protocol, register memory map, discrete 4-LED action engine, autonomous local call clear state machine, and ready-to-flash C source code.

Protocol: Modbus RTU over RS-485 Default Baud: 19200 bps (8-N-1) Transaction Latency: ~25 ms / unit (FC 03) Bus Cycle: ~250–750 ms (10–30 units) Host Controller: Raspberry Pi Gateway (Master)

đŸ—ī¸ 1. Hardware Architecture & Discrete LED Integration

System Division of Responsibilities:
The Wall Unit microcontroller firmware handles physical button debouncing, drives local discrete LEDs/buzzer via EEPROM Action Codes, executes instant 0ms local resets on Cancel keypress, and responds to Modbus RTU packets within ~25ms. The host SBC (Raspberry Pi) orchestrates hospital ward routing, escalations, MQTT, mobile notifications, database logging, and Corridor Over-Door Dome lights.

1.1 Discrete 4-LED Functional Architecture & Dynamic Call Mapping

On discrete 4-LED Wall Units, the 4 LEDs reflect the Active Call State / Functional Role of the station:

  • LED 1 (Status): Solid Green / Heartbeat → Station Online & Standby.
  • LED 2 (ACK / Hold): Solid Cyan/Green → Care in progress / Call acknowledged.
  • LED 3 (Shared Call): Solid/Blink Yellow → Standard Call (Nurse Call, Extra Help, Assist).
  • LED 4 (Code Blue): Rapid Red Flash → Critical Emergency / Code Blue (Priority 1).

Microcontroller Execution: When any button is pressed, the firmware looks up that button's configured Action Bitmask (Regs 40011–40020, 40031–40038) and Priority Rank (Regs 40061–40076). If configured as Priority 1 (Code Blue), pressing that button activates the Code Blue LED. If configured as Priority 2..4, it activates the Shared Call LED.

1.2 Call Clearing & Local Reset State Machine

Call clearing is implemented as Autonomous Local Clear (0ms Latency) + SBC State Synchronization:

  • Local Immediate Action: When the Cancel/Clear key (assigned Action Code 0x0001 CLEAR_ALL) is pressed on the wall unit, the MCU immediately turns OFF Call/Code Blue LEDs and stops the buzzer locally (0ms lag).
  • SBC Polling Synchronization: The MCU clears its internal call state and latches the clear event bit. On the next bus poll cycle (~250–750ms), the SBC detects the cleared state and synchronizes the central dashboard, MQTT broker, and Corridor Dome light.
  • Remote Clear Support: If a call is cancelled remotely from the Nurse Station dashboard, the SBC sends Modbus FC 06 Write Reg 40010 = 0x0001 (Remote Clear Command) to the wall unit, and the MCU turns off its LEDs and mutes buzzer.

1.3 Corridor Over-Door Dome LED Commissioning & Discovery

Corridor Door Lights share the same zero-collision discovery architecture as wall units (see index.html Section 7 and registers.html Section 8):

  • Factory Default Slave ID: Uncommissioned domes ship at Slave ID 255 (0x00FF) with Reg 40254 = 0.
  • Discovery Indication: Flashes White / Cyan at 2.5 Hz when Gateway broadcasts Discovery command (Reg 40255 = 0x00AA / 0x00FF).
  • Pairing Trigger: â€ĸ Hardware Trigger (Primary): Holding internal PCB service button or external magnetic reed switch for 3.0s → Dome turns Solid Yellow and sets Reg 40253 = 1.
    â€ĸ Sequential Isolated Mode (Fallback): For dome fixtures without accessible buttons, attaching new uncommissioned domes one at a time on an isolated bus allows automatic discovery at address 255 without physical button access.
  • Address Assignment: Gateway polls address 255 for Reg 40253 == 1, writes assigned Slave ID via Register 255 (40256), dome echoes reply, burns to 8-byte EEPROM, illuminates Solid Green for 3.0s, and Gateway configures Cycle Interval (40011) and Idle Glow Color (40012).

⚡ 2. System Action Bitmasks (Registers 40011 to 40020, 40031 to 40038)

Each button press event is configured with an Action Bitmask (value = ∑ (1 << code)) stored in EEPROM. Valid action range is 0x00 to 0x7F (bits 0–6). Writes above 0x7F are rejected with Modbus Exception 03 (Illegal Data Value). The following combinations are invalid and must be blocked by the configuring tool: CLEAR_ALL + call bits, ACKNOWLEDGE + call bits, and CALL_SOLID + CALL_BLINK. Should such a value already be present in EEPROM from an earlier configuration, firmware resolves it deterministically: CALL_SOLID + CALL_BLINK renders BLINK; CLEAR_ALL + call bits performs the clear and ignores the call bits; ACKNOWLEDGE + call bits registers the call and ignores the acknowledge.

Action Bitmask Action Name Hardware LED Action Buzzer Behavior
0x0000 (0) DISABLED / NO_ACTION Button press ignored; no call registered, no LED change. No buzzer activation.
0x0001 (1) CLEAR_ALL (Reset Action) Turns OFF all call LEDs, turns OFF ACK LED, returns to idle. Mutes / Stops buzzer completely.
0x0002 (2) CALL_SOLID (Standard Call) Turns ON assigned button LED Solid with configured color. Standard periodic beeper (500ms ON / 500ms OFF).
0x0004 (4) ACKNOWLEDGE (Care Started) Turns ON ACK (Hold) LED Solid; active call stays solid. Instantly mutes call buzzer.
0x0008 (8) MUTE_BUZZER (Snooze) Leaves active LEDs illuminated as-is; sets muted = true on active call. Mutes local buzzer sound only; visual call LEDs remain active. Resets on new higher call or clear.
0x0010 (16) STAFF_CHECKIN (Attendance) Turns ON confirmation LED for 3 seconds, then auto-clears. 1 short 100ms chirp confirmation beep.
0x0020 (32) CALL_BLINK (Urgent / Blink) Blinks assigned button LED continuously with configured color. Continuous emergency alarm sound.
0x0040 (64) MULTI_CALL_CYCLE (Modifier) Modifier bit (combine with 0x0002 or 0x0020; not a standalone action). Alternates active call colors in sequence (Bed 1 → Bed 2 → Bed 1). Periodic beeper sound.

💡 Common Multi-Action Bitmask Combinations (∑ 2code)

  • Muted Silent Call (Wall / Handset SP - Reg 40011 / 40031): CALL_SOLID (2) + MUTE_BUZZER (8) = 10 (0x000A) → Lights visual LED without sounding room buzzer.
  • Urgent Multi-Call Cycle (Handset / Wall LP - Reg 40012 / 40032): CALL_BLINK (32) + MULTI_CALL_CYCLE (64) = 96 (0x0060) → Rapid flashing call joins room alternating cycle.
  • Acknowledge + Staff Attendance (Handset / Wall SP - Reg 40017 / 40033): ACKNOWLEDGE (4) + STAFF_CHECKIN (16) = 20 (0x0014) → Acknowledges call and logs nurse attendance.
  • Silent Local Reset (Cancel Key - Reg 40018 / 40034 / 40037): CLEAR_ALL (1) + MUTE_BUZZER (8) = 9 (0x0009) → 0ms immediate cancellation and buzzer silence.

📖 3. Complete Modbus Holding Register Reference

A. Live Status & Inputs (Registers 40001, 40008, 40009, 40010)

Polled continuously by the Gateway with 100ms serial timeout:

Register Bit / Offset Parameter Name Description / Trigger Condition
40001 Bit 0..7 BTN1_SP..BTN4_LP Button 1–4 Single / Long Press latched event bits.
40001 Bit 8..9 TOILET_BTN1_SP/LP Attached Toilet Pull Cord Single Pull / Long Pull (>2s).
40001 Bit 10..11 TOILET_BTN2_SP/LP Attached Toilet Cancel / Reset Key Single Press / Long Press.
40001 Bit 12..13 REMOTE_BTN1_SP/LP Attached Remote Handset Call Single / Long Press.
40001 Bit 14..15 REMOTE_BTN2_SP/LP Attached Remote Handset Cancel Single / Long Press.
40008 Bit 0..1 STATUS_CONTINUOUS Bit 0 = Bed Occupancy Sensor (1=Occupied, 0=Vacant), Bit 1 = Tamper Switch. (NOT clear-on-read).
40009 16-bit CLEAR_MASK Gateway writes handled bits mask to clear processed bits in 40001. Reading returns 0x0000.
40010 16-bit REMOTE_CMD Nurse Station downlink & diagnostic command (0x0001 Clear, 0x0004 Ack, 0x0008 Mute, 0x0100 LED Test, 0x0200 Buzzer Chirp Test, 0x0400 EEPROM Check; diagnostic bits 0x0100/0x0200/0x0400 can be combined).

B. EEPROM Configuration Registers (53 Bytes Total in Non-Volatile Memory)

PLC Address Hex Dec Access Storage Parameter Name Default Value Range / Description
40002 0x0001 1 R/W EEPROM Single-Press Enable Mask 0xFFFF 16-bit mask (indexes even bits: 0, 2, 4, 6, 8, 10, 12, 14 in 40001)
40003 0x0002 2 R/W EEPROM Long-Press Enable Mask 0xFFFF 16-bit mask (indexes odd bits: 1, 3, 5, 7, 9, 11, 13, 15 in 40001)
40004 0x0003 3 R/W EEPROM Long-Press Hold Threshold (ms) 2000 ms required to hold for Long Press (500–5000ms)
40005 0x0004 4 R/W EEPROM Button Debounce Filter (ms) 50 Hardware debounce filter duration in ms (10–200ms)
40006 0x0005 5 R/W EEPROM Idle Glow Color 3 (Blue) Idle standby color (3=Blue, 0=OFF)
40011..20, 31..38 0x000A.. 10.. R/W EEPROM Button Action Bitmasks Bitmasks Bitmask values: 0x0001 Clear to 0x0040 Multi-Cycle (Range: 0x00..0x7F)
40021..30 0x0014.. 20.. R/W EEPROM Button RGB Colors Color codes 0=OFF, 1=Red, 2=Green, 3=Blue, 4=Yellow, 5=Cyan, 6=Magenta. (Toilet uses 40029, Handset uses 40030)
40050 0x0031 49 R/W RAM Global Emergency Alarm 0 1 = All-Red Strobe + Continuous Alarm, 0 = Normal
40061..76 0x003C.. 60.. R/W EEPROM Priority Ranks 1..16 / 0 1 = Highest (Code Blue), 16 = Lowest, 0 = Non-call
40250 0x00F9 249 READ FLASH Hardware Model Type 104 104 = 4-Btn, 102 = 2-Btn, 101 = 1-Btn, 200 = Corridor Dome
40251 0x00FA 250 READ FLASH Firmware Version 0x0200 Embedded firmware version (v2.00)
40252 0x00FB 251 READ FLASH Device Serial Number 1001 Factory serial number / batch ID
40253 0x00FC 252 R/W RAM Pairing Selection Flag 0 1 = Physically Selected by technician (>3s hold)
40254 0x00FD 253 R/W EEPROM Commissioning Status Flag 0 0 = Factory New, 1 = Commissioned
40255 0x00FE 254 R/W RAM Discovery Mode Command 0 0x00AA (New), 0x00FF (All), 0 (Exit)
40256 0x00FF 255 R/W EEPROM Modbus Slave ID 255 Assigned Slave Address (1..247)
â„šī¸ Reserved Registers: On wall units, registers 40007, 40035, 40036 return 0x0000 on read; writes are silently ignored with NO exception. (On corridor dome units, register 40007 is assigned to Color Bed Slot 6.)

đŸ’ģ 4. Complete Microcontroller C Source Code Reference

The C implementation below is complete, modular, and packed (53 Bytes EEPROM):

📄 siscall_modbus_firmware.h (Register Maps, Packed Structs, Bitmasks)
#ifndef SISCALL_MODBUS_FIRMWARE_H
#define SISCALL_MODBUS_FIRMWARE_H

#include <stdint.h>
#include <stdbool.h>

/* Action Code Bitmasks */
#define ACTION_DISABLED       0x0000
#define ACTION_CLEAR_ALL      0x0001
#define ACTION_CALL_SOLID     0x0002
#define ACTION_ACKNOWLEDGE    0x0004
#define ACTION_MUTE_BUZZER    0x0008
#define ACTION_STAFF_CHECKIN  0x0010
#define ACTION_CALL_BLINK     0x0020
#define ACTION_MULTI_CYCLE    0x0040

/* Nurse Station Remote & Diagnostic Command Bitmasks (Reg 40010 / Offset 9) */
#define CMD_REMOTE_CLEAR      0x0001  /* Bit 0: Remote Clear active calls */
#define CMD_REMOTE_ACK        0x0004  /* Bit 2: Remote Acknowledge */
#define CMD_REMOTE_MUTE       0x0008  /* Bit 3: Remote Mute / Snooze Buzzer */
#define CMD_DIAG_LED_TEST     0x0100  /* Bit 8: LED Self-Test (3s cycle) */
#define CMD_DIAG_BUZZER_TEST  0x0200  /* Bit 9: Buzzer Chirp Test (200ms) */
#define CMD_DIAG_EEPROM_CHECK 0x0400  /* Bit 10: EEPROM Checksum Verify */

/* Modbus Protocol Offsets (0-indexed) */
#define REG_LIVE_STATUS       0    /* 40001 (RO Latched) */
#define REG_SP_ENABLE_MASK    1    /* 40002 */
#define REG_LP_ENABLE_MASK    2    /* 40003 */
#define REG_LP_HOLD_TIME      3    /* 40004 */
#define REG_DEBOUNCE_TIME     4    /* 40005 */
#define REG_IDLE_GLOW_COLOR   5    /* 40006 */
#define REG_STATUS_CONTINUOUS 7    /* 40008 (RO Continuous) */
#define REG_CLEAR_MASK        8    /* 40009 (R/W Clear Mask) */
#define REG_REMOTE_CMD        9    /* 40010 (Downlink / Diagnostic) */
#define REG_GLOBAL_EMERGENCY  49   /* 40050 */
#define REG_DEVICE_MODEL      249  /* 40250 (RO Model Code) */
#define REG_FIRMWARE_VER      250  /* 40251 (RO Firmware Ver) */
#define REG_SERIAL_NO         251  /* 40252 (RO Serial No) */
#define REG_PAIRING_FLAG      252  /* 40253 (RAM Pairing Flag) */
#define REG_COMMISSIONED_FLAG 253  /* 40254 (EEPROM Flag) */
#define REG_DISCOVERY_CMD     254  /* 40255 (Discovery Command) */
#define REG_SLAVE_ID          255  /* 40256 (EEPROM Slave ID) */

/* Active Call Record with Snooze State */
typedef struct {
    uint8_t btn;
    uint8_t action_mask;
    uint8_t color;
    uint8_t priority;
    bool    muted;          /* Snooze flag: true when silenced by MUTE_BUZZER (0x0008) */
} ActiveCall_t;

/* EEPROM Non-Volatile Memory Structure (53 Bytes Total, Packed) */
typedef struct __attribute__((packed)) {
    uint8_t  slave_id;                  /* 0x00: Reg 255 (Default 255) */
    uint8_t  is_commissioned;           /* 0x01: Reg 253 (0=Factory, 1=Commissioned) */
    uint16_t single_press_mask;         /* 0x02..0x03: Reg 1 (Default 0xFFFF) */
    uint16_t long_press_mask;           /* 0x04..0x05: Reg 2 (Default 0xFFFF) */
    uint16_t long_press_hold_ms;        /* 0x06..0x07: Reg 3 (Default 2000ms) */
    uint16_t debounce_ms;               /* 0x08..0x09: Reg 4 (Default 50ms) */
    uint8_t  idle_glow_color;           /* 0x0A: Reg 5 (Default 3=Blue) */
    
    uint8_t  btn_sp_action[4];          /* 0x0B..0x0E: Reg 10,12,14,16 (Default: 2, 32, 2, 4) */
    uint8_t  btn_lp_action[4];          /* 0x0F..0x12: Reg 11,13,15,17 (Default: 32, 32, 2, 1) */
    uint8_t  toilet_btn1_action[2];     /* 0x13..0x14: Reg 18,19 (Default: 32, 32) */
    uint8_t  toilet_btn2_action[2];     /* 0x15..0x16: Reg 36,37 (Default: 1, 1) */
    uint8_t  remote_action[4];          /* 0x17..0x1A: Reg 30..33 (Default: 2, 32, 4, 1) */

    uint8_t  btn_sp_color[4];           /* 0x1B..0x1E: Reg 20,22,24,26 (Default: 4, 1, 5, 2) */
    uint8_t  btn_lp_color[4];           /* 0x1F..0x22: Reg 21,23,25,27 (Default: 1, 1, 1, 0) */
    uint8_t  toilet_color;              /* 0x23: Reg 28 (Default 4=Yellow) */
    uint8_t  handset_color;             /* 0x24: Reg 29 (Default 4=Yellow) */

    uint8_t  btn_sp_priority[4];        /* 0x25..0x28: Reg 60,62,64,66 (Default: 3, 1, 4, 0) */
    uint8_t  btn_lp_priority[4];        /* 0x29..0x2C: Reg 61,63,65,67 (Default: 2, 1, 4, 0) */
    uint8_t  toilet_btn1_priority[2];   /* 0x2D..0x2E: Reg 68,69 (Default: 2, 1) */
    uint8_t  toilet_btn2_priority[2];   /* 0x2F..0x30: Reg 70,71 (Default: 0, 0) */
    uint8_t  remote_priority[4];        /* 0x31..0x34: Reg 72..75 (Default: 3, 2, 0, 0) */
} DeviceConfig_t;

extern DeviceConfig_t g_config;
extern uint16_t g_reg40001_live_status;
extern uint16_t g_reg40253_pairing_flag;

void EEPROM_LoadConfig(void);
void EEPROM_SaveConfig(void);

#endif /* SISCALL_MODBUS_FIRMWARE_H */
📄 action_engine.c (Single-Pass Multi-Action Pipeline, No Early Returns, MUTE Derivation)
#include "siscall_modbus_firmware.h"

/* Single-Pass Button Event Pipeline with Multi-Action Bitmasks */
void handle_button_event(uint8_t btn_index, bool is_long_press) {
    uint8_t action_mask = get_action_mask_from_eeprom(btn_index, is_long_press);
    uint8_t led_color   = get_color_code_from_eeprom(btn_index, is_long_press);
    uint8_t prio_rank   = get_priority_rank_from_eeprom(btn_index, is_long_press);

    /* 0. Ignore if action code is 0 (0 = Disabled / No Action) */
    if (action_mask == ACTION_DISABLED) {
        return;
    }

    /* 1. CLEAR_ALL (0x0001): Immediate local 0ms call reset */
    bool has_clear = (action_mask & ACTION_CLEAR_ALL) != 0;
    if (has_clear) {
        turn_off_all_button_leds();
        set_hold_led(false);
        stop_buzzer();
        clear_all_active_calls(); /* Clears all call records and resets snooze states */
        /* Do NOT return: allow non-call actions (e.g. Staff Check-in 0x0010) to execute */
    }

    /* 2. Call Actions (0x0002 Solid, 0x0020 Blink, 0x0040 Cycle) */
    if (!has_clear && (action_mask & (ACTION_CALL_SOLID | ACTION_CALL_BLINK | ACTION_MULTI_CYCLE))) {
        register_button_call(btn_index, action_mask, led_color, prio_rank); /* Initializes call->muted = false */
    }

    /* 3. Evaluate Winning Active Call & Render Visuals */
    uint8_t win_btn = evaluate_highest_priority_active_call();
    if (win_btn != NO_ACTIVE_CALL) {
        uint8_t win_mask  = get_action_mask(win_btn);
        uint8_t win_color = get_color(win_btn);

        if (win_mask & ACTION_CALL_BLINK) {
            set_button_led_blink(win_btn, win_color);
        } else if (win_mask & ACTION_CALL_SOLID) {
            set_button_led_solid(win_btn, win_color);
        }

        if (win_mask & ACTION_MULTI_CYCLE) {
            start_color_alternating_loop();
        }
    }

    /* 4. ACKNOWLEDGE (0x0004): Acknowledges active winning call (only when pressed as non-call action) */
    if ((action_mask & ACTION_ACKNOWLEDGE) && !(action_mask & (ACTION_CALL_SOLID | ACTION_CALL_BLINK | ACTION_MULTI_CYCLE))) {
        if (win_btn != NO_ACTIVE_CALL) {
            set_hold_led(true);
            set_active_call_led_solid();
            ActiveCall_t *call = get_active_call(win_btn);
            if (call) call->muted = true; /* Acknowledge silences active alarm */
        }
        stop_buzzer();
    }

    /* 5. MUTE_BUZZER (0x0008) pressed alone as a snooze action (without call bits) */
    if ((action_mask & ACTION_MUTE_BUZZER) && !(action_mask & (ACTION_CALL_SOLID | ACTION_CALL_BLINK | ACTION_MULTI_CYCLE))) {
        if (win_btn != NO_ACTIVE_CALL) {
            ActiveCall_t *call = get_active_call(win_btn);
            if (call) call->muted = true; /* Snooze active winning call */
        }
        stop_buzzer();
    }

    /* 6. Dynamic Buzzer Evaluation from Winning Call */
    if (win_btn != NO_ACTIVE_CALL) {
        ActiveCall_t *win_call = get_active_call(win_btn);
        /* Sound buzzer only if winning call is not silent (0x0008 in mask) and not snoozed */
        if (win_call && !(win_call->action_mask & ACTION_MUTE_BUZZER) && !win_call->muted) {
            start_buzzer_beep();
        } else {
            stop_buzzer();
        }
    } else {
        stop_buzzer();
    }

    /* 7. STAFF_CHECKIN (0x0010): 3.0s confirmation LED + 100ms short chirp */
    if (action_mask & ACTION_STAFF_CHECKIN) {
        flash_confirmation_led(3000);
        play_short_chirp();
    }

    /* 8. Latch Modbus Live Status Register 40001 once at the end */
    latch_live_status_bit(btn_index, is_long_press);
}

/* Discovery Mode: Technician holds Button 1 for > 3s */
void on_button1_held_3_seconds_discovery(void) {
    if (g_state.is_discovery_mode) {
        g_reg40253_pairing_flag = 1; /* Dedicated pairing flag */
        set_button_led(0, COLOR_YELLOW); /* Solid Yellow selection feedback */
    }
}
📄 modbus_slave.c (Modbus RTU FC 03 / FC 06 / FC 16 Engine)
#include "siscall_modbus_firmware.h"
#include <string.h>

uint16_t Modbus_CRC16(const uint8_t *buffer, uint16_t length) {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < length; i++) {
        crc ^= buffer[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
            else crc >>= 1;
        }
    }
    return crc;
}

void Modbus_ProcessPacket(const uint8_t *rx_buf, uint16_t rx_len, uint8_t *tx_buf, uint16_t *tx_len) {
    if (rx_len < 4) return;

    uint16_t received_crc = rx_buf[rx_len - 2] | (rx_buf[rx_len - 1] << 8);
    if (Modbus_CRC16(rx_buf, rx_len - 2) != received_crc) return;

    uint8_t slave_addr = rx_buf[0];
    uint8_t function_code = rx_buf[1];

    if (slave_addr != g_config.slave_id && slave_addr != 0x00 && slave_addr != 255) return;

    uint16_t reg_addr = (rx_buf[2] << 8) | rx_buf[3];

    /* FC 03: Read Holding Registers */
    if (function_code == 0x03) {
        uint16_t reg_count = (rx_buf[4] << 8) | rx_buf[5];
        
        /* Address 255 check: only selected unit (Reg 40253 == 1) may respond */
        if (slave_addr == 255 && g_reg40253_pairing_flag != 1) {
            *tx_len = 0;
            return;
        }

        tx_buf[0] = slave_addr;
        tx_buf[1] = 0x03;
        tx_buf[2] = reg_count * 2;

        for (uint16_t i = 0; i < reg_count; i++) {
            uint16_t curr_addr = reg_addr + i;
            uint16_t value = 0;

            if (curr_addr == REG_LIVE_STATUS) value = g_reg40001_live_status;
            else if (curr_addr == REG_SP_ENABLE_MASK) value = g_config.single_press_mask;
            else if (curr_addr == REG_LP_ENABLE_MASK) value = g_config.long_press_mask;
            else if (curr_addr == REG_DEVICE_MODEL) value = 104; /* 4-Button Unit */
            else if (curr_addr == REG_FIRMWARE_VER) value = 0x0200; /* v2.00 */
            else if (curr_addr == REG_SERIAL_NO) value = 1001;
            else if (curr_addr == REG_PAIRING_FLAG) value = g_reg40253_pairing_flag;
            else if (curr_addr == REG_COMMISSIONED_FLAG) value = g_config.is_commissioned;
            else if (curr_addr == REG_SLAVE_ID) value = g_config.slave_id;

            tx_buf[3 + i * 2] = (value >> 8) & 0xFF;
            tx_buf[4 + i * 2] = value & 0xFF;
        }

        uint16_t resp_len = 3 + reg_count * 2;
        uint16_t crc = Modbus_CRC16(tx_buf, resp_len);
        tx_buf[resp_len] = crc & 0xFF;
        tx_buf[resp_len + 1] = (crc >> 8) & 0xFF;
        *tx_len = resp_len + 2;
    }
    /* FC 06: Write Single Register */
    else if (function_code == 0x06) {
        uint16_t write_val = (rx_buf[4] << 8) | rx_buf[5];

        /* Address 255 check: only selected unit may accept writes */
        if (slave_addr == 255 && g_reg40253_pairing_flag != 1) {
            *tx_len = 0;
            return;
        }

        /* 1. Send Modbus echo reply FIRST before changing address or executing */
        if (slave_addr != 0x00) {
            memcpy(tx_buf, rx_buf, 8);
            *tx_len = 8;
        } else {
            *tx_len = 0; /* Broadcast address 0 never replies */
        }

        /* 2. Process register writes */
        if (reg_addr == REG_DISCOVERY_CMD) {
            if (write_val == 0x00AA) {
                if (g_config.is_commissioned == 0) g_state.is_discovery_mode = true;
            } else if (write_val == 0x00FF) {
                g_state.is_discovery_mode = true;
            } else if (write_val == 0x0000) {
                g_state.is_discovery_mode = false;
                g_reg40253_pairing_flag = 0;
            }
        } else if (reg_addr == REG_SLAVE_ID && write_val >= 1 && write_val <= 247) {
            g_config.slave_id = (uint8_t)write_val;
            g_config.is_commissioned = 1;
            g_reg40253_pairing_flag = 0;
            EEPROM_SaveConfig(); /* Burn to EEPROM */

            /* Solid Green 3s non-blocking feedback */
            g_state.is_discovery_mode = false;
            set_all_leds_green_3s();
        } else if (reg_addr == REG_CLEAR_MASK) {
            g_reg40001_live_status &= ~write_val; /* Atomically clear handled bits */
        } else if (reg_addr == REG_REMOTE_CMD) {
            if (write_val & CMD_REMOTE_CLEAR) handle_remote_clear_command();
            if (write_val & CMD_REMOTE_ACK)   handle_remote_ack_command();
            if (write_val & CMD_REMOTE_MUTE)  handle_remote_mute_command();
            if (write_val & CMD_DIAG_LED_TEST) run_led_diagnostic_test();
            if (write_val & CMD_DIAG_BUZZER_TEST) run_buzzer_diagnostic_test();
            if (write_val & CMD_DIAG_EEPROM_CHECK) verify_eeprom_checksum();
        } else if (reg_addr == REG_GLOBAL_EMERGENCY) {
            if (write_val == 1) start_global_emergency_all_red_flash();
            else stop_global_emergency();
        }
    }
}