lgpio 0.1.1 → 0.1.3

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 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