NORVI X is a next generation modular industrial controller designed to power smart automation and IoT solutions. Built around a CPU module and a range of plug and play expansion modules, the platform scales from a handful of I/O points up to 200, giving system integrators, OEMs, and industrial engineers a flexible foundation for automation, remote monitoring, and data acquisition projects.
The NORVI X-DI16 is a 16 channel digital input expansion module built for the NORVI X modular controller. It gives you protected 24V DC digital inputs for wiring in industrial switches, sensors, and other logic level signals. At the core of the module sits a PCA9555 I2C port expander, which lets the NORVI X CPU read all sixteen input states over the internal expansion bus instead of tying up individual GPIO pins. Because the module supports eight selectable I2C addresses, you can connect up to eight DI16 modules on the same controller, giving you a total of 128 digital inputs. This guide walks through the safety requirements, hardware specifications, terminal wiring, address selection, GPIO mapping, and test code you need to install and commission the module.
Key Features #
- Sixteen optically isolated 24V DC digital inputs
- Supports both sourcing (PNP) and sinking (NPN).
- One shared common terminal for all sixteen inputs
- PCA9555 I2C port expander with eight selectable addresses
- Up to eight DI16 modules per controller, for 128 inputs total
- DIN rail mountable enclosure with spring terminals
- Plug and play connection to the CPU module through the expansion bus
- One status LED per input channel
- CE / EMC compliant (EN 61131-2:2007, EN 61010-1:2010+A1:2019, EN IEC 61010-2-201:2018, 2014/30/EU)
Safety Information #
Warning: Disconnect all power sources before installing, wiring, or servicing the NORVI X-DI16. Failure to do so may result in electric shock or damage to the equipment.
- Install and wire this module only if you are a qualified electrician or trained technician familiar with industrial control equipment.
- Confirm the host CPU is running from a regulated 24 V DC supply, since the module draws its power through the expansion port rather than a separate connection.
- Mount the module on a DIN rail inside a suitably rated enclosure. It carries an IP20 protection rating and is not designed for direct outdoor or wash down exposure.
- Keep the operating environment within minus 40 to plus 85 degrees C and avoid condensing humidity.
- Do not connect or disconnect the module from the expansion bus while the system is powered.
- Follow all local and national electrical codes applicable to industrial control panel wiring.
Technical Specifications #
Electrical #
| Rated Supply Voltage | 24V DC |
| Current Consumption | 90mA @ 24V DC |
Input Specifications #
| Number of Inputs | 16 Digital Inputs |
| Input Type | Isolated 24 V DC, Sinking and Sourcing |
| Input Voltage Range | 0 to 30 V DC |
| Logic Low (OFF) | Below 5 V DC |
| Logic High (ON) | Above 15 V DC |
| Input Impedance | Approximately 3.3 kΩ |
| Input Current | Approximately 7 mA at 24 V DC |
| Switching Frequency | Up to 1 kHz |
| Isolation Type | Optocoupler isolation per channel |
| Power supply | 24 V DC, shared with the CPU through the expansion port |
| Status Indicators | 16 LED input indicators |
| Connector Type | Spring terminal |
Hardware Layout & GPIO Map #
The table below lists the terminals on the X DI16 and how they map into the PCA9555 port expander. Unlike the direct GPIO input modules, none of the host CPU’s GPIO pins are claimed by the input channels themselves, only SCL and SDA are shared across the I2C bus.
| Terminal | Function |
|---|---|
| IN1 | Digital Input 1 |
| IN2 | Digital Input 2 |
| IN3 | Digital Input 3 |
| IN4 | Digital Input 4 |
| IN5 | Digital Input 5 |
| IN6 | Digital Input 6 |
| IN7 | Digital Input 7 |
| IN8 | Digital Input 8 |
| IN9 | Digital Input 9 |
| IN10 | Digital Input 10 |
| IN11 | Digital Input 11 |
| IN12 | Digital Input 12 |
| IN13 | Digital Input 13 |
| IN14 | Digital Input 14 |
| IN15 | Digital Input 15 |
| IN16 | Digital Input 16 |
| DCOM | Isolated digital common |
I2C Address Selection #

| Address Selection | ||||
|---|---|---|---|---|
| DIP 1 | DIP 2 | DIP 3 | DIP 4 | Address |
| OFF | OFF | OFF | × | 0x27 |
| ON | OFF | OFF | × | 0x26 |
| OFF | ON | OFF | × | 0x25 |
| ON | ON | OFF | × | 0x24 |
| OFF | OFF | ON | × | 0x23 |
| ON | OFF | ON | × | 0x22 |
| OFF | ON | ON | × | 0x21 |
| ON | ON | ON | × | 0x20 |
The X DI16 connects to the host CPU’s I2C bus using SCL on IO9 and SDA on IO8. DIP switch 4 is not used for addressing. Set each stacked DI16 module to a different address so the CPU can talk to them individually on the shared I2C bus.
Wiring Guide #
GPIO pin assignments for the I2C bus are listed in the Expansion Port table below; this section covers how to physically wire the module, not the pin numbers themselves.
Power Connection #
The module draws its 24 V DC supply through the expansion port itself, shared with the CPU, so there is no separate power terminal to wire on the X DI16.
Digital Input Wiring #
- For a sourcing (PNP) sensor, connect the sensor’s signal output to the corresponding INx terminal, and connect the sensor’s negative or ground lead to DCOM.
- For a sinking (NPN) sensor, connect the sensor’s signal output to the corresponding INx terminal, and tie the sensor’s positive lead to the 24 V supply, with DCOM referenced to the same supply ground.
- For a simple dry contact switch, wire one side of the contact to INx and the other side to DCOM.
- Since all sixteen inputs share a single DCOM terminal, sourcing and sinking sensors cannot be mixed on the same module. Choose one wiring style for all sixteen channels.

Connecting the Module to the CPU #
- Power off the system.
- Mount the X DI16 on the DIN rail next to the CPU module or an adjacent expansion module.
- Seat it firmly onto the expansion bus connector until it locks into place.
- Set the module’s DIP switches to a free I2C address, especially if more than one DI16 is being installed.
- Wire your field signals (switches, sensors, dry contacts) to the INPUT 1 through INPUT 16 terminals, confirming each signal stays within the module’s 0 to 30 V DC input range.
- Power the system back on.
- Upload the module’s test sketch from the NORVI GitHub repository to confirm each channel reads correctly before wiring it into your final control logic.

Expansion Port (50 pin) #
The X-DI16 connects through the same 50 pin expansion bus used across the NORVI X family. The pins this module actively uses are SCL on pin 5 (IO9) and SDA on pin 6 (IO8), along with the shared 24V and GND power pins. The remaining pins on the connector, including the CPU’s SPI, UART, USB, and PCA9539 chip select lines, pass through untouched and remain available for other expansion modules or CPU functions.
| PIN INDEX | Purpose | GPIO | SOURCE |
|---|---|---|---|
| 1 | MOSI | IO11 | ESP32-S3 |
| 2 | MISO | IO13 | |
| 3 | SCLK | IO12 | |
| 4 | GND | —- | |
| 5 | SCL | IO9 | |
| 6 | SDA | IO8 | |
| 7 | GND | —- | |
| 8 | RX1 | IO18 | |
| 9 | TX1 | IO17 | |
| 10 | GND | —- | |
| 11 | D+ | —- | |
| 12 | D- | —- | |
| 13 | GND | —- | |
| 14 | CS1 | P1_0 | PCA9539 I2C Address 0x75 |
| 15 | CS2 | P0_7 | |
| 16 | CS3 | P0_6 | |
| 17 | CS4 | P0_5 | |
| 18 | IO1 | IO1 | ESP32-S3 |
| 19 | IO2 | IO2 | |
| 20 | IO4 | IO4 | |
| 21 | IO5 | IO5 | |
| 22 | IO6 | IO6 | |
| 23 | IO7 | IO7 | |
| 24 | IO10 | IO10 | |
| 25 | IO11 | IO35 | |
| 26 | IO12 | IO36 | |
| 27 | IO13 | IO37 | |
| 28 | IO14 | IO14 | |
| 29 | RS485_TX | IO15 | |
| 30 | GND | —- | |
| 31 | GND | —- | |
| 32 | RS485_RX | IO16 | |
| 33 | P1_1 | P1_1 | PCA9539 I2C Address 0x75 |
| 34 | P1_2 | P1_2 | |
| 35 | IO21 | IO21 | ESP32-S3 |
| 36 | GND | —- | |
| 37 | GND | —- | |
| 38 | P1_3 | P1_3 | PCA9539 I2C Address 0x75 |
| 39 | P1_4 | P1_4 | |
| 40 | P1_5 | P1_5 | |
| 41 | IO39 | IO39 | ESP32-S3 |
| 42 | IO40 | IO40 | |
| 43 | NC | —- | |
| 44 | NC | —- | |
| 45 | NC | —- | |
| 46 | GND | —- | |
| 47 | 5V | 5V | |
| 48 | GND | GND | |
| 49 | 24V | 24V | |
| 50 | 24V | 24V |
Software Setup (Arduino IDE) #
The NORVI X-DI8 is read through the host CPU using the Arduino IDE with the ESP32 board package. Firmware examples and required libraries are maintained in the NORVI GitHub repository.
Board Configuration #
-
Install Arduino IDE
Install the Arduino IDE and add ESP32 board support:
-
Connect the Controller
Connect the NORVI X CPU to your computer using the USB interface.
-
Select Board and Port
Configure the board and communication port:


Configure Board Settings #
Under the Tools menu, configure the following settings:
| Setting | Selection |
|---|---|
| USB CDC On Boot | Enabled |
| Flash Size | 16MB (128Mb) |
| PSRAM | QSPI PSRAM |
Test Code Examples #
Required Libraries #
Install these via Arduino IDE → Sketch → Include Library → Manage Libraries before compiling any of the examples below:
- Wire
- SPI
- PCA9538
- TFT_eSPI
- CST816S
- PCA9555
Install the Required Library #
Install the Wire library through:
Search for Wire and install it if required.

Open and Upload the Test Sketch #
- Open the X-DI16 test sketch from the NORVI GitHub repository .
- Verify the board and port selections.
- Click Upload to upload the test sketch to the NORVI X CPU.
Test Code Examples #
Required Libraries #
Install these via Arduino IDE → Sketch → Include Library → Manage Libraries before compiling any of the examples below:
- Wire
- SPI
- PCA9538
- TFT_eSPI
- CST816S
- PCA9555
Install the Required Library #
Install the Wire library through:
Search for Wire and install it if required.

Open and Upload the Test Sketch #
- Open the X-DI16 test sketch from the NORVI GitHub repository .
- Verify the board and port selections.
- Click Upload to upload the test sketch to the NORVI X CPU.
Initialization #
This sets up the I2C bus, scans for the expander, configures all sixteen channels as inputs, and starts the TFT display. Run this first to confirm the module powers up and the expander responds before checking individual channels.
PCA9555 ioport(0x27);
TFT_eSPI tft = TFT_eSPI();
void setup() {
Serial.begin(115200);
Wire.begin(SDA, SCL);
I2C_SCAN();
for (int i = 0; i < 16; i++)
ioport.pinMode(i, INPUT);
SPI.begin(SCLK, MISO, MOSI);
tft.init(); tft.setRotation(0);
drawLabel();
}
Note: The PCA9555_ADDR value of 0x27 corresponds to the default DIP switch setting (DIP 1 OFF, DIP 2 OFF and 3 OFF). Change this value to match the address configured by the module’s DIP switches. When using multiple X-DI16 modules on the same I²C bus, ensure that each module is configured with a unique I²C address.
Digital Input Read (X DI16 Test Sketch Excerpt) #
The main loop polls all sixteen channels through the PCA9555 and only redraws a value on screen when it changes, so the display stays responsive as inputs toggle.
void loop()
{
int state[16];
// Read Inputs
state[0] = ioport.digitalRead(PCA_A0);
state[1] = ioport.digitalRead(PCA_A1);
state[2] = ioport.digitalRead(PCA_A2);
state[3] = ioport.digitalRead(PCA_A3);
state[4] = ioport.digitalRead(PCA_A4);
state[5] = ioport.digitalRead(PCA_A5);
state[6] = ioport.digitalRead(PCA_A6);
state[7] = ioport.digitalRead(PCA_A7);
state[8] = ioport.digitalRead(PCA_B0);
state[9] = ioport.digitalRead(PCA_B1);
state[10] = ioport.digitalRead(PCA_B2);
state[11] = ioport.digitalRead(PCA_B3);
state[12] = ioport.digitalRead(PCA_B4);
state[13] = ioport.digitalRead(PCA_B5);
state[14] = ioport.digitalRead(PCA_B6);
state[15] = ioport.digitalRead(PCA_B7);
// Update only changed values
for (int i = 0; i < 16; i++)
{
if (state[i] != oldState[i])
{
updateInput(i, state[i]);
oldState[i] = state[i];
}
}
delay(10);
}
void updateInput(uint8_t ch, uint8_t value)
{
int x, y;
if(ch < 8)
{
x = 60;
y = 80 + (ch * 30);
}
else
{
x = 190;
y = 80 + ((ch - 8) * 30);
}
tft.fillRect(x, y - 18, 20, 20, TFT_BLACK);
if(value)
tft.setTextColor(TFT_GREEN, TFT_BLACK);
else
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.setCursor(x, y);
tft.print(value);
}
Validating the Full Test Sketch #
When the complete sketch runs successfully, the display shows NORVI X-DI16 INPUT TEST at the top, followed by DI1 through DI16 with a live value beside each input. Toggling a sensor or dry contact on any channel should update its displayed value between 0 and 1. The I²C scanner also confirms that the PCA9555 is detected on the configured I²C address, allowing you to verify that the inputs are reading correctly before integrating them into your final control logic.
Troubleshooting #
| Symptom | Suggested Action |
|---|---|
| No response from an input | Check the wiring polarity and confirm that DCOM is properly connected to the sensor’s return path. |
| LED stays off despite an active sensor | Confirm that the input voltage exceeds the 15 V logic-high threshold. |
| Module not detected on the I2C scan | Power down, reseat the module on the expansion bus, and confirm the DIP switch address doesn’t conflict with another installed DI16. |
| Two stacked modules interfere with each other | Check that each DI16 is set to a different I2C address using its DIP switches. |
| TFT display blank or not working | Check three settings under Tools: USB CDC On Boot set to Enabled, Flash Size set to 16MB (128Mb), and PSRAM set to QSPI PSRAM. |