Closed geerlingguy closed 1 year ago
I2C bringup, following these instructions:
pi@raspberrypi:~ $ sudo ldto enable i2c-ao
Overlay i2c-ao: applied
pi@raspberrypi:~ $ ls -al /dev/i2c-*
crw-rw---- 1 root i2c 89, 0 Sep 22 04:17 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1 Mar 2 22:23 /dev/i2c-1
pi@raspberrypi:~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: 10 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Hooray!
Now for the real test. Following the EP-0099 Docs:
# Turn on relay channel 1
i2cset -y 1 0x10 0x01 0xFF
# Turn off relay channel 1
i2cset -y 1 0x10 0x01 0x00
It worked!
So for something like a Python script we need to first initialize the bus:
sudo ldto enable i2c-ao
Then run a script with python3 relaytest.py
:
import time as t
import smbus
import sys
DEVICE_BUS = 1
DEVICE_ADDR = 0x10
bus = smbus.SMBus(DEVICE_BUS)
while True:
try:
for i in range(1,5):
bus.write_byte_data(DEVICE_ADDR, i, 0xFF)
t.sleep(1)
bus.write_byte_data(DEVICE_ADDR, i, 0x00)
t.sleep(1)
except KeyboardInterrupt as e:
print("Quit the Loop")
sys.exit()
Now working on a button. It looks like I'll need to install the le potato wiring tool, and here's a GPIO pin header reference
pi@raspberrypi:~ $ sudo apt install gpiod libretech-gpio libretech-dtoverlay
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
gpiod is already the newest version (1.6.2-1).
libretech-dtoverlay is already the newest version (0.1.2).
libretech-gpio is already the newest version (0.1.2).
But I do want to use Python, so:
sudo apt install -y python3-libgpiod
Here's a handy guide: How to control GPIO via Python 3.
I am plugged into physical GPIO pins 39
(GND) and 40
(Linux #83, sysfs #484, name GPIOX_4).
pi@raspberrypi:~ $ gpiodetect
gpiochip0 [aobus-banks] (11 lines)
gpiochip1 [periphs-banks] (100 lines)
pi@raspberrypi:~ $ gpioinfo periphs-banks
gpiochip1 - 100 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed unused input active-high
line 4: unnamed unused input active-high
line 5: unnamed unused input active-high
line 6: unnamed unused input active-high
line 7: unnamed unused input active-high
line 8: unnamed unused input active-high
line 9: unnamed unused input active-high
line 10: unnamed unused input active-high
line 11: unnamed unused input active-high
line 12: unnamed unused input active-high
line 13: unnamed unused input active-high
line 14: "Eth Link LED" unused input active-high
line 15: "Eth Activity LED" unused input active-high
line 16: "HDMI HPD" unused input active-high
line 17: "HDMI SDA" unused input active-high
line 18: "HDMI SCL" unused input active-high
line 19: "HDMI_5V_EN" "regulator-hdmi-5v" output active-high [used]
line 20: "9J1 Header Pin2" unused input active-high
line 21: "Analog Audio Mute" "enable" output active-high [used]
line 22: "2J3 Header Pin6" unused input active-high
line 23: "2J3 Header Pin5" unused input active-high
line 24: "2J3 Header Pin4" unused input active-high
line 25: "2J3 Header Pin3" unused input active-high
line 26: "eMMC D0" unused input active-high
line 27: "eMMC D1" unused input active-high
line 28: "eMMC D2" unused input active-high
line 29: "eMMC D3" unused input active-high
line 30: "eMMC D4" unused input active-high
line 31: "eMMC D5" unused input active-high
line 32: "eMMC D6" unused input active-high
line 33: "eMMC D7" unused input active-high
line 34: "eMMC Clk" unused input active-high
line 35: "eMMC Reset" "reset" output active-low [used]
line 36: "eMMC CMD" unused input active-high
line 37: "ALT BOOT MODE" unused input active-high
line 38: unnamed unused input active-high
line 39: unnamed unused input active-high
line 40: unnamed unused input active-high
line 41: "eMMC Data Strobe" unused input active-high
line 42: "SDCard D1" unused input active-high
line 43: "SDCard D0" unused input active-high
line 44: "SDCard CLK" unused input active-high
line 45: "SDCard CMD" unused input active-high
line 46: "SDCard D3" unused input active-high
line 47: "SDCard D2" unused input active-high
line 48: "SDCard Det" "cd" input active-low [used]
line 49: unnamed unused input active-high
line 50: unnamed unused input active-high
line 51: unnamed unused input active-high
line 52: unnamed unused input active-high
line 53: unnamed unused input active-high
line 54: unnamed unused input active-high
line 55: unnamed unused input active-high
line 56: unnamed unused input active-high
line 57: unnamed unused input active-high
line 58: unnamed unused input active-high
line 59: unnamed unused input active-high
line 60: unnamed unused input active-high
line 61: unnamed unused input active-high
line 62: unnamed unused input active-high
line 63: unnamed unused input active-high
line 64: unnamed unused input active-high
line 65: unnamed unused input active-high
line 66: unnamed unused input active-high
line 67: unnamed unused input active-high
line 68: unnamed unused input active-high
line 69: unnamed unused input active-high
line 70: unnamed unused input active-high
line 71: unnamed unused input active-high
line 72: unnamed unused input active-high
line 73: "Green LED" "librecomputer:system-status" output active-high [used]
line 74: "VCCK Enable" unused input active-high
line 75: "7J1 Header Pin27" unused input active-high
line 76: "7J1 Header Pin28" unused input active-high
line 77: "VCCK Regulator" unused input active-high
line 78: "VDDEE Regulator" unused input active-high
line 79: "7J1 Header Pin22" unused input active-high
line 80: "7J1 Header Pin26" unused input active-high
line 81: "7J1 Header Pin36" unused input active-high
line 82: "7J1 Header Pin38" unused input active-high
line 83: "7J1 Header Pin40" unused input active-high
line 84: "7J1 Header Pin37" unused input active-high
line 85: "7J1 Header Pin33" unused input active-high
line 86: "7J1 Header Pin35" unused input active-high
line 87: "7J1 Header Pin19" unused input active-high
line 88: "7J1 Header Pin21" unused input active-high
line 89: "7J1 Header Pin24" unused input active-high
line 90: "7J1 Header Pin23" unused input active-high
line 91: "7J1 Header Pin8" unused input active-high
line 92: "7J1 Header Pin10" unused input active-high
line 93: "7J1 Header Pin16" unused input active-high
line 94: "7J1 Header Pin18" unused input active-high
line 95: "7J1 Header Pin32" unused input active-high
line 96: "7J1 Header Pin29" unused input active-high
line 97: "7J1 Header Pin31" unused input active-high
line 98: "7J1 Header Pin7" unused input active-high
line 99: unnamed unused input active-high
Testing the LED inside the button, I can get output with:
gpioset --mode=time --sec=1 periphs-banks 83=1
That lights it up, but only connected one way—since LEDs are diodes they only work in one orientation. There's no indication on the button hardware itself which way is correct, so we'll need to find a quick way to light the LED then mark which side is + and which is -... which goes to GPIO and which to ground.
And I wired up the button to pin 36 (number 82), and this is now working, accepting my button presses:
pi@raspberrypi:~ $ gpioget --bias=pull-up periphs-banks 82
1
pi@raspberrypi:~ $ gpioget --bias=pull-up periphs-banks 82
0
pi@raspberrypi:~ $ gpioget --bias=pull-up periphs-banks 82
1
pi@raspberrypi:~ $ gpioget --bias=pull-up periphs-banks 82
1
pi@raspberrypi:~ $ gpioget --bias=pull-up periphs-banks 82
0
If I try the example gpiomon.py
I get an error "No such device":
pi@raspberrypi:~ $ /usr/share/doc/python3-libgpiod/examples/gpiomon.py periphs-banks 82
Traceback (most recent call last):
File "/usr/share/doc/python3-libgpiod/examples/gpiomon.py", line 37, in <module>
lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_EV_BOTH_EDGES)
OSError: [Errno 19] No such device
Asked about it in the Le Potato forums: https://hub.libre.computer/t/how-to-control-gpio-via-python-3/601/42
I might have to write an event loop differently and just brute force check on the status of the pin over and over. Still should be plenty fast, but more annoying, programming-wise.
Aside: great article on the gpiod integration in the kernel: https://ostconf.com/system/attachments/files/000/001/532/original/Linux_Piter_2018_-_New_GPIO_interface_for_linux_userspace.pdf?1541021776
Oh, uh...
Just to update you, the Amlogic GXL family has an interrupt controller that is able to monitor up to 8 interrupts. Each monitored interrupt can be falling or rising edge and monitoring both counts as 2 interrupts. So it’s very limited and reserved mostly for device-tree bound devices. You can look at the dtoverlays in the libretech-wiring-tool to see how it’s done. Due to the small number of monitorable interrupts, it is easy to exceed this if gpio_to_irq is enabled so upstream decided to leave that disabled for this SoC family. This effectively prevents gpiomon from working.
From: https://hub.libre.computer/t/interrupt-capable-pins-on-aml-s905x-cc/67/6?u=geerlingguy
It looks like Armbian might support the rising edge interrupts: https://hub.libre.computer/t/gpiomon-does-not-work-on-a-le-potato-running-your-suggested-ubuntu-image/83/5
Going to see if there's a hack I can do with an overlay to get it working with the Raspbian image. Or we can just try Armbian. No skin off my back!
Testing with Armbian, it looks like the image uses root
and 1234
and requires setting a password on first login.
On first boot with a freshly-flashed microSD card, the board seems to take 3-5 minutes to finish filling a 32GB card before it reboots into Armbian.
There's actually an entire setup wizard (which is slightly annoying) that goes through everything including setting up a locale. Not sure if it's all required, but eventually you create a user account then get dumped back on the CLI as root. I logged out, then logged back in as the account I created.
To enable I2C, I had to run sudo armbian-config
and then go to System > Hardware > [*] i2cA
.
Now I see the device:
pi@lepotato:~$ ls -al /dev/i2c-*
crw-rw---- 1 root i2c 89, 0 Mar 3 22:38 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1 Mar 3 22:38 /dev/i2c-1
And... unfortunately we're not seeing the relay at all on the bus :/
To make things easier, I added the user to the i2c
group: sudo adduser pi i2c
Testing GPIO at least, I tried sudo apt install -y gpiod
and it's already there, so then I ran sudo apt install -y python3-libgpiod
, and it did install the python library. Testing my test script now...
I have to run with sudo, and I'm getting that same:
PermissionError: [Errno 1] Operation not permitted
Drat.
Whee! Getting the i2c working requires compiling our own overlay from the libretech-wiring-tool project:
wget https://raw.githubusercontent.com/libre-computer-project/libretech-wiring-tool/master/libre-computer/aml-s905x-cc/dt/i2c-ao.dts
sudo armbian-add-overlay i2c-a.dts # this compiles the overlay into /boot/overlay-user/i2c-a.dtbo
sudo reboot
(We will need to automate that, as well as the python GPIO library install.)
Then on reboot:
pi@lepotato:~$ i2cdetect -l
i2c-0 i2c Meson I2C adapter I2C adapter
i2c-1 i2c DesignWare HDMI I2C adapter
pi@lepotato:~$ i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: 10 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
I also had to install python3-smbus
so I could use the bus in my Python test script.
Whew. Now on to getting rising edge detection on one of the GPIO pins...
There's no gpio
group on Armbian, so I could modify the gpio
filesystem points and set up a group, but I'm lazy, so I'm just going to throw sudo at it.
Trying a few different GPIO pins:
[ 1103.739324] gpio irq setup: hwirq: 0x5B irqfirst: 0x59 irqlast: 0x6B pin[81]
[ 1103.739546] genirq: Setting trigger mode 3 for irq 52 failed (meson_gpio_irq_set_type+0x0/0x68)
[ 1147.805176] gpio irq setup: hwirq: 0x61 irqfirst: 0x59 irqlast: 0x6B pin[87]
[ 1147.805423] genirq: Setting trigger mode 3 for irq 53 failed (meson_gpio_irq_set_type+0x0/0x68)
[ 1149.301159] gpio irq setup: hwirq: 0x62 irqfirst: 0x59 irqlast: 0x6B pin[88]
[ 1149.301405] genirq: Setting trigger mode 3 for irq 54 failed (meson_gpio_irq_set_type+0x0/0x68)
After adding some more, eventually I run out:
...
[ 1520.683876] genirq: Setting trigger mode 3 for irq 56 failed (meson_gpio_irq_set_type+0x0/0x68)
[ 1522.597196] gpio irq setup: hwirq: 0x6D irqfirst: 0x53 irqlast: 0x58 pin[75]
[ 1522.597219] meson-gxl-pinctrl c8834000.bus:pinctrl@4b0: no more irq for pin[75]
[ 1529.683749] gpio irq setup: hwirq: 0x27 irqfirst: 0x24 irqlast: 0x33 pin[29]
[ 1529.683826] irq_meson_gpio: No channel available
[ 1534.178476] gpio irq setup: hwirq: 0x6A irqfirst: 0x59 irqlast: 0x6B pin[96]
[ 1534.178553] irq_meson_gpio: No channel available
Yay! Using LINE_REQ_EV_RISING_EDGE
I can get it to work now!
Of course... I'm getting this annoying behavior where when I click in, I get like 30 RISING EDGE notifications:
^[[A^Cpi@lepotato:~$ sudo ./gpio-test.py
gpiochip1['gpiochip1:82 /7J1 Header Pin38/', 'gpiochip1:81 /7J1 Header Pin36/']
'RISING EDGE (1889.233528982) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.233593567) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.233608692) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.233985909) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234013493) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234033118) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234049577) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234329917) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234361001) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234395793) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234444253) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234475128) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234497462) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234512837) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234537838) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234688216) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234712759) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234718634) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234724384) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234742259) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.234758051) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1889.235335398) source('gpiochip1:82 /7J1 Header Pin38/')'
and when I release, a few more for good measure:
'RISING EDGE (1890.994243722) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1890.994314015) source('gpiochip1:82 /7J1 Header Pin38/')'
'RISING EDGE (1890.994406351) source('gpiochip1:82 /7J1 Header Pin38/')'
We'll have to debounce in code (or set up a capacitor-based debounce circuit... but we don't have time for a custom PCB or anything...
I added some code to detect rising edge:
def rising_edge_detect(event_source, event_value, event_time):
global bounce_timer
# A note on debouncing: I tried to find a way using the value to get whether
# we were truly getting a 'down' click or 'up' release. And I failed.
# Miserably. There is no reliable way (at least not with the arcade switches
# I've been testing) to determine whether a press is happening on the down
# or up side of the falling edge on this board. And we don't have time to
# print our own debounce circuit, so this function is what you get.
time_now = time.perf_counter_ns()
if ((time_now - bounce_timer) > bounce_limit):
button_click(event_source, event_value, event_time)
# Reset bounce timer so further noise won't be registered.
bounce_timer = time.perf_counter_ns()
I have done it, it is written.
Hardware seems to be checking out now. On to future tasks!
I will work on automating the setup before closing this out though.
Closing this and following up with automation in #14.
Each room needs a Le Potato running the lights and accepting button input.
I am currently testing the Raspbian 11 for Le Potato image, and here are my notes setting it up:
boot
volumeuserconf.txt
onboot
with one line:username:password
, where password is generated by copying the output of theopenssl passwd -6
command (Windows/Linux), oropenssl passwd -1
(Mac)ssh
onboot
Profit!