lgpio 0.1.2 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|