Customizing the LEDs on the Netgate SG-3100
The SG-3100 pfSense appliance from Netgate has three RGB LEDs on the front. By default, the black diamond LED slowly "breathes" blue when everything is normal, and the green circle LED slowly "breathes" red when there is a software update. During boot, the circle LED quickly "breathes" blue, followed by the square, and finally the diamond. Since I'm always curious (and because the bright blue light was beginning to bother me), I went about figuring out how to individually control each LED (nine in total, for three full RGB LEDs). My starting point was a post by @stephenw10 on the Netgate forums.
Edit (27/09/2020): It turns out that the GPIO device might not always be 0
, here's a way to get the GPIO device ID (credit: github.com/justdaniel-gh):
sysctl dev.gpio | grep .led. | cut -d . -f 3 | uniq
Edit (18/02/2020): This still works for the new pfSense Plus edition (21.02), although my GPIO ID changed from 0
to 2
after the upgrade.
The LED Driver
Here's the output of dmesg
that shows the LED driver being loaded:
---<<BOOT>>---
Copyright (c) 1992-2020 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 12.2-STABLE 38a4c12973d(plus-devel-12) pfSense-SG-3100 arm
FreeBSD clang version 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611aa2)
CPU: ARM Cortex-A9 r4p1 (ECO: 0x00000000)
...
ofwbus0: <Open Firmware Device Tree>
simplebus0: <Flattened device tree simple bus> on ofwbus0
simplebus1: <Flattened device tree simple bus> on simplebus0
...
twsi0: <Marvell Integrated I2C Bus Controller> mem 0x11000-0x1101f irq 5 on simplebus1
iicbus0: <OFW I2C bus> on twsi0
iic0: <I2C generic I/O> on iicbus0
gpio2: <NXP PCA9552 LED driver> at addr 0xc0 on iicbus0
device_attach: gpio2 attach returned 6
gpio2: <ISSI IS31FL3199 9 channel light effect LED driver> at addr 0xce on iicbus0
gpiobus2: <OFW GPIO bus> on gpio2
gpioc2: <GPIO controller> on gpio2
...
Customizing the Status LEDs
LED Groups
A LED group has a red, green, and blue led in it, making it a single RGB unit. There are three LED groups, the green circle (), blue square (), and black diamond (). Each LED group is addressed as an led
, according to the following table:
Pin | LED |
---|---|
0 | ⬛ |
1 | 🟦 |
2 | 🟩 |
LED Colours
Each LED colour is addressed as a pin
, according to the following table:
LED Channel | 🟩 | 🟦 | ⬛ |
---|---|---|---|
Red | 6 | 3 | 0 |
Green | 7 | 4 | 1 |
Blue | 8 | 5 | 2 |
RGB Control Mode
The LED controller has two RGB modes, PWM and One Shot Programming. PWM mode means the LEDs are only controlled by the duty cycle (brightness). One Shot Programming mode enables the timing characteristics (rising, holding, falling, & off time) of the controller IC, which allows creating patterns.
To enable PWM mode (disabling One Shot mode):
sysctl dev.gpio.<gpio>.led.<led>.pwm=1
To enable One Shot mode (disabling PWM mode):
sysctl dev.gpio.<gpio>.led.<led>.pwm=0
Double Time
There is a fourth setting for each LED group, called Double Time. Double Time is where T3
is calculated by doubling the value of T1
. E.g.: T3 = 2 * (T1)
Enable Double Time:
sysctl dev.gpio.<gpio>.led.<led>.DT=1
Disable Double Time:
sysctl dev.gpio.<gpio>.led.<led>.DT=0
LED Brightness
The brightness for each individual LED colour has 256 levels, where 0
is off and the remaining 255 are brightness levels. The brightness is set using the gpioctl
command to configure the PWM duty cycle.
Brightness level 0
(off):
gpioctl -f /dev/gpioc<gpio> <pin> duty 0
Brightness level 0
(full):
gpioctl -f /dev/gpioc<gpio> <pin> duty 255
If you supply a value above 255, the value is interpreted as starting from 0, with the greatest factor of 256 subtracted. E.g. 256
is the same as 256 - 256 = 0
, which would be off.
LED Controller
The three groups of three LEDs (nine total) are controlled by the IS31FL3199 integrated circuit from Integrated Silicon Solution, Inc.
The most useful piece of information from the IS31FL3199 LED controller datasheet is this breathing timing graph (figure 7):
Register | Timing Stage | Maximum value | Notes |
---|---|---|---|
T0 |
off time | 31200 (31.2s) |
|
T1 |
rising | 4160 (4.16s) |
|
T2 |
holding | 16640 (16.64s) |
|
T3 |
falling | 4160 (4.16s) |
value will be equal to T1 , unless Double Time is enabled |
T4 |
off time | 31200 (31.2s) |
In case you're interested, there is more detailed timing info in tables 11, 12, and 13.
Source: http://www.issi.com/WW/pdf/IS31FL3199.pdf
Breathing Timings
Changing the timings for each of the breathing stages is done using the sysctl
command.
To set the T0
(off time) timing for a single LED:
sysctl dev.gpio.<gpio>.pin.<pin>.T0=<value>
To set the T4
(off time) timing for a single LED:
sysctl dev.gpio.<gpio>.pin.<pin>.T4=<value>
To set the T2
(hold time) timing for an LED group:
sysctl dev.gpio.<gpio>.led.<led>.T2=<value>
To set the T1
(rising time) and T3
(falling time) timing for an LED group:
sysctl dev.gpio.<gpio>.led.<led>.T1-T3=<value>
Example
These commands will configure the first LED (green circle) to breathe red, green, and blue sequentially. This example assumes the GPIO device ID is 0
.
# Turn up the brightness
gpioctl -f /dev/gpioc0 6 duty 128
gpioctl -f /dev/gpioc0 7 duty 128
gpioctl -f /dev/gpioc0 8 duty 128
# Green Circle LED
sysctl dev.gpio.0.led.2.T2=1040
sysctl dev.gpio.0.led.2.T1-T3=520
# Red colour
sysctl dev.gpio.0.pin.6.T0=0
sysctl dev.gpio.0.pin.6.T4=4160
# Green colour
sysctl dev.gpio.0.pin.7.T0=2080
sysctl dev.gpio.0.pin.7.T4=4160
# Blue colour
sysctl dev.gpio.0.pin.8.T0=4160
sysctl dev.gpio.0.pin.8.T4=4160
Closing Thoughts
The range of settings available for the three front status LEDs on the Netgate SG-3100 creates a lot of posibilities for extending their use. The center (blue square) LED isn't currently used after the boot process, and could be configured to indicate the WAN status, etc. Personally, I only need a dim blue LED for now, just to indicate that the appliance is powered on. To achieve this, I installed the shellcmd
package, and created a command that will dim the LED near the end of the boot process.
sysctl dev.gpio.0.led.0.pwm=1; gpioctl 2 duty 2