lgpio 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +55 -11
- data/examples/i2c_aht10.rb +36 -0
- data/examples/i2c_aht10_zip.rb +36 -0
- data/examples/i2c_ssd1306_bench.rb +25 -0
- data/examples/reports.rb +1 -0
- data/examples/rotary_encoder.rb +3 -0
- data/examples/rotary_encoder_led.rb +56 -0
- data/examples/spi_loopback.rb +18 -0
- data/examples/spi_read.rb +14 -0
- data/examples/spi_ws2812.rb +30 -0
- data/examples/spi_ws2812_bounce.rb +44 -0
- data/ext/lgpio/lgpio.c +187 -3
- data/lib/lgpio/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84493ab6ca425d960887480b8f186a610fd9e41f0885657c79b5a4038a8a2035
|
4
|
+
data.tar.gz: 705d88399e518bdbc167eca65cd795dea2d33936b919aaaf22a34c8be032a1a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77f3c42ed9a94f8ea0c59acc2cb276efd400990099ac4272f06619645953f6014dbc4a9330d04ed154eee86fabba4791290fcd7201b326f2d6f4b2437a5d5812
|
7
|
+
data.tar.gz: 7fd2aa6715e36f3e95d35248b1b13ffb141dca3c50b1ab7a5fbe2e420b67a3873d3de50a24b5eda460a50bd69133492043356715522b27cbe509e280c3174699
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,16 +1,60 @@
|
|
1
1
|
# lgpio
|
2
2
|
|
3
|
-
Ruby bindings for the [lgpio (lg)](https://github.com/joan2937/lg)
|
4
|
-
|
5
|
-
- [lg homepage](https://abyz.me.uk/lg/index.html)
|
6
|
-
|
7
|
-
### Progress:
|
3
|
+
Ruby bindings for the [lgpio (lg)](https://github.com/joan2937/lg) Linux library, running on single board computers (SBCs), like Raspberry Pi.
|
8
4
|
|
5
|
+
## Standard LGPIO Functions
|
9
6
|
- [x] GPIO Read/Write
|
10
7
|
- [x] GPIO Group Read/Write
|
11
|
-
- [x] GPIO Alerts / Callbacks
|
12
|
-
-
|
13
|
-
- [x]
|
14
|
-
-
|
15
|
-
- [
|
16
|
-
-
|
8
|
+
- [x] GPIO Alerts / Callbacks
|
9
|
+
- lg generates alerts at high speed, in a separate thread. In Ruby, they can be read from a queue as part of your application loop.
|
10
|
+
- [x] PWM Output
|
11
|
+
- Software timed on any pin. No interface for hardware PWM yet.
|
12
|
+
- [x] Wave
|
13
|
+
- Software timed on any pin, as with PWM.
|
14
|
+
- [x] I2C
|
15
|
+
- [x] SPI
|
16
|
+
|
17
|
+
## Extras
|
18
|
+
- [x] WS2812 over SPI
|
19
|
+
- Only outputs on a SPI MOSI pin. Must be able to set SPI clock frequency to 2.4 MHz.
|
20
|
+
- [ ] Bit Bang SPI
|
21
|
+
- [ ] Bit Bang I2C
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
On Debian-based Linuxes (RaspberryPi OS, Armbian, DietPi etc.):
|
25
|
+
```bash
|
26
|
+
sudo apt install swig python3-dev python3-setuptools
|
27
|
+
|
28
|
+
# NOTE: There's a bug with GPIO numbers > 255.
|
29
|
+
# If you need to use those, wget the second URL instead, until that fix is merged.
|
30
|
+
wget https://github.com/joan2937/lg/archive/master.zip
|
31
|
+
# wget https://github.com/vickash/lg/archive/refs/heads/master.zip
|
32
|
+
|
33
|
+
unzip master.zip
|
34
|
+
cd lg-master
|
35
|
+
make
|
36
|
+
sudo make install
|
37
|
+
|
38
|
+
gem install lgpio
|
39
|
+
```
|
40
|
+
|
41
|
+
## Enabling Hardware & Permissions
|
42
|
+
Depending on your SBC and Linux distro/version, you may need to manually enable I2C and SPI hardware. You should use the setup or config tool that came with your distro for that.
|
43
|
+
|
44
|
+
You may also not have permission to access the GPIO and peripherals. To run without `sudo`, you need read+write permission to some or all of the following devices:
|
45
|
+
```
|
46
|
+
/dev/gpiochip* (For GPIO, example: /dev/gpiochip0)
|
47
|
+
/dev/i2c-* (For I2C, example: /dev/i2c-1)
|
48
|
+
/dev/spidev* (For SPI, example: /dev/spidev-0.1)
|
49
|
+
```
|
50
|
+
|
51
|
+
## Documentation
|
52
|
+
- See examples folder for demos of every implemented interface.
|
53
|
+
- Development was done on an Orange Pi Zero 2W. Your GPIO numbers may be different, so change them.
|
54
|
+
- For more info, see the [lgpio C API docs.](https://abyz.me.uk/lg/lgpio.html)
|
55
|
+
- As much as possible, the Ruby methods closely follow the C API functions, except:
|
56
|
+
- Snake case instead of camel case names for methods.
|
57
|
+
- Method names have the leading `lg` removed, since inside the `LGPIO` class.
|
58
|
+
- Constants have leading `LG_` removed, as above.
|
59
|
+
- "count" or "length" arguments associated with array args are not needed.
|
60
|
+
- Check the return values of your method calls. On failure, they return negative values, matching the `LG_` error codes at the bottom of the C API doc page.
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
I2C_DEV = 2
|
4
|
+
POWER_ON_DELAY = 0.100
|
5
|
+
RESET_DELAY = 0.020
|
6
|
+
COMMAND_DELAY = 0.010
|
7
|
+
MEASURE_DELAY = 0.080
|
8
|
+
DATA_LENGTH = 6
|
9
|
+
SOFT_RESET = [0xBA]
|
10
|
+
INIT_AND_CALIBRATE = [0xE1, 0x08, 0x00]
|
11
|
+
START_MEASUREMENT = [0xAC, 0x33, 0x00]
|
12
|
+
|
13
|
+
aht10_handle = LGPIO.i2c_open(I2C_DEV, 0x38, 0)
|
14
|
+
|
15
|
+
# Startup sequence
|
16
|
+
sleep(POWER_ON_DELAY)
|
17
|
+
LGPIO.i2c_write_device(aht10_handle, SOFT_RESET)
|
18
|
+
sleep(RESET_DELAY)
|
19
|
+
LGPIO.i2c_write_device(aht10_handle, INIT_AND_CALIBRATE)
|
20
|
+
sleep(COMMAND_DELAY)
|
21
|
+
|
22
|
+
# Read and close
|
23
|
+
LGPIO.i2c_write_device(aht10_handle, START_MEASUREMENT)
|
24
|
+
sleep(MEASURE_DELAY)
|
25
|
+
bytes = LGPIO.i2c_read_device(aht10_handle, DATA_LENGTH)
|
26
|
+
LGPIO.i2c_close(aht10_handle)
|
27
|
+
|
28
|
+
# Humidity uses the upper 4 bits of the shared byte as its lowest 4 bits.
|
29
|
+
h_raw = ((bytes[1] << 16) | (bytes[2] << 8) | (bytes[3])) >> 4
|
30
|
+
humidity = (h_raw.to_f / 2**20) * 100
|
31
|
+
|
32
|
+
# Temperature uses the lower 4 bits of the shared byte as its highest 4 bits.
|
33
|
+
t_raw = ((bytes[3] & 0x0F) << 16) | (bytes[4] << 8) | bytes[5]
|
34
|
+
temperature = (t_raw.to_f / 2**20) * 200 - 50
|
35
|
+
|
36
|
+
puts "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - Temperature: #{temperature.round(2)} \xC2\xB0C | Humidity: #{humidity.round(2)} %"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
I2C_DEV = 2
|
4
|
+
POWER_ON_DELAY = 0.100
|
5
|
+
RESET_DELAY = 0.020
|
6
|
+
COMMAND_DELAY = 0.010
|
7
|
+
MEASURE_DELAY = 0.080
|
8
|
+
SOFT_RESET = [5, 1, 0xBA, 0]
|
9
|
+
INIT_AND_CALIBRATE = [5, 3, 0xE1, 0x08, 0x00, 0]
|
10
|
+
START_MEASUREMENT = [5, 3, 0xAC, 0x33, 0x00, 0]
|
11
|
+
READ_SIX = [4, 6, 0]
|
12
|
+
|
13
|
+
aht10_handle = LGPIO.i2c_open(I2C_DEV, 0x38, 0)
|
14
|
+
|
15
|
+
# Startup sequence
|
16
|
+
sleep(POWER_ON_DELAY)
|
17
|
+
LGPIO.i2c_zip(aht10_handle, SOFT_RESET, 0)
|
18
|
+
sleep(RESET_DELAY)
|
19
|
+
LGPIO.i2c_zip(aht10_handle, INIT_AND_CALIBRATE, 0)
|
20
|
+
sleep(COMMAND_DELAY)
|
21
|
+
|
22
|
+
# Read and close
|
23
|
+
LGPIO.i2c_zip(aht10_handle, START_MEASUREMENT, 0)
|
24
|
+
sleep(MEASURE_DELAY)
|
25
|
+
bytes = LGPIO.i2c_zip(aht10_handle, READ_SIX, 6)
|
26
|
+
LGPIO.i2c_close(aht10_handle)
|
27
|
+
|
28
|
+
# Humidity uses the upper 4 bits of the shared byte as its lowest 4 bits.
|
29
|
+
h_raw = ((bytes[1] << 16) | (bytes[2] << 8) | (bytes[3])) >> 4
|
30
|
+
humidity = (h_raw.to_f / 2**20) * 100
|
31
|
+
|
32
|
+
# Temperature uses the lower 4 bits of the shared byte as its highest 4 bits.
|
33
|
+
t_raw = ((bytes[3] & 0x0F) << 16) | (bytes[4] << 8) | bytes[5]
|
34
|
+
temperature = (t_raw.to_f / 2**20) * 200 - 50
|
35
|
+
|
36
|
+
puts "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - Temperature: #{temperature.round(2)} \xC2\xB0C | Humidity: #{humidity.round(2)} %"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
I2C_DEV = 2
|
4
|
+
INIT_ARRAY = [0, 168, 63, 211, 0, 64, 161, 200, 218, 18, 164, 166, 213, 128, 219, 32, 217, 241, 141, 20, 32, 0, 175]
|
5
|
+
START_ARRAY = [0, 33, 0, 127, 34, 0, 7]
|
6
|
+
BLANK_ARRAY = [64] + Array.new(1024) { 0 }
|
7
|
+
FILL_ARRAY = [64] + Array.new(1024) { 255 }
|
8
|
+
|
9
|
+
ssd1306_handle = LGPIO.i2c_open(I2C_DEV, 0x3C, 0)
|
10
|
+
LGPIO.i2c_write_device(ssd1306_handle, INIT_ARRAY)
|
11
|
+
FRAME_COUNT = 100
|
12
|
+
|
13
|
+
start = Time.now
|
14
|
+
(FRAME_COUNT / 2).times do
|
15
|
+
LGPIO.i2c_write_device(ssd1306_handle, START_ARRAY)
|
16
|
+
LGPIO.i2c_write_device(ssd1306_handle, FILL_ARRAY)
|
17
|
+
LGPIO.i2c_write_device(ssd1306_handle, START_ARRAY)
|
18
|
+
LGPIO.i2c_write_device(ssd1306_handle, BLANK_ARRAY)
|
19
|
+
end
|
20
|
+
finish = Time.now
|
21
|
+
|
22
|
+
LGPIO.i2c_close(ssd1306_handle)
|
23
|
+
|
24
|
+
fps = FRAME_COUNT / (finish - start)
|
25
|
+
puts "SSD1306 benchmark result: #{fps.round(2)} fps"
|
data/examples/reports.rb
CHANGED
data/examples/rotary_encoder.rb
CHANGED
@@ -18,12 +18,15 @@ chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
|
18
18
|
|
19
19
|
# Encoder pin setup
|
20
20
|
LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_A)
|
21
|
+
LGPIO.gpio_set_debounce(chip_handle, PIN_A, 1)
|
21
22
|
LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_A)
|
22
23
|
LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_B)
|
24
|
+
LGPIO.gpio_set_debounce(chip_handle, PIN_B, 1)
|
23
25
|
LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_B)
|
24
26
|
|
25
27
|
# Switch pin setup
|
26
28
|
LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_UP, PIN_SW)
|
29
|
+
LGPIO.gpio_set_debounce(chip_handle, PIN_SW, 1)
|
27
30
|
LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::FALLING_EDGE, PIN_SW)
|
28
31
|
|
29
32
|
# Start generating reports for GPIO level changes.
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# Demo of a simple 30-detent rotary encoder.
|
3
|
+
# PIN_A = CLK/CLOCK, PIN_B = DT/DATA, PIN_SW = SWITCH
|
4
|
+
#
|
5
|
+
require 'lgpio'
|
6
|
+
|
7
|
+
GPIO_CHIP = 0
|
8
|
+
PIN_A = 76
|
9
|
+
PIN_B = 228
|
10
|
+
|
11
|
+
PIN_LED = 260
|
12
|
+
PWM_FREQ = 500
|
13
|
+
PWM_OFFSET = 0
|
14
|
+
PWM_CYCLES = 0 # 0 = infinite
|
15
|
+
|
16
|
+
# Encoder state
|
17
|
+
state_a = 0
|
18
|
+
state_b = 0
|
19
|
+
led_duty = 0
|
20
|
+
|
21
|
+
chip_handle = LGPIO.chip_open(GPIO_CHIP)
|
22
|
+
|
23
|
+
# LED pin setup
|
24
|
+
LGPIO.gpio_claim_output(chip_handle, LGPIO::SET_PULL_NONE, PIN_LED, LGPIO::LOW)
|
25
|
+
|
26
|
+
# Encoder pin setup
|
27
|
+
LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_A)
|
28
|
+
LGPIO.gpio_set_debounce(chip_handle, PIN_A, 1)
|
29
|
+
LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_A)
|
30
|
+
LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_B)
|
31
|
+
LGPIO.gpio_set_debounce(chip_handle, PIN_B, 1)
|
32
|
+
LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_B)
|
33
|
+
|
34
|
+
# Start generating reports for GPIO level changes.
|
35
|
+
LGPIO.gpio_start_reporting
|
36
|
+
|
37
|
+
# Get and reports to update state.
|
38
|
+
loop do
|
39
|
+
report = LGPIO.gpio_get_report
|
40
|
+
if report
|
41
|
+
if report[:gpio] == PIN_A
|
42
|
+
# Half quadrature, so we count every detent.
|
43
|
+
state_a = report[:level]
|
44
|
+
elsif report[:gpio] == PIN_B
|
45
|
+
delta = (report[:level] == state_a) ? -1 : 1
|
46
|
+
state_b = report[:level]
|
47
|
+
|
48
|
+
led_duty += delta
|
49
|
+
led_duty = 0 if led_duty < 0
|
50
|
+
led_duty = 100 if led_duty > 100
|
51
|
+
LGPIO.tx_pwm(chip_handle, PIN_LED, PWM_FREQ, led_duty, PWM_OFFSET, PWM_CYCLES)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
sleep(0.001)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
SPI_DEV = 1
|
4
|
+
SPI_CHAN = 0
|
5
|
+
SPI_MODE = 0
|
6
|
+
SPI_BAUD = 1_000_000
|
7
|
+
|
8
|
+
spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
|
9
|
+
|
10
|
+
tx_bytes = [0, 1, 2, 3, 4, 5, 6, 7]
|
11
|
+
puts "TX bytes: #{tx_bytes.inspect}"
|
12
|
+
|
13
|
+
# rx_bytes == tx_bytes if MOSI looped back to MISO.
|
14
|
+
# rx_byte all 255 when MOSI and MISO not connected.
|
15
|
+
rx_bytes = LGPIO.spi_xfer(spi_handle, tx_bytes)
|
16
|
+
puts "RX bytes: #{rx_bytes.inspect}"
|
17
|
+
|
18
|
+
LGPIO.spi_close(spi_handle)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'lgpio'
|
2
|
+
|
3
|
+
SPI_DEV = 1
|
4
|
+
SPI_CHAN = 0
|
5
|
+
SPI_MODE = 0
|
6
|
+
SPI_BAUD = 1_000_000
|
7
|
+
|
8
|
+
spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
|
9
|
+
|
10
|
+
# Pull MISO high or low. High gives array of 255, low array of 0.
|
11
|
+
rx_bytes = LGPIO.spi_read(spi_handle, 8)
|
12
|
+
puts "RX bytes: #{rx_bytes.inspect}"
|
13
|
+
|
14
|
+
LGPIO.spi_close(spi_handle)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#
|
2
|
+
# 2.4 MHz SPI method for writing WS2812 addressable LEDs.
|
3
|
+
# Based on: https://learn.adafruit.com/dma-driven-neopixels/overview
|
4
|
+
#
|
5
|
+
require 'lgpio'
|
6
|
+
|
7
|
+
# SPI Config
|
8
|
+
SPI_DEV = 1
|
9
|
+
SPI_CHAN = 0
|
10
|
+
SPI_MODE = 0
|
11
|
+
SPI_BAUD = 2_400_000
|
12
|
+
|
13
|
+
# 4 pixels: RGBW. Data order per pixel is GRB.
|
14
|
+
pixels_on = [
|
15
|
+
0, 255, 0,
|
16
|
+
255, 0, 0,
|
17
|
+
0, 0, 255,
|
18
|
+
255, 255, 255,
|
19
|
+
]
|
20
|
+
# 4 pixels, all off.
|
21
|
+
pixels_off = Array.new(12) { 0 }
|
22
|
+
|
23
|
+
spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
|
24
|
+
|
25
|
+
loop do
|
26
|
+
LGPIO.spi_ws2812_write(spi_handle, pixels_on)
|
27
|
+
sleep 0.5
|
28
|
+
LGPIO.spi_ws2812_write(spi_handle, pixels_off)
|
29
|
+
sleep 0.5
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# 2.4 MHz SPI method for writing WS2812 addressable LEDs.
|
3
|
+
# Based on: https://learn.adafruit.com/dma-driven-neopixels/overview
|
4
|
+
#
|
5
|
+
require 'lgpio'
|
6
|
+
|
7
|
+
# SPI config
|
8
|
+
SPI_DEV = 1
|
9
|
+
SPI_CHAN = 0
|
10
|
+
SPI_MODE = 0
|
11
|
+
SPI_BAUD = 2_400_000
|
12
|
+
|
13
|
+
PIXEL_COUNT = 8
|
14
|
+
COLORS = [
|
15
|
+
[255, 255, 255],
|
16
|
+
[0, 255, 0],
|
17
|
+
[255, 0, 0],
|
18
|
+
[0, 0, 255],
|
19
|
+
[255, 255, 0],
|
20
|
+
[255, 0, 255],
|
21
|
+
[0, 255, 255]
|
22
|
+
]
|
23
|
+
|
24
|
+
# Move along the strip and back, one pixel at a time.
|
25
|
+
POSITIONS = (0..PIXEL_COUNT-1).to_a + (1..PIXEL_COUNT-2).to_a.reverse
|
26
|
+
|
27
|
+
pixels = Array.new(PIXEL_COUNT) { [0, 0, 0] }
|
28
|
+
spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
|
29
|
+
|
30
|
+
loop do
|
31
|
+
COLORS.each do |color|
|
32
|
+
POSITIONS.each do |index|
|
33
|
+
# Clear and write.
|
34
|
+
(0..PIXEL_COUNT-1).each { |i| pixels[i] = [0, 0, 0] }
|
35
|
+
LGPIO.spi_ws2812_write(spi_handle, pixels.flatten)
|
36
|
+
|
37
|
+
# Set one pixel and write.
|
38
|
+
pixels[index] = color
|
39
|
+
LGPIO.spi_ws2812_write(spi_handle, pixels.flatten)
|
40
|
+
|
41
|
+
sleep 0.05
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/ext/lgpio/lgpio.c
CHANGED
@@ -83,6 +83,11 @@ static VALUE group_write(VALUE self, VALUE handle, VALUE gpio, VALUE bits, VALUE
|
|
83
83
|
return INT2NUM(result);
|
84
84
|
}
|
85
85
|
|
86
|
+
static VALUE gpio_set_debounce(VALUE self, VALUE handle, VALUE gpio, VALUE debounce) {
|
87
|
+
int result = lgGpioSetDebounce(NUM2INT(handle), NUM2INT(gpio), NUM2INT(debounce));
|
88
|
+
return INT2NUM(result);
|
89
|
+
}
|
90
|
+
|
86
91
|
static VALUE gpio_claim_alert(VALUE self, VALUE handle, VALUE flags, VALUE eFlags, VALUE gpio) {
|
87
92
|
int result = lgGpioClaimAlert(NUM2INT(handle), NUM2INT(flags), NUM2INT(eFlags), NUM2INT(gpio), -1);
|
88
93
|
return INT2NUM(result);
|
@@ -101,12 +106,13 @@ void queue_gpio_reports(int count, lgGpioAlert_p events, void *data){
|
|
101
106
|
|
102
107
|
static VALUE gpio_start_reporting(VALUE self) {
|
103
108
|
lgGpioSetSamplesFunc(queue_gpio_reports, NULL);
|
109
|
+
return Qnil;
|
104
110
|
}
|
105
111
|
|
106
|
-
static VALUE gpio_get_report(VALUE self){
|
112
|
+
static VALUE gpio_get_report(VALUE self){
|
107
113
|
VALUE hash = rb_hash_new();
|
108
114
|
bool popped = false;
|
109
|
-
|
115
|
+
|
110
116
|
pthread_mutex_lock(&queueLock);
|
111
117
|
// qWritePos is where the NEXT report will go. Always trail it by 1.
|
112
118
|
if (qWritePos - qReadPos != 1){
|
@@ -170,6 +176,168 @@ static VALUE tx_wave(VALUE self, VALUE handle, VALUE lead_gpio, VALUE pulses) {
|
|
170
176
|
return INT2NUM(result);
|
171
177
|
}
|
172
178
|
|
179
|
+
static VALUE i2c_open(VALUE self, VALUE i2cDev, VALUE i2cAddr, VALUE i2cFlags){
|
180
|
+
int handle = lgI2cOpen(NUM2INT(i2cDev), NUM2INT(i2cAddr), NUM2INT(i2cFlags));
|
181
|
+
return INT2NUM(handle);
|
182
|
+
}
|
183
|
+
|
184
|
+
static VALUE i2c_close(VALUE self, VALUE handle){
|
185
|
+
int result = lgI2cClose(NUM2INT(handle));
|
186
|
+
return INT2NUM(result);
|
187
|
+
}
|
188
|
+
|
189
|
+
static VALUE i2c_write_device(VALUE self, VALUE handle, VALUE byteArray){
|
190
|
+
int count = RARRAY_LEN(byteArray);
|
191
|
+
uint8_t txBuf[count];
|
192
|
+
VALUE currentByte;
|
193
|
+
for(int i=0; i<count; i++){
|
194
|
+
currentByte = rb_ary_entry(byteArray, i);
|
195
|
+
Check_Type(currentByte, T_FIXNUM);
|
196
|
+
txBuf[i] = NUM2CHR(currentByte);
|
197
|
+
}
|
198
|
+
|
199
|
+
int result = lgI2cWriteDevice(NUM2INT(handle), txBuf, count);
|
200
|
+
return INT2NUM(result);
|
201
|
+
}
|
202
|
+
|
203
|
+
static VALUE i2c_read_device(VALUE self, VALUE handle, VALUE count){
|
204
|
+
int rxCount = NUM2INT(count);
|
205
|
+
uint8_t rxBuf[rxCount];
|
206
|
+
|
207
|
+
int result = lgI2cReadDevice(NUM2INT(handle), rxBuf, rxCount);
|
208
|
+
if(result < 0) return INT2NUM(result);
|
209
|
+
|
210
|
+
VALUE retArray = rb_ary_new2(rxCount);
|
211
|
+
for(int i=0; i<rxCount; i++){
|
212
|
+
rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
|
213
|
+
}
|
214
|
+
return retArray;
|
215
|
+
}
|
216
|
+
|
217
|
+
static VALUE i2c_zip(VALUE self, VALUE handle, VALUE txArray, VALUE rb_rxCount){
|
218
|
+
int txCount = RARRAY_LEN(txArray);
|
219
|
+
uint8_t txBuf[txCount];
|
220
|
+
VALUE currentByte;
|
221
|
+
for(int i=0; i<txCount; i++){
|
222
|
+
currentByte = rb_ary_entry(txArray, i);
|
223
|
+
Check_Type(currentByte, T_FIXNUM);
|
224
|
+
txBuf[i] = NUM2CHR(currentByte);
|
225
|
+
}
|
226
|
+
|
227
|
+
int rxCount = NUM2INT(rb_rxCount);
|
228
|
+
uint8_t rxBuf[rxCount+1];
|
229
|
+
|
230
|
+
// Buffer size must be rxCount+1 or result is LG_BAD_I2C_RLEN
|
231
|
+
int result = lgI2cZip(NUM2INT(handle), txBuf, txCount, rxBuf, rxCount+1);
|
232
|
+
if(result < 0) return INT2NUM(result);
|
233
|
+
|
234
|
+
if (rxCount == 0) return Qnil;
|
235
|
+
VALUE retArray = rb_ary_new2(rxCount);
|
236
|
+
for(int i=0; i<rxCount; i++){
|
237
|
+
rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
|
238
|
+
}
|
239
|
+
return retArray;
|
240
|
+
}
|
241
|
+
|
242
|
+
static VALUE spi_open(VALUE self, VALUE spiDev, VALUE spiChan, VALUE spiBaud, VALUE spiFlags){
|
243
|
+
int handle = lgSpiOpen(NUM2INT(spiDev), NUM2INT(spiChan), NUM2INT(spiBaud), NUM2INT(spiFlags));
|
244
|
+
return INT2NUM(handle);
|
245
|
+
}
|
246
|
+
|
247
|
+
static VALUE spi_close(VALUE self, VALUE handle){
|
248
|
+
int result = lgSpiClose(NUM2INT(handle));
|
249
|
+
return INT2NUM(result);
|
250
|
+
}
|
251
|
+
|
252
|
+
static VALUE spi_read(VALUE self, VALUE handle, VALUE rxCount){
|
253
|
+
int count = NUM2INT(rxCount);
|
254
|
+
|
255
|
+
// Not sure if this needs null termination like I2C. +1 won't hurt.
|
256
|
+
uint8_t rxBuf[count+1];
|
257
|
+
|
258
|
+
int result = lgSpiRead(NUM2INT(handle), rxBuf, count);
|
259
|
+
if(result < 0) return INT2NUM(result);
|
260
|
+
|
261
|
+
VALUE retArray = rb_ary_new2(count);
|
262
|
+
for(int i=0; i<count; i++){
|
263
|
+
rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
|
264
|
+
}
|
265
|
+
return retArray;
|
266
|
+
}
|
267
|
+
|
268
|
+
static VALUE spi_write(VALUE self, VALUE handle, VALUE txArray){
|
269
|
+
int count = RARRAY_LEN(txArray);
|
270
|
+
uint8_t txBuf[count];
|
271
|
+
VALUE currentByte;
|
272
|
+
for(int i=0; i<count; i++){
|
273
|
+
currentByte = rb_ary_entry(txArray, i);
|
274
|
+
Check_Type(currentByte, T_FIXNUM);
|
275
|
+
txBuf[i] = NUM2CHR(currentByte);
|
276
|
+
}
|
277
|
+
|
278
|
+
int result = lgSpiWrite(NUM2INT(handle), txBuf, count);
|
279
|
+
return INT2NUM(result);
|
280
|
+
}
|
281
|
+
|
282
|
+
static VALUE spi_xfer(VALUE self, VALUE handle, VALUE txArray){
|
283
|
+
int count = RARRAY_LEN(txArray);
|
284
|
+
uint8_t txBuf[count];
|
285
|
+
VALUE currentByte;
|
286
|
+
for(int i=0; i<count; i++){
|
287
|
+
currentByte = rb_ary_entry(txArray, i);
|
288
|
+
Check_Type(currentByte, T_FIXNUM);
|
289
|
+
txBuf[i] = NUM2CHR(currentByte);
|
290
|
+
}
|
291
|
+
|
292
|
+
// Not sure if this needs null termination like I2C. +1 won't hurt.
|
293
|
+
uint8_t rxBuf[count+1];
|
294
|
+
|
295
|
+
int result = lgSpiXfer(NUM2INT(handle), txBuf, rxBuf, count);
|
296
|
+
if(result < 0) return INT2NUM(result);
|
297
|
+
|
298
|
+
VALUE retArray = rb_ary_new2(count);
|
299
|
+
for(int i=0; i<count; i++){
|
300
|
+
rb_ary_store(retArray, i, UINT2NUM(rxBuf[i]));
|
301
|
+
}
|
302
|
+
return retArray;
|
303
|
+
}
|
304
|
+
|
305
|
+
static VALUE spi_ws2812_write(VALUE self, VALUE handle, VALUE pixelArray){
|
306
|
+
int count = RARRAY_LEN(pixelArray);
|
307
|
+
|
308
|
+
// Pull low for at least one byte at 2.4 Mhz before data, and 90 after.
|
309
|
+
int zeroesBefore = 1;
|
310
|
+
int zeroesAfter = 90;
|
311
|
+
int txBufLength = zeroesBefore + (count*3) + zeroesAfter;
|
312
|
+
uint8_t txBuf[txBufLength];
|
313
|
+
for (int i=0; i<txBufLength; i++) { txBuf[i] = 0; }
|
314
|
+
|
315
|
+
VALUE currentByte_rb;
|
316
|
+
uint8_t currentByte;
|
317
|
+
uint8_t currentBit;
|
318
|
+
uint32_t temp;
|
319
|
+
|
320
|
+
for (int i=0; i<count; i++){
|
321
|
+
temp = 0;
|
322
|
+
currentByte_rb = rb_ary_entry(pixelArray, i);
|
323
|
+
Check_Type(currentByte_rb, T_FIXNUM);
|
324
|
+
currentByte = NUM2CHR(currentByte_rb);
|
325
|
+
|
326
|
+
for (int i=7; i>=0; i--) {
|
327
|
+
currentBit = (currentByte & (1 << i));
|
328
|
+
temp = temp << 3;
|
329
|
+
temp = (currentBit == 0) ? (temp | 0b100) : (temp | 0b110);
|
330
|
+
}
|
331
|
+
|
332
|
+
txBuf[zeroesBefore+(i*3)] = (temp >> 16) & 0xFF;
|
333
|
+
txBuf[zeroesBefore+(i*3)+1] = (temp >> 8) & 0xFF;
|
334
|
+
txBuf[zeroesBefore+(i*3)+2] = temp & 0xFF;
|
335
|
+
}
|
336
|
+
|
337
|
+
int result = lgSpiWrite(NUM2INT(handle), txBuf, txBufLength);
|
338
|
+
return INT2NUM(result);
|
339
|
+
}
|
340
|
+
|
173
341
|
void Init_lgpio(void) {
|
174
342
|
// Modules
|
175
343
|
VALUE mLGPIO = rb_define_module("LGPIO");
|
@@ -198,8 +366,9 @@ void Init_lgpio(void) {
|
|
198
366
|
rb_define_singleton_method(mLGPIO, "group_free", group_free, 2);
|
199
367
|
rb_define_singleton_method(mLGPIO, "group_read", group_read, 2);
|
200
368
|
rb_define_singleton_method(mLGPIO, "group_write", group_write, 4);
|
201
|
-
|
369
|
+
|
202
370
|
// Alerts / Reports
|
371
|
+
rb_define_singleton_method(mLGPIO, "gpio_set_debounce", gpio_set_debounce, 3);
|
203
372
|
rb_define_singleton_method(mLGPIO, "gpio_claim_alert", gpio_claim_alert, 4);
|
204
373
|
rb_define_singleton_method(mLGPIO, "gpio_start_reporting", gpio_start_reporting, 0);
|
205
374
|
rb_define_singleton_method(mLGPIO, "gpio_get_report", gpio_get_report, 0);
|
@@ -213,4 +382,19 @@ void Init_lgpio(void) {
|
|
213
382
|
rb_define_singleton_method(mLGPIO, "tx_pwm", tx_pwm, 6);
|
214
383
|
rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
|
215
384
|
rb_define_singleton_method(mLGPIO, "tx_wave", tx_wave, 3);
|
385
|
+
|
386
|
+
// I2C
|
387
|
+
rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
|
388
|
+
rb_define_singleton_method(mLGPIO, "i2c_close", i2c_close, 1);
|
389
|
+
rb_define_singleton_method(mLGPIO, "i2c_write_device", i2c_write_device, 2);
|
390
|
+
rb_define_singleton_method(mLGPIO, "i2c_read_device", i2c_read_device, 2);
|
391
|
+
rb_define_singleton_method(mLGPIO, "i2c_zip", i2c_zip, 3);
|
392
|
+
|
393
|
+
// SPI
|
394
|
+
rb_define_singleton_method(mLGPIO, "spi_open", spi_open, 4);
|
395
|
+
rb_define_singleton_method(mLGPIO, "spi_close", spi_close, 1);
|
396
|
+
rb_define_singleton_method(mLGPIO, "spi_read", spi_read, 2);
|
397
|
+
rb_define_singleton_method(mLGPIO, "spi_write", spi_write, 2);
|
398
|
+
rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
|
399
|
+
rb_define_singleton_method(mLGPIO, "spi_ws2812_write", spi_ws2812_write, 2);
|
216
400
|
}
|
data/lib/lgpio/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lgpio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vickash
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Use GPIO / PWM / I2C / SPI / UART on Linux SBCs in Ruby
|
14
14
|
email: mail@vickash.com
|
@@ -26,10 +26,18 @@ files:
|
|
26
26
|
- examples/blink.rb
|
27
27
|
- examples/group_in.rb
|
28
28
|
- examples/group_out.rb
|
29
|
+
- examples/i2c_aht10.rb
|
30
|
+
- examples/i2c_aht10_zip.rb
|
31
|
+
- examples/i2c_ssd1306_bench.rb
|
29
32
|
- examples/momentary.rb
|
30
33
|
- examples/pwm.rb
|
31
34
|
- examples/reports.rb
|
32
35
|
- examples/rotary_encoder.rb
|
36
|
+
- examples/rotary_encoder_led.rb
|
37
|
+
- examples/spi_loopback.rb
|
38
|
+
- examples/spi_read.rb
|
39
|
+
- examples/spi_ws2812.rb
|
40
|
+
- examples/spi_ws2812_bounce.rb
|
33
41
|
- examples/wave.rb
|
34
42
|
- ext/lgpio/extconf.rb
|
35
43
|
- ext/lgpio/lgpio.c
|