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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bb351b8041dc3f5e78ed92128c3c971cf3f9e35d247a71e8d4ce56acec87f32
4
- data.tar.gz: a4df4836decc51de97ad16093bbd19e649cd02b675d6511c601feddde0cded66
3
+ metadata.gz: 84493ab6ca425d960887480b8f186a610fd9e41f0885657c79b5a4038a8a2035
4
+ data.tar.gz: 705d88399e518bdbc167eca65cd795dea2d33936b919aaaf22a34c8be032a1a1
5
5
  SHA512:
6
- metadata.gz: f94daeeaba65d22c4ee2e0205a9399f18f886cc5f42e15f53a4991381756b7a33d97d29113918b809b6ff96d7900a327ee26d441d9249e86020cee7c21c34765
7
- data.tar.gz: 367559fcd9750f88e728b80836c3dfa7d9273dcb2c734bc67faed29b23e177fc03e9e18df9678533907f3ddbe85be5d4d443a5d45404a03322ac09e586827e11
6
+ metadata.gz: 77f3c42ed9a94f8ea0c59acc2cb276efd400990099ac4272f06619645953f6014dbc4a9330d04ed154eee86fabba4791290fcd7201b326f2d6f4b2437a5d5812
7
+ data.tar.gz: 7fd2aa6715e36f3e95d35248b1b13ffb141dca3c50b1ab7a5fbe2e420b67a3873d3de50a24b5eda460a50bd69133492043356715522b27cbe509e280c3174699
data/.gitignore CHANGED
@@ -23,3 +23,4 @@ tmp
23
23
  .ruby-version
24
24
  .rvmrc
25
25
  .zed
26
+ ._*
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) C library, which provides GPIO, PWM, I2C, SPI and Serial features on single-board-computers (SBCs) running Linux.
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 (read from a queue)
12
- - [x] PWM (software-timed)
13
- - [x] Wave (software-timed)
14
- - [ ] I2C
15
- - [ ] SPI
16
- - [ ] Serial
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
@@ -6,6 +6,7 @@ PIN = 76
6
6
  chip_handle = LGPIO.chip_open(GPIO_CHIP)
7
7
 
8
8
  LGPIO.gpio_claim_input(chip_handle, LGPIO::SET_PULL_NONE, PIN)
9
+ LGPIO.gpio_set_debounce(chip_handle, PIN, 1)
9
10
  LGPIO.gpio_claim_alert(chip_handle, 0, LGPIO::BOTH_EDGES, PIN)
10
11
  LGPIO.gpio_start_reporting
11
12
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module LGPIO
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
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.2
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-07-31 00:00:00.000000000 Z
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