lgpio 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d173e84c707ec6e3d84715dbc7c56287a22d735fbbf118adc4d3b602513b852e
4
- data.tar.gz: cb8d9acbdc1ed6da8fe9f037f8b837ee989b50d48b173980165b34e5c19d9b92
3
+ metadata.gz: e24e221cb39d27714d3fdd9486c47dd01e33115980d29e5104a455e14fd40890
4
+ data.tar.gz: 4ffd6e35644bc8dcde49486cd43d8e7ca73ebbfe9b46955a8de89a4ecff49d33
5
5
  SHA512:
6
- metadata.gz: 4959700837149d70e16f550cbd02e13d03b4507ec1b50910c45bf7809e2c711547939ad50d2ba559c0b167befb6c670202e5de371bbe40bf764032d580e94830
7
- data.tar.gz: 360a46aa061edb191ce80f67be784ac4edae21d7126cb4e05c3ce3812d416d2daa6f804ecfa532c790637d51f63010e01a07277971897fa26d7442f1c581a251
6
+ metadata.gz: 5060a2fd7a5ef20a1de29f01a87aa2ff2639c9d0e9005594de672d7fa011df48e7a65b893ad4310d07d780f9677c1544ccc80018a5bb4794e014513f4864fae4
7
+ data.tar.gz: 16e6a32af1a358ffcf18c7864d2748527f635a83f42734fc3191ecb6eed7fb50cef3ffe4370651b063fde6e4a28bd2075a12b6adb8d04b3e4646353d973515c3
data/README.md CHANGED
@@ -1,16 +1,54 @@
1
1
  # lgpio
2
2
 
3
- Ruby bindings for the [lgpio (lg)](https://github.com/joan2937/lg) C library, which provides GPIO, PWM, I2C, SPI and Serial features on single-board-computers (SBCs) running Linux.
3
+ Ruby bindings for the [lgpio (lg)](https://github.com/joan2937/lg) C library, for Linux on single board computers (SBCs), like the Raspberry Pi and its competitors.
4
4
 
5
- - [lg homepage](https://abyz.me.uk/lg/index.html)
5
+ ## Features
6
+ - GPIO Read/Write
7
+ - GPIO Group Read/Write
8
+ - 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
+ - PWM Output
11
+ - Software timed on any pin. No interface for hardware PWM yet.
12
+ - Wave
13
+ - Software timed on any pin, as with PWM.
14
+ - I2C
15
+ - SPI
6
16
 
7
- ### Progress:
17
+ ## Installation
18
+ On Debian-based Linuxes (RaspberryPi OS, Armbian, DietPi etc.):
19
+ ```bash
20
+ sudo apt install swig python3-dev python3-setuptools
8
21
 
9
- - [x] GPIO Read/Write
10
- - [x] GPIO Group Read/Write
11
- - [ ] GPIO Callbacks
12
- - [x] PWM (software-timed)
13
- - [x] Wave (software-timed)
14
- - [ ] I2C
15
- - [ ] SPI
16
- - [ ] Serial
22
+ # NOTE: There's a bug with GPIO numbers > 255.
23
+ # If you need to use those, wget the second URL instead, until that fix is merged.
24
+ wget https://github.com/joan2937/lg/archive/master.zip
25
+ # wget https://github.com/vickash/lg/archive/refs/heads/master.zip
26
+
27
+ unzip master.zip
28
+ cd lg-master
29
+ make
30
+ sudo make install
31
+
32
+ gem install lgpio
33
+ ```
34
+
35
+ ## Enabling Hardware & Permissions
36
+ Depending on your SBC and Linux distro/version, you may need to manually enable I2C and SPI hardware. You may use the setup or config tool that came with your distro for that.
37
+
38
+ You may also not have permission to access some or all of the GPIO and peripherals. To run without `sudo`, you will need read+write permission to some or all of the following devices:
39
+ ```
40
+ /dev/gpiochip* (For GPIO, example: /dev/gpiochip0)
41
+ /dev/i2c-* (For I2C, example: /dev/i2c-1)
42
+ /dev/spidev* (For SPI, example: /dev/spidev-0.1)
43
+ ```
44
+
45
+ ## Documentation
46
+ - See examples folder for demos of every implemented interface.
47
+ - Development was done on an Orange Pi Zero 2W. Your GPIO numbers will be different, so change them.
48
+ - For more info, see the [lgpio C API docs.](https://abyz.me.uk/lg/lgpio.html)
49
+ - As much as possible, the Ruby methods closely follow the C API functions, except:
50
+ - Snake case instead of camel case names for methods.
51
+ - Method names have the leading `lg` removed, since inside the `LGPIO` class.
52
+ - Constants have leading `LG_` removed, as above.
53
+ - "count" or "length" arguments associated with array args are not needed.
54
+ - Check the return values of your method calls. On failure, they return negative values 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"
@@ -0,0 +1,19 @@
1
+ require 'lgpio'
2
+
3
+ GPIO_CHIP = 0
4
+ PIN = 76
5
+
6
+ chip_handle = LGPIO.chip_open(GPIO_CHIP)
7
+
8
+ LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN)
9
+ LGPIO.gpio_set_debounce(chip_handle, PIN, 1)
10
+ LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN)
11
+ LGPIO.gpio_start_reporting
12
+
13
+ loop do
14
+ report = LGPIO.gpio_get_report
15
+ report ? puts(report) : sleep(0.001)
16
+ end
17
+
18
+ LGPIO.gpio_free(chip_handle, PIN)
19
+ LGPIO.chip_close(chip_handle)
@@ -0,0 +1,54 @@
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
+ PIN_SW = 226
11
+
12
+ # Encoder state
13
+ position = 0
14
+ state_a = 0
15
+ state_b = 0
16
+
17
+ chip_handle = LGPIO.chip_open(GPIO_CHIP)
18
+
19
+ # Encoder pin setup
20
+ LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_A)
21
+ LGPIO.gpio_set_debounce(chip_handle, PIN_A, 1)
22
+ LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_A)
23
+ LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN_B)
24
+ LGPIO.gpio_set_debounce(chip_handle, PIN_B, 1)
25
+ LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN_B)
26
+
27
+ # Switch pin setup
28
+ LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_UP, PIN_SW)
29
+ LGPIO.gpio_set_debounce(chip_handle, PIN_SW, 1)
30
+ LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::FALLING_EDGE, PIN_SW)
31
+
32
+ # Start generating reports for GPIO level changes.
33
+ LGPIO.gpio_start_reporting
34
+
35
+ # Get and reports to update state.
36
+ loop do
37
+ report = LGPIO.gpio_get_report
38
+ if report
39
+ if report[:gpio] == PIN_A
40
+ # Half quadrature, so we count every detent.
41
+ state_a = report[:level]
42
+ elsif report[:gpio] == PIN_B
43
+ delta = (report[:level] == state_a) ? -1 : 1
44
+ position += delta
45
+ state_b = report[:level]
46
+ puts "Position: #{position}"
47
+ elsif report[:gpio] == PIN_SW
48
+ position = 0
49
+ puts "Position: 0"
50
+ end
51
+ else
52
+ sleep(0.001)
53
+ end
54
+ end
@@ -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,60 @@
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
+ # WS2812 Constants
8
+ NP_ONE = 0b110
9
+ NP_ZERO = 0b100
10
+ NP_START = [0]
11
+ NP_END = Array.new(90) { 0 }
12
+ BITS = (0..7).to_a.reverse
13
+
14
+ # SPI Config
15
+ SPI_DEV = 1
16
+ SPI_CHAN = 0
17
+ SPI_MODE = 0
18
+ SPI_BAUD = 2_400_000
19
+
20
+ # Map array of pixel values (3 bytes each) to raw SPI bytes (1bit -> 3 bits).
21
+ def pixel_to_raw_spi_bytes(pixel_arr)
22
+ raw_spi_bytes = []
23
+ pixel_arr.flatten.each do |byte|
24
+ long = 0b0
25
+ for i in BITS do
26
+ long = long << 3
27
+ if byte[i] == 0
28
+ long |= NP_ZERO
29
+ else
30
+ long |= NP_ONE
31
+ end
32
+ end
33
+ # Pack as big-endian uint32, then unpack to bytes, taking lowest 24 bits only.
34
+ long = [long].pack('L>')
35
+ raw_spi_bytes << long.unpack('C*')[1..3]
36
+ end
37
+ return raw_spi_bytes.flatten
38
+ end
39
+
40
+ # 4 pixels: RGBW. Data order per pixel is GRB.
41
+ pixels_on = [
42
+ 0, 255, 0,
43
+ 255, 0, 0,
44
+ 0, 0, 255,
45
+ 255, 255, 255,
46
+ ]
47
+ data_on = NP_START + pixel_to_raw_spi_bytes(pixels_on) + NP_END
48
+
49
+ # 4 pixels, all off.
50
+ pixels_off = Array.new(12) { 0 }
51
+ data_off = NP_START + pixel_to_raw_spi_bytes(pixels_off) + NP_END
52
+
53
+ spi_handle = LGPIO.spi_open(SPI_DEV, SPI_CHAN, SPI_BAUD, SPI_MODE)
54
+
55
+ loop do
56
+ LGPIO.spi_write(spi_handle, data_on)
57
+ sleep 0.5
58
+ LGPIO.spi_write(spi_handle, data_off)
59
+ sleep 0.5
60
+ end
data/ext/lgpio/lgpio.c CHANGED
@@ -1,6 +1,13 @@
1
1
  #include <lgpio.h>
2
2
  #include <ruby.h>
3
3
 
4
+ // Set up a queue for up to 2**16 GPIO reports.
5
+ static pthread_mutex_t queueLock;
6
+ #define QUEUE_LENGTH UINT16_MAX + 1
7
+ static lgGpioReport_t reportQueue[QUEUE_LENGTH];
8
+ static uint16_t qWritePos = 1;
9
+ static uint16_t qReadPos = 0;
10
+
4
11
  static VALUE chip_open(VALUE self, VALUE gpio_dev) {
5
12
  int result = lgGpiochipOpen(NUM2INT(gpio_dev));
6
13
  return INT2NUM(result);
@@ -76,6 +83,56 @@ static VALUE group_write(VALUE self, VALUE handle, VALUE gpio, VALUE bits, VALUE
76
83
  return INT2NUM(result);
77
84
  }
78
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
+
91
+ static VALUE gpio_claim_alert(VALUE self, VALUE handle, VALUE flags, VALUE eFlags, VALUE gpio) {
92
+ int result = lgGpioClaimAlert(NUM2INT(handle), NUM2INT(flags), NUM2INT(eFlags), NUM2INT(gpio), -1);
93
+ return INT2NUM(result);
94
+ }
95
+
96
+ void queue_gpio_reports(int count, lgGpioAlert_p events, void *data){
97
+ pthread_mutex_lock(&queueLock);
98
+ for(int i=0; i<count; i++) {
99
+ memcpy(&reportQueue[qWritePos], &events[i].report, sizeof(lgGpioReport_t));
100
+ qWritePos += 1;
101
+ // qReadPos is the LAST report read. If passing by 1, increment it too. Lose oldest data first.
102
+ if (qWritePos - qReadPos == 1) qReadPos += 1;
103
+ }
104
+ pthread_mutex_unlock(&queueLock);
105
+ }
106
+
107
+ static VALUE gpio_start_reporting(VALUE self) {
108
+ lgGpioSetSamplesFunc(queue_gpio_reports, NULL);
109
+ return Qnil;
110
+ }
111
+
112
+ static VALUE gpio_get_report(VALUE self){
113
+ VALUE hash = rb_hash_new();
114
+ bool popped = false;
115
+
116
+ pthread_mutex_lock(&queueLock);
117
+ // qWritePos is where the NEXT report will go. Always trail it by 1.
118
+ if (qWritePos - qReadPos != 1){
119
+ qReadPos += 1;
120
+ rb_hash_aset(hash, ID2SYM(rb_intern("timestamp")), ULL2NUM(reportQueue[qReadPos].timestamp));
121
+ rb_hash_aset(hash, ID2SYM(rb_intern("chip")), UINT2NUM(reportQueue[qReadPos].chip));
122
+ rb_hash_aset(hash, ID2SYM(rb_intern("gpio")), UINT2NUM(reportQueue[qReadPos].gpio));
123
+ rb_hash_aset(hash, ID2SYM(rb_intern("level")), UINT2NUM(reportQueue[qReadPos].level));
124
+ rb_hash_aset(hash, ID2SYM(rb_intern("flags")), UINT2NUM(reportQueue[qReadPos].flags));
125
+ popped = true;
126
+ }
127
+ pthread_mutex_unlock(&queueLock);
128
+
129
+ if (popped){
130
+ return hash;
131
+ } else {
132
+ return Qnil;
133
+ }
134
+ }
135
+
79
136
  static VALUE tx_busy(VALUE self, VALUE handle, VALUE gpio, VALUE kind) {
80
137
  int result = lgTxBusy(NUM2INT(handle), NUM2INT(gpio), NUM2INT(kind));
81
138
  return INT2NUM(result);
@@ -119,6 +176,132 @@ static VALUE tx_wave(VALUE self, VALUE handle, VALUE lead_gpio, VALUE pulses) {
119
176
  return INT2NUM(result);
120
177
  }
121
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
+
122
305
  void Init_lgpio(void) {
123
306
  // Modules
124
307
  VALUE mLGPIO = rb_define_module("LGPIO");
@@ -130,6 +313,9 @@ void Init_lgpio(void) {
130
313
  rb_define_const(mLGPIO, "SET_PULL_UP", INT2NUM(LG_SET_PULL_UP));
131
314
  rb_define_const(mLGPIO, "SET_PULL_DOWN", INT2NUM(LG_SET_PULL_DOWN));
132
315
  rb_define_const(mLGPIO, "SET_PULL_NONE", INT2NUM(LG_SET_PULL_NONE));
316
+ rb_define_const(mLGPIO, "RISING_EDGE", INT2NUM(LG_RISING_EDGE));
317
+ rb_define_const(mLGPIO, "FALLING_EDGE", INT2NUM(LG_FALLING_EDGE));
318
+ rb_define_const(mLGPIO, "BOTH_EDGES", INT2NUM(LG_BOTH_EDGES));
133
319
  rb_define_singleton_method(mLGPIO, "chip_open", chip_open, 1);
134
320
  rb_define_singleton_method(mLGPIO, "chip_close", chip_close, 1);
135
321
  rb_define_singleton_method(mLGPIO, "gpio_free", gpio_free, 2);
@@ -145,6 +331,12 @@ void Init_lgpio(void) {
145
331
  rb_define_singleton_method(mLGPIO, "group_read", group_read, 2);
146
332
  rb_define_singleton_method(mLGPIO, "group_write", group_write, 4);
147
333
 
334
+ // Alerts / Reports
335
+ rb_define_singleton_method(mLGPIO, "gpio_set_debounce", gpio_set_debounce, 3);
336
+ rb_define_singleton_method(mLGPIO, "gpio_claim_alert", gpio_claim_alert, 4);
337
+ rb_define_singleton_method(mLGPIO, "gpio_start_reporting", gpio_start_reporting, 0);
338
+ rb_define_singleton_method(mLGPIO, "gpio_get_report", gpio_get_report, 0);
339
+
148
340
  // PWM / Servo / Wave
149
341
  rb_define_const(mLGPIO, "TX_PWM", INT2NUM(LG_TX_PWM));
150
342
  rb_define_const(mLGPIO, "TX_WAVE",INT2NUM(LG_TX_WAVE));
@@ -154,4 +346,18 @@ void Init_lgpio(void) {
154
346
  rb_define_singleton_method(mLGPIO, "tx_pwm", tx_pwm, 6);
155
347
  rb_define_singleton_method(mLGPIO, "tx_servo", tx_servo, 6);
156
348
  rb_define_singleton_method(mLGPIO, "tx_wave", tx_wave, 3);
349
+
350
+ // I2C
351
+ rb_define_singleton_method(mLGPIO, "i2c_open", i2c_open, 3);
352
+ rb_define_singleton_method(mLGPIO, "i2c_close", i2c_close, 1);
353
+ rb_define_singleton_method(mLGPIO, "i2c_write_device", i2c_write_device, 2);
354
+ rb_define_singleton_method(mLGPIO, "i2c_read_device", i2c_read_device, 2);
355
+ rb_define_singleton_method(mLGPIO, "i2c_zip", i2c_zip, 3);
356
+
357
+ // SPI
358
+ rb_define_singleton_method(mLGPIO, "spi_open", spi_open, 4);
359
+ rb_define_singleton_method(mLGPIO, "spi_close", spi_close, 1);
360
+ rb_define_singleton_method(mLGPIO, "spi_read", spi_read, 2);
361
+ rb_define_singleton_method(mLGPIO, "spi_write", spi_write, 2);
362
+ rb_define_singleton_method(mLGPIO, "spi_xfer", spi_xfer, 2);
157
363
  }
data/lib/lgpio/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LGPIO
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.3"
3
3
  end
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.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - vickash
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-03 00:00:00.000000000 Z
11
+ date: 2024-08-03 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,8 +26,17 @@ 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
34
+ - examples/reports.rb
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
31
40
  - examples/wave.rb
32
41
  - ext/lgpio/extconf.rb
33
42
  - ext/lgpio/lgpio.c
@@ -54,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
63
  - !ruby/object:Gem::Version
55
64
  version: '0'
56
65
  requirements: []
57
- rubygems_version: 3.5.9
66
+ rubygems_version: 3.5.16
58
67
  signing_key:
59
68
  specification_version: 4
60
69
  summary: lgpio (lg) bindings for Ruby