Raspberry Pi OS Kernel module for Iono Pi Max - the industrial controller based on the Raspberry Pi Compute Module.
Usage examples, from the shell:
Close a relay:
$ echo 1 > /sys/class/ionopimax/digital_out/o1
Read the voltage on AV1:
$ cat /sys/class/ionopimax/analog_in/av1
Or using Python:
f = open('/sys/class/ionopimax/digital_out/o1', 'w')
f.write('1')
f.close()
print('Relay O1 closed')
f = open('/sys/class/ionopimax/analog_in/av1', 'r')
val = f.read().strip()
f.close()
print('AV1: ' + val)
For installation on Ubuntu read this.
Make sure your system is updated:
sudo apt update
sudo apt upgrade
If you are using Iono Pi Max with a Raspberry Pi CM 4S and a 32-bit OS, add to /boot/firmware/config.txt
(/boot/config.txt
in older versions) the following line: [why?]
arm_64bit=0
Reboot:
sudo reboot
After reboot, install git and the Raspberry Pi kernel headers:
sudo apt install git raspberrypi-kernel-headers
Clone this repo:
git clone --depth 1 https://github.com/sfera-labs/iono-pi-max-kernel-module.git
Make and install:
cd iono-pi-max-kernel-module
make clean
make
sudo make install
Compile the Device Tree and install it:
dtc -@ -Hepapr -I dts -O dtb -o ionopimax.dtbo ionopimax.dts
sudo cp ionopimax.dtbo /boot/overlays/
Add to /boot/firmware/config.txt
(/boot/config.txt
in older versions) the following line:
dtoverlay=ionopimax
The 99-ionopimax-serial.rules
udev rule makes sure the USB dev connected to Iono's RS-485 (or RS-232 if inverted) interface is always available under /dev/ionopimax-serial
:
sudo cp 99-ionopimax-serial.rules /etc/udev/rules.d/
Optionally, to be able to use the /sys/class/ionopimax/
files not as super user, create a new group "ionopimax" and set it as the module owner group by adding an udev rule:
sudo groupadd ionopimax
sudo cp 99-ionopimax.rules /etc/udev/rules.d/
and add your user to the group, e.g. for user "pi":
sudo usermod -a -G ionopimax pi
To enable SocketCAN support for the CAN FD/CAN 2.0 controller (MCP2518FD), add to /boot/firmware/config.txt
(/boot/config.txt
in older versions) the following line:
dtoverlay=mcp251xfd,spi0-0,interrupt=28
Reboot:
sudo reboot
After loading the module, you will find all the available devices under the directory /sys/class/ionopimax/
.
The following paragraphs list all the possible devices (directories) and files coresponding to Iono Pi Max's features.
You can write to and/or read these files to configure, monitor and control your Iono Pi Max. The kernel module will take care of performing the corresponding GPIO or I2C operations. I2C transactions are automatically repeated in case of error and CRC validation is used when supported by the installed firmware (>= 1.4).
Files written in italic are configuration parameters. Those marked with * are not persistent, i.e. their values are reset to default after a power cycle. To change the default values use the /mcu/config
file (see below).
Configuration parameters not marked with * are permanently saved each time they are changed, so that their value is retained across power cycles or MCU resets.
This allows to have a different configuration during the boot up phase, even after an abrupt shutdown. For instance, you may want a short watchdog timeout while your application is running, but it needs to be reset to a longer timeout when a power cycle occurs so that Iono Pi Max has the time to boot and restart your application handling the watchdog heartbeat.
File | R/W | Value | Description |
---|---|---|---|
status | R | 0 | Button released |
status | R | 1 | Button pressed |
status_deb(pollable) | R | 0 | Button debounced state released |
status_deb(pollable) | R | 1 | Button debounced state pressed |
status_deb_ms | R/W | <val> | Button debounce time in milliseconds. Default: 50 |
status_deb_cnt | R/W | <val> | Button debounced presses count. Rolls back to 0 after 4294967295 |
File | R/W | Value | Description |
---|---|---|---|
status | R/W | 0 | Buzzer off |
status | R/W | 1 | Buzzer on |
status | W | F | Flip buzzer's state |
beep | W | <t> | Buzzer on for <t> ms |
beep | W | <t_on> <t_off> <rep> | Buzzer beep <rep> times with <t_on>/<t_off> ms periods. E.g. "200 50 3" |
File | R/W | Value | Description |
---|---|---|---|
l<n>_r | R/W | <val> | Value (0 - 255) of red channel of RGB LED number <n> (1 - 5) |
l<n>_g | R/W | <val> | Value (0 - 255) of green channel of RGB LED number <n> (1 - 5) |
l<n>_b | R/W | <val> | Value (0 - 255) of blue channel of RGB LED number <n> (1 - 5) |
l<n>_br | R/W | <val> | Brightness value (0 - 255) of RGB LED number <n> (1 - 5) |
File | R/W | Value | Description |
---|---|---|---|
di<n> | R | 0 | Digital input (DI) <n> (1 - 4) low |
di<n> | R | 1 | Digital input (DI) <n> (1 - 4) high |
For each digital input, we also expose:
- the debounced state
- 2 debounce times in ms ("on" for high state and "off" for low state) with default value of 50ms
- 2 state counters ("on" for high state and "off" for low state)
The debounce times for each DI has been splitted in "on" and "off" in order to make the debounce feature more versatile and suited for particular application needs (e.g. if we consider digital input 1, and set its debounce "on" time to 50ms and its debounce "off" time to 0ms, we just created a delay-on type control for digital input 1 with delay-on time equal to 50ms).
Change in value of a debounce time automatically resets both counters.
The debounce state of each digital input at system start is UNDEFINED (-1), because if the signal on the specific channel cannot remain stable for a period of time greater than the ones defined as debounce "on" and "off" times, we are not able to provide a valid result.
File | R/W | Value | Description |
---|---|---|---|
di<n>_deb(pollable) | R | 1 | Digital input <n> debounced value high |
di<n>_deb(pollable) | R | 0 | Digital input <n> debounced value low |
di<n>_deb(pollable) | R | -1 | Digital input <n> debounced value undefined |
di<n>_deb_on_ms | RW | val | Minimum stable time in ms to trigger change of the debounced value of digital input <n> to high state. Default value=50 |
di<n>_deb_off_ms | RW | val | Minimum stable time in ms to trigger change of the debounced value of digital input <n> to low state. Default value=50 |
di<n>_deb_on_cnt | R | val | Number of times with the debounced value of the digital input <n> in high state. Rolls back to 0 after 4294967295 |
di<n>_deb_off_cnt | R | val | Number of times with the debounced value of the digital input <n> in low state. Rolls back to 0 after 4294967295 |
File | R/W | Value | Description |
---|---|---|---|
pdc* | R/W | 0 | Pulldown current disabled |
pdc* | R/W | 1 | Pulldown current enabled (factory default) |
o<n> | R/W | 0 | Relay (O) <n> (1 - 4) open |
o<n> | R/W | 1 | Relay (O) <n> (1 - 4) closed |
o<n> | R | F | Relay (O) <n> (1 - 4) fault while open |
o<n> | R | S | Relay (O) <n> (1 - 4) fault while closed |
oc<n> | R/W | 0 | Open collector (OC) <n> (1 - 4) open |
oc<n> | R/W | 1 | Open collector (OC) <n> (1 - 4) closed |
oc<n> | R | F | Open collector (OC) <n> (1 - 4) fault open |
oc<n> | R | S | Open collector (OC) <n> (1 - 4) short circuit |
File | R/W | Value | Description |
---|---|---|---|
dt<n>_mode | R/W | x | DT <n> (1 - 4) line not controlled by kernel module |
dt<n>_mode | R/W | in | DT <n> (1 - 4) line set as input |
dt<n>_mode | R/W | out | DT <n> (1 - 4) line set as output |
dt<n> | R(/W) | 0 | DT <n> (1 - 4) line low. Writable only in output mode |
dt<n> | R(/W) | 1 | DT <n> (1 - 4) line high. Writable only in output mode |
File | R/W | Value | Description |
---|---|---|---|
enabled* | R/W | 0 | Analog converter disabled (power off) |
enabled* | R/W | 1 | Analog converter enabled (factory default) |
hsf* | R/W | 0 | High speed filter for AV/AI inputs disabled (factory default) |
hsf* | R/W | 1 | High speed filter for AV/AI inputs enabled |
av<n>_mode* | R/W | 0 | AV <n> (1 - 4) input disabled (FW >= 1.3) |
av<n>_mode* | R/W | U | AV <n> (1 - 4) unipolar mode (range 0V - +20V) (factory default) |
av<n>_mode* | R/W | B | AV <n> (1 - 4) bipolar mode (range -10V - +10V) |
av<n> | R | <val> | AV <n> (1 - 4) voltage value in mV/100 |
ai<n>_mode* | R/W | 0 | AI <n> (1 - 4) input disabled (FW >= 1.3) |
ai<n>_mode* | R/W | U | AI <n> (1 - 4) unipolar mode (range 0mA - +20mA) (factory default) |
ai<n>_mode* | R/W | B | AI <n> (1 - 4) bipolar mode (range -10mA - +10mA) |
ai<n> | R | <val> | AI <n> (1 - 4) current value in µA |
at<n>_mode* | R/W | 0 | AT <n> (1 - 2) disabled (factory default) |
at<n>_mode* | R/W | 1 | AT <n> (1 - 2) enabled as PT100 sensor input |
at<n>_mode* | R/W | 2 | AT <n> (1 - 2) enabled as PT1000 sensor input |
at<n> | R | <val> | AT <n> (1 - 2) temperature value in °C/100 |
File | R/W | Value | Description |
---|---|---|---|
ao<n>_enabled* | R/W | 0 | Analog output (AO) <n> disabled (factory default) |
ao<n>_enabled* | R/W | 1 | Analog output (AO) <n> enabled |
ao<n>_mode* | R/W | I | Analog output (AO) <n> current mode (factory default) |
ao<n>_mode* | R/W | V | Analog output (AO) <n> voltage mode |
ao<n>* | R/W | <val> | Analog output (AO) <n> value, in mV (voltage mode - range 0-10240) or µA (current mode - range 0-20480) (FW ver. < 1.9 accepts ranges 0-10000 and 0-20000 respectively for voltage and current mode) |
ao<n>_err | R | <err> | Analog output (AO) <n> errors register value. Bit 0 (LSB) set to 1 indicates an over-temperature error, bit 1 a load error, and bit 2 (MSB) a common mode error |
File | R/W | Value | Description |
---|---|---|---|
vso_enabled* | R/W | 0 | VSO output disabled |
vso_enabled* | R/W | 1 | VSO output enabled (factory default) |
vso* | R/W | <val> | VSO voltage value in mV (11500 - 24500) (factory default: 12000) |
vso_mon_v | R | <val> | Actual voltage measured on VSO, in mV |
vso_mon_i | R | <val> | Current drain measured on VSO, in mA |
5vo_enabled* | R/W | 0 | 5VO output disabled |
5vo_enabled* | R/W | 1 | 5VO output enabled |
File | R/W | Value | Description |
---|---|---|---|
enabled | R/W | 0 | Watchdog disabled |
enabled | R/W | 1 | Watchdog enabled |
enabled | W | F | Flip watchdog enabled state |
expired(pollable) | R | 0 | Watchdog timeout not expired |
expired(pollable) | R | 1 | Watchdog timeout expired |
heartbeat | W | 0 | Set watchdog heartbeat line low |
heartbeat | W | 1 | Set watchdog heartbeat line high |
heartbeat | W | F | Flip watchdog heartbeat state |
enable_mode* | R/W | D | Watchdog normally disabled (factory default) |
enable_mode* | R/W | A | Watchdog always enabled (ignores /enabled value) |
timeout* | R/W | <t> | Watchdog heartbeat timeout, in seconds (1 - 65535). Factory default: 60 |
down_delay* | R/W | <t> | Forced shutdown delay from the moment the timeout is expired and the shutdown cycle has not been enabled, in seconds (1 - 65535). Factory default: 60 |
sd_switch* | R/W | <n> | Switch boot from SDA/SDB after <n> consecutive watchdog resets, if no heartbeat is detected. A value of n > 1 can be used with /enable_mode set to A only; if /enable_mode is set to D, then /sd_switch is set automatically to 1 |
sd_switch* | R/W | 0 | SD switch on watchdog reset disabled (factory default) |
File | R/W | Value | Description |
---|---|---|---|
down_enabled | R/W | 0 | Delayed shutdown cycle disabled |
down_enabled | R/W | 1 | Delayed shutdown cycle enabled |
down_delay* | R/W | <t> | Shutdown delay from the moment it is enabled, in seconds (1 - 65535). Factory default: 60 |
off_time* | R/W | <t> | Duration of power-off, in seconds (1 - 65535). Factory default: 5 |
up_delay* | R/W | <t> | Power-up delay after main power is restored, in seconds (0 - 65535). Factory default: 0 |
down_enable_mode* | R/W | I | Immediate (factory default): when shutdown is enabled, Iono will immediately initiate the power-cycle, i.e. wait for the time specified in /down_delay and then power off the Pi board for the time specified in /off_time |
down_enable_mode* | R/W | A | Arm: enabling shutdown will arm the shutdown procedure, but will not start the power-cycle until the shutdown enable line goes low again (i.e. shutdown disabled or Raspberry Pi switched off). After the line goes low, Iono will initiate the power-cycle |
up_mode* | R/W | A | Always: if shutdown is enabled when the main power is not present, only the Raspberry Pi is turned off, and the power is always restored after the power-off time, even if running on battery, with no main power present |
up_mode* | R/W | M | Main power (factory default): if shutdown is enabled when the main power is not present, Iono is fully powered down after the shutdown wait time, and powered up again only when the main power is restored |
sd_switch | R/W | 1 | Switch boot from SDA/SDB at every power-cycle |
sd_switch | R/W | 0 | SD switch at power-cycle disabled (factory default) |
File | R/W | Value | Description |
---|---|---|---|
enabled* | R/W | 0 | UPS disabled |
enabled* | R/W | 1 | UPS enabled (factory default) |
battery | R | 0 | Running on main power |
battery | R | 1 | Running on battery power |
status | R | 0 | UPS status: idle |
status | R | 1 | UPS status: detecting battery |
status | R | 2 | UPS status: battery disconnected |
status | R | 4 | UPS status: charging battery |
status | R | 5 | UPS status: battery charged |
status | R | 6 | UPS status: battery in use |
status | R | 8 | UPS status: battery overvoltage error |
status | R | 9 | UPS status: battery undervoltage error |
status | R | 10 | UPS status: charger damaged |
status | R | 11 | UPS status: unstable |
battery_charge | R | <n> | Estimated battery charge percentage |
charger_mon_v | R | <val> | Voltage measured on battery charger output, in mV |
charger_mon_i | R | <val> | Current drain measured on battery charger output, in mA |
battery_capacity* | R/W | <c> | Battery capacity in mAh (100 - 60000). Writable only while UPS disabled. Factory default: 800 |
battery_v* | R/W | <n> | Voltage rating of the battery in mV. Accepted values: 12000 or 24000. factory default: 12000 |
battery_i_max* | R/W | <c> | Maximum charging current allowed. If set to zero, the value is derived from the battery capacity. Writable only while UPS disabled. Factory default: 0 |
power_delay* | R/W | <t> | UPS automatic power-cycle timeout, in seconds (0 - 65535). Iono will automatically initiate a delayed power-cycle (just like when /power/down_enabled is set to 1) if the main power source is not available for the number of seconds set. A value of 0 (factory default) disables the automatic power-cycle |
File | R/W | Value | Description |
---|---|---|---|
mon_v | R | <val> | Voltage measured on power supply input, in mV |
mon_i | R | <val> | Current drain measured on power supply input, in mA |
File | R/W | Value | Description |
---|---|---|---|
sdx_enabled* | R/W | 1 | SDX bus enabled (factory default) |
sdx_enabled* | R/W | 0 | SDX bus disabled |
sdx_enabled | R/W | 2 | SDX bus disabled, reset to enabled upon power cycle (FW ver. >= 1.2) |
sd1_enabled* | R/W | 1 | SD1 bus enabled |
sd1_enabled* | R/W | 0 | SD1 bus disabled (factory default) |
sd1_enabled | R/W | 2 | SD1 bus enabled, reset to disabled upon power cycle (FW ver. >= 1.2) |
sdx_default | R/W | A | At power-up, SDX bus routed to SDA and SD1 bus to SDB by default (factory default) |
sdx_default | R/W | B | At power-up, SDX bus routed to SDB and SD1 bus to SDA, by default |
sdx_routing | R/W | A | SDX bus routed to SDA and SD1 bus to SDB (factory default) |
sdx_routing | R/W | B | SDX bus routed to SDB and SD1 bus to SDA |
File | R/W | Value | Description |
---|---|---|---|
usb<n>_enabled | R/W | 0 | USB <n> (1 or 2) disabled (power off) |
usb<n>_enabled | R/W | 1 | USB <n> (1 or 2) enabled |
usb<n>_err | R | 0 | USB <n> (1 or 2) OK |
usb<n>_err | R | 1 | USB <n> (1 or 2) fault |
File | R/W | Value | Description |
---|---|---|---|
always_on* | R/W | 0 | The fan will activate automatically based on system state (factory default) |
always_on* | R/W | 1 | Fan always active |
status | R | 0 | Fan inactive |
status | R | 1 | Fan active |
File | R/W | Value | Description |
---|---|---|---|
top | R | <val> | Temperature value from sensor on the top side of the bottom board, in °C/100 |
bottom | R | <val> | Temperature value from sensor on the bottom side of the bottom board, in °C/100 |
Requires FW version >= 1.6
File | R/W | Value | Description |
---|---|---|---|
rs232_rs485_inv* | R/W | 0 | RS-232 connected to UART, RS-485 connected to USB (default) |
rs232_rs485_inv* | R/W | 1 | RS-232 connected to USB, RS-485 connected to UART with TX enable line not controlled (use rs485_txe) |
rs232_rs485_inv* | R/W | <baud> <B><P><S> | RS-232 connected to USB, RS-485 connected to UART with TX enable line automatically controlled based on the specified parameters: <baud> - baud rate: 1200, 2400, 4800, 9600, 19200, 38400, 57600, or 115200 <B> - bits: 7 or 8 <P> - parity: N (none), E (even), or O (odd) <S> - stop bits: 1 or 2 Example: "9600 8N1" |
rs485_txe | R/W | 0 | RS-485 TX enable line control: transmission disabled |
rs485_txe | R/W | 1 | RS-485 TX enable line control: transmission enabled |
File | R/W | Value | Description |
---|---|---|---|
enabled* | R/W | 0 | Expansion bus disabled (factory default) |
enabled* | R/W | 1 | Expansion bus enabled |
aux | R | 0 | Expansion bus auxiliary line low |
aux | R | 1 | Expansion bus auxiliary line high |
5vx* | R/W | 0 | Expansion bus 5V power off (factory default) |
5vx* | R/W | 1 | Expansion bus 5V power on |
File | R/W | Value | Description |
---|---|---|---|
status_all | R | <val> | System state register value. Bitmap of all the following status flags from fan_status (bit 0 - LSB) to rs485_err (bit 13) |
fan_status | R | 0 | Fan inactive |
fan_status | R | 1 | Fan active |
5vo_prot | R | 0 | 5VO output OK |
5vo_prot | R | 1 | 5VO output protection enabled. Output temporarily disabled |
5vx_prot | R | 0 | Expansion bus 5V line OK |
5vx_prot | R | 1 | Expansion bus 5V line protection enabled. Output temporarily disabled |
expbus_aux | R | 0 | Expansion bus auxiliary line low |
expbus_aux | R | 1 | Expansion bus auxiliary line high |
vso_prot | R | 0 | VSO output OK |
vso_prot | R | 1 | VSO output protection enabled. Output temporarily disabled |
ao<n>_prot | R | 0 | AO <n> output OK |
ao<n>_prot | R | 1 | AO <n> output protection enabled. AO <n> temporarily disabled |
vso_err | R | 0 | VSO control OK |
vso_err | R | 1 | VSO control failure |
ad4112_err | R | 0 | Analog converter for analog inputs OK |
ad4112_err | R | 1 | Analog converter for analog inputs failure |
ups_err | R | 0 | UPS control OK |
ups_err | R | 1 | UPS control failure |
led_err | R | 0 | LEDs control OK |
led_err | R | 1 | LEDs control failure |
sys_temp_err | R | 0 | System temperature probes OK |
sys_temp_err | R | 1 | System temperature probes failure |
rs232_err | R | 0 | RS-232 interface OK |
rs232_err | R | 1 | RS-232 interface failure |
rs485_err | R | 0 | RS-485 interface OK |
rs485_err | R | 1 | RS-485 interface failure |
You can use the DT lines as Wiegand interfaces for keypads or card readers. You can connect up to two Wiegand devices using DT1/DT2 respctively for the D0/D1 lines of the first device (w1) and DT3/DT4 for D0/D1 of the second device (w2).
File | R/W | Value | Description |
---|---|---|---|
w<N>_enabled | R/W | 0 | Wiegand interface w<N> disabled |
w<N>_enabled | R/W | 1 | Wiegand interface w<N> enabled |
w<N>_data(pollable) | R | <ts> <bits> <data> | Latest data read from wiegand interface w<N>. The first number (<ts>) represents an internal timestamp of the received data, it shall be used only to discern newly available data from the previous one. <bits> reports the number of bits received (max 64). <data> is the sequence of bits received represnted as unsigned integer |
The following properties can be used to improve noise detection and filtering. The w<N>_noise property reports the latest event and is reset to 0 after being read.
File | R/W | Value | Description |
---|---|---|---|
w<N>_pulse_width_max | R/W | <val> | Maximum bit pulse width accepted, in µs |
w<N>_pulse_width_min | R/W | <val> | Minimum bit pulse width accepted, in µs |
w<N>_pulse_itvl_max | R/W | <val> | Maximum interval between pulses accepted, in µs |
w<N>_pulse_itvl_min | R/W | <val> | Minimum interval between pulses accepted, in µs |
w<N>_noise | R | 0 | No noise |
w<N>_noise | R | 10 | Fast pulses on lines |
w<N>_noise | R | 11 | Pulses interval too short |
w<N>_noise | R | 12/13 | Concurrent movement on both D0/D1 lines |
w<N>_noise | R | 14 | Pulse too short |
w<N>_noise | R | 15 | Pulse too long |
File | R/W | Value | Description |
---|---|---|---|
config | W | S | Save the current configuration as default to be retained across power cycles |
config | W | R | Restore the original factory configuration and default values |
fw_version | R | <m>.<n> | Read the firmware version, <m> is the major version number, <n> is the minor version number E.g. "1.0" |
File | R/W | Value | Description |
---|---|---|---|
serial_num | R | 9 1-byte HEX values | Secure element serial number |
Check that the SocketCAN interface is correctly enabled by running:
ifconfig -a
you should see an interface named can0
:
can0: flags=128<NOARP> mtu 16
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 199
Initialize can0
specifying communication parameters and protocol options, e.g.:
sudo ip link set can0 up type can bitrate 1000000 dbitrate 8000000 restart-ms 1000 berr-reporting on fd on
sudo ifconfig can0 txqueuelen 65536
You can run some tests using the SocketCAN utilities provided by can-utils
:
sudo apt install can-utils
Dump data from the bus:
candump can0
Generate random traffic:
cangen can0 -mv
Refer to the SocketCAN documentation for further details.