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