rpi_gpio 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ /*
2
+ Original code by Ben Croston modified for Ruby by Nick Lowery
3
+ (github.com/clockvapor)
4
+ Copyright (c) 2014-2020 Nick Lowery
5
+
6
+ Copyright (c) 2013-2015 Ben Croston
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
9
+ this software and associated documentation files (the "Software"), to deal in
10
+ the Software without restriction, including without limitation the rights to
11
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
+ of the Software, and to permit persons to whom the Software is furnished to do
13
+ so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ */
26
+
27
+ #define NO_EDGE 0
28
+ #define RISING_EDGE 1
29
+ #define FALLING_EDGE 2
30
+ #define BOTH_EDGE 3
31
+
32
+ int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime);
33
+ void remove_edge_detect(unsigned int gpio);
34
+ int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio));
35
+ int event_detected(unsigned int gpio);
36
+ int gpio_event_added(unsigned int gpio);
37
+ int event_initialise(void);
38
+ void event_cleanup(int gpio);
39
+ void event_cleanup_all(void);
40
+ int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout);
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'rpi_gpio'
4
+ dir_config(extension_name)
5
+ create_makefile(extension_name)
@@ -0,0 +1,453 @@
1
+ /*
2
+ Original code by Ben Croston modified for Ruby by Nick Lowery
3
+ (github.com/clockvapor)
4
+ Copyright (c) 2014-2020 Nick Lowery
5
+
6
+ Copyright (c) 2012-2016 Ben Croston
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
9
+ this software and associated documentation files (the "Software"), to deal in
10
+ the Software without restriction, including without limitation the rights to
11
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
+ of the Software, and to permit persons to whom the Software is furnished to do
13
+ so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ */
26
+
27
+ #include "rb_gpio.h"
28
+
29
+ extern VALUE m_GPIO;
30
+ int gpio_warnings = 1;
31
+
32
+ VALUE _extract_channels(VALUE channel_or_list)
33
+ {
34
+ VALUE channel_list;
35
+
36
+ // parse arguments
37
+ if (RB_TYPE_P(channel_or_list, T_ARRAY) == 1) {
38
+ channel_list = channel_or_list;
39
+ } else {
40
+ channel_list = rb_ary_new();
41
+ rb_ary_push(channel_list, channel_or_list);
42
+ }
43
+
44
+ return channel_list;
45
+ }
46
+
47
+ void define_gpio_module_stuff(void)
48
+ {
49
+ int i;
50
+
51
+ rb_define_module_function(m_GPIO, "setup", GPIO_setup, 2);
52
+ rb_define_module_function(m_GPIO, "clean_up", GPIO_clean_up, -1);
53
+ rb_define_module_function(m_GPIO, "reset", GPIO_reset, 0);
54
+ rb_define_module_function(m_GPIO, "set_numbering", GPIO_set_numbering, 1);
55
+ rb_define_module_function(m_GPIO, "set_high", GPIO_set_high, 1);
56
+ rb_define_module_function(m_GPIO, "set_low", GPIO_set_low, 1);
57
+ rb_define_module_function(m_GPIO, "high?", GPIO_test_high, 1);
58
+ rb_define_module_function(m_GPIO, "low?", GPIO_test_low, 1);
59
+ rb_define_module_function(m_GPIO, "set_warnings", GPIO_set_warnings, 1);
60
+
61
+ for (i = 0; i < 54; i++) {
62
+ gpio_direction[i] = -1;
63
+ }
64
+
65
+ // detect board revision and set up accordingly
66
+ if (get_rpi_info(&rpiinfo)) {
67
+ rb_raise(rb_eRuntimeError, "this gem can only be run on a Raspberry Pi");
68
+ setup_error = 1;
69
+ return;
70
+ } else if (rpiinfo.p1_revision == 1) {
71
+ pin_to_gpio = &pin_to_gpio_rev1;
72
+ } else if (rpiinfo.p1_revision == 2) {
73
+ pin_to_gpio = &pin_to_gpio_rev2;
74
+ } else { // assume model B+ or A+
75
+ pin_to_gpio = &pin_to_gpio_rev3;
76
+ }
77
+ }
78
+
79
+ int mmap_gpio_mem(void)
80
+ {
81
+ int result;
82
+
83
+ if (module_setup) {
84
+ return 0;
85
+ }
86
+
87
+ result = setup();
88
+ if (result == SETUP_DEVMEM_FAIL) {
89
+ rb_raise(rb_eRuntimeError, "no access to /dev/mem; try running as root");
90
+ return 1;
91
+ } else if (result == SETUP_MALLOC_FAIL) {
92
+ rb_raise(rb_eNoMemError, "out of memory");
93
+ return 2;
94
+ } else if (result == SETUP_MMAP_FAIL) {
95
+ rb_raise(rb_eRuntimeError, "mmap of GPIO registers failed");
96
+ return 3;
97
+ } else if (result == SETUP_CPUINFO_FAIL) {
98
+ rb_raise(rb_eRuntimeError, "unable to open /proc/cpuinfo");
99
+ return 4;
100
+ } else if (result == SETUP_NOT_RPI_FAIL) {
101
+ rb_raise(rb_eRuntimeError, "not running on a RPi");
102
+ return 5;
103
+ } else { // result == SETUP_OK
104
+ module_setup = 1;
105
+ return 0;
106
+ }
107
+ }
108
+
109
+ int is_gpio_initialized(unsigned int gpio)
110
+ {
111
+ if (gpio_direction[gpio] != INPUT && gpio_direction[gpio] != OUTPUT) {
112
+ rb_raise(rb_eRuntimeError,
113
+ "you must setup the GPIO channel first with "
114
+ "RPi::GPIO.setup CHANNEL, :as => :input or "
115
+ "RPi::GPIO.setup CHANNEL, :as => :output");
116
+ return 0;
117
+ }
118
+
119
+ return 1;
120
+ }
121
+
122
+ int is_gpio_output(unsigned int gpio)
123
+ {
124
+ if (gpio_direction[gpio] != OUTPUT) {
125
+ if (gpio_direction[gpio] != INPUT) {
126
+ rb_raise(rb_eRuntimeError,
127
+ "you must setup the GPIO channel first with "
128
+ "RPi::GPIO.setup CHANNEL, :as => :input or "
129
+ "RPi::GPIO.setup CHANNEL, :as => :output");
130
+ return 0;
131
+ }
132
+
133
+ rb_raise(rb_eRuntimeError, "GPIO channel not setup as output");
134
+ return 0;
135
+ }
136
+
137
+ return 1;
138
+ }
139
+
140
+ int is_rpi(void)
141
+ {
142
+ if (setup_error) {
143
+ rb_raise(rb_eRuntimeError, "this gem can only be run on a Raspberry Pi");
144
+ return 0;
145
+ }
146
+
147
+ return 1;
148
+ }
149
+
150
+ // RPi::GPIO.clean_up(channel=nil)
151
+ // clean up everything by default; otherwise, clean up given channel
152
+ VALUE GPIO_clean_up(int argc, VALUE *argv, VALUE self)
153
+ {
154
+ int i;
155
+ int found = 0;
156
+ int channel = -666; // lol, quite a flag
157
+ unsigned int gpio;
158
+
159
+ if (argc == 1) {
160
+ channel = NUM2INT(argv[0]);
161
+ } else if (argc > 1) {
162
+ rb_raise(rb_eArgError, "wrong number of arguments; 0 for all pins, 1 for a specific pin");
163
+ return Qnil;
164
+ }
165
+
166
+ if (channel != -666 && get_gpio_number(channel, &gpio)) {
167
+ return Qnil;
168
+ }
169
+
170
+ if (module_setup && !setup_error) {
171
+ if (channel == -666) {
172
+ // clean up any /sys/class exports
173
+ event_cleanup_all();
174
+
175
+ // set everything back to input
176
+ for (i = 0; i < 54; i++) {
177
+ if (gpio_direction[i] != -1) {
178
+ setup_gpio(i, INPUT, PUD_OFF);
179
+ gpio_direction[i] = -1;
180
+ found = 1;
181
+ }
182
+ }
183
+ } else {
184
+ // clean up any /sys/class exports
185
+ event_cleanup(gpio);
186
+
187
+ // set everything back to input
188
+ if (gpio_direction[gpio] != -1) {
189
+ setup_gpio(gpio, INPUT, PUD_OFF);
190
+ gpio_direction[gpio] = -1;
191
+ found = 1;
192
+ }
193
+ }
194
+ }
195
+
196
+ // check if any channels set up - if not warn about misuse of GPIO.clean_up()
197
+ if (!found && gpio_warnings) {
198
+ rb_warn("no channels have been set up yet; nothing to clean up");
199
+ }
200
+
201
+ return Qnil;
202
+ }
203
+
204
+ // RPi::GPIO.reset
205
+ //
206
+ // cleans up all pins, unsets numbering mode, enables warnings.
207
+ VALUE GPIO_reset(VALUE self)
208
+ {
209
+ GPIO_clean_up(0, NULL, self);
210
+ gpio_mode = MODE_UNKNOWN;
211
+ GPIO_set_warnings(self, Qtrue);
212
+ return Qnil;
213
+ }
214
+
215
+ // RPi::GPIO.setup(channel, hash(:as => {:input, :output}, :pull => {:off,
216
+ // :down, :up}(default :off), :initialize => {:high, :low}))
217
+ //
218
+ // sets up a channel as either input or output with an optional pull-down or
219
+ // pull-up resistor and an optional initialize state
220
+ VALUE GPIO_setup(VALUE self, VALUE channel, VALUE hash)
221
+ {
222
+ unsigned int gpio;
223
+ int chan = -1;
224
+ VALUE channel_list = _extract_channels(channel);
225
+ int chan_count = RARRAY_LEN(channel_list);
226
+ const char *direction_str = NULL;
227
+ int direction;
228
+ VALUE pud_val = Qnil;
229
+ const char *pud_str = NULL;
230
+ int pud = PUD_OFF;
231
+ int func;
232
+
233
+ VALUE initialize_val = Qnil;
234
+ const char *initialize_str = NULL;
235
+ int initialize = HIGH;
236
+
237
+ // func to set up channel stored in channel variable
238
+ int setup_one(void) {
239
+ if (get_gpio_number(chan, &gpio)) {
240
+ return 0;
241
+ }
242
+
243
+ // warn if the channel is already in use (not from this program).
244
+ func = gpio_function(gpio);
245
+ if (gpio_warnings &&
246
+ ((func != 0 && func != 1) ||
247
+ (gpio_direction[gpio] == -1 && func == 1)))
248
+ {
249
+ rb_warn("this channel is already in use... continuing anyway. use RPi::GPIO.set_warnings(false) to "
250
+ "disable warnings");
251
+ }
252
+
253
+ if (gpio_warnings) {
254
+ if (rpiinfo.p1_revision == 0) { // compute module - do nothing
255
+ } else if ((rpiinfo.p1_revision == 1 &&
256
+ (gpio == 0 || gpio == 1)) ||
257
+ (gpio == 2 || gpio == 3)) {
258
+ if (pud == PUD_UP || pud == PUD_DOWN) {
259
+ rb_warn("a physical pull up resistor is fitted on this channel");
260
+ }
261
+ }
262
+ }
263
+
264
+ if (direction == OUTPUT && (initialize == LOW || initialize == HIGH)) {
265
+ output_gpio(gpio, initialize);
266
+ }
267
+ setup_gpio(gpio, direction, pud);
268
+ gpio_direction[gpio] = direction;
269
+ return 1;
270
+ }
271
+
272
+ // pin direction
273
+ direction_str = rb_id2name(rb_to_id(rb_hash_aref(hash, ID2SYM(rb_intern("as")))));
274
+ if (strcmp("input", direction_str) == 0) {
275
+ direction = INPUT;
276
+ } else if (strcmp("output", direction_str) == 0) {
277
+ direction = OUTPUT;
278
+ } else {
279
+ rb_raise(rb_eArgError, "invalid pin direction; must be :input or :output");
280
+ }
281
+
282
+ // pull up, down, or off
283
+ pud_val = rb_hash_aref(hash, ID2SYM(rb_intern("pull")));
284
+ if (pud_val != Qnil) {
285
+ if (direction == OUTPUT) {
286
+ rb_raise(rb_eArgError, "output pin cannot use pull argument");
287
+ return Qnil;
288
+ }
289
+
290
+ pud_str = rb_id2name(rb_to_id(pud_val));
291
+ if (strcmp("down", pud_str) == 0) {
292
+ pud = PUD_DOWN;
293
+ } else if (strcmp("up", pud_str) == 0) {
294
+ pud = PUD_UP;
295
+ } else if (strcmp("off", pud_str) == 0) {
296
+ pud = PUD_OFF;
297
+ } else {
298
+ rb_raise(rb_eArgError, "invalid pin pull direction; must be :up, :down, or :off");
299
+ return Qnil;
300
+ }
301
+ } else {
302
+ pud = PUD_OFF;
303
+ }
304
+ // initialize high or low
305
+ initialize_val = rb_hash_aref(hash, ID2SYM(rb_intern("initialize")));
306
+ if (initialize_val != Qnil) {
307
+ if (direction == INPUT) {
308
+ rb_raise(rb_eArgError, "input pins cannot use initial argument");
309
+ return Qnil;
310
+ }
311
+
312
+ initialize_str = rb_id2name(rb_to_id(initialize_val));
313
+ if (strcmp("high", initialize_str) == 0) {
314
+ initialize = HIGH;
315
+ } else if (strcmp("low", initialize_str) == 0) {
316
+ initialize = LOW;
317
+ } else {
318
+ rb_raise(rb_eArgError, "invalid pin initialize state; must be :high or :low");
319
+ return Qnil;
320
+ }
321
+ } else {
322
+ initialize = HIGH;
323
+ }
324
+
325
+ if (!is_rpi() || mmap_gpio_mem()) {
326
+ return Qnil;
327
+ }
328
+
329
+ if (direction != INPUT && direction != OUTPUT) {
330
+ rb_raise(rb_eArgError, "invalid direction");
331
+ return Qnil;
332
+ }
333
+
334
+ if (direction == OUTPUT) {
335
+ pud = PUD_OFF;
336
+ }
337
+
338
+ for (int i = 0; i < chan_count; i++) {
339
+ chan = NUM2INT(rb_ary_entry(channel_list, i));
340
+ if (!setup_one()) {
341
+ return Qnil;
342
+ }
343
+ }
344
+
345
+ return self;
346
+ }
347
+
348
+ // RPi::GPIO.set_numbering(mode)
349
+ VALUE GPIO_set_numbering(VALUE self, VALUE mode)
350
+ {
351
+ int new_mode;
352
+ const char *mode_str = NULL;
353
+
354
+ if (TYPE(mode) == T_SYMBOL) {
355
+ mode_str = rb_id2name(rb_to_id(mode));
356
+ } else {
357
+ mode_str = RSTRING_PTR(mode);
358
+ }
359
+
360
+ if (strcmp(mode_str, "board") == 0) {
361
+ new_mode = BOARD;
362
+ } else if (strcmp(mode_str, "bcm") == 0) {
363
+ new_mode = BCM;
364
+ } else {
365
+ rb_raise(rb_eArgError, "invalid numbering mode; must be :board or :bcm");
366
+ }
367
+
368
+ if (!is_rpi()) {
369
+ return Qnil;
370
+ }
371
+
372
+ if (new_mode != BOARD && new_mode != BCM) {
373
+ rb_raise(rb_eArgError, "invalid mode");
374
+ return Qnil;
375
+ }
376
+
377
+ if (rpiinfo.p1_revision == 0 && new_mode == BOARD) {
378
+ rb_raise(rb_eRuntimeError, ":board numbering system not applicable on compute module");
379
+ return Qnil;
380
+ }
381
+
382
+ gpio_mode = new_mode;
383
+ return self;
384
+ }
385
+
386
+ // RPi::GPIO.set_high(channel)
387
+ VALUE GPIO_set_high(VALUE self, VALUE channel)
388
+ {
389
+ unsigned int gpio;
390
+ int chan = -1;
391
+ VALUE channel_list = _extract_channels(channel);
392
+ int chan_count = RARRAY_LEN(channel_list);
393
+
394
+ for (int i = 0; i < chan_count; i++) {
395
+ chan = NUM2INT(rb_ary_entry(channel_list, i));
396
+ if (get_gpio_number(chan, &gpio) || !is_gpio_output(gpio) || check_gpio_priv()) {
397
+ return Qnil;
398
+ } else {
399
+ output_gpio(gpio, 1);
400
+ }
401
+ }
402
+
403
+ return self;
404
+ }
405
+
406
+ // RPi::GPIO.set_low(channel)
407
+ VALUE GPIO_set_low(VALUE self, VALUE channel)
408
+ {
409
+ unsigned int gpio;
410
+ int chan = -1;
411
+ VALUE channel_list = _extract_channels(channel);
412
+ int chan_count = RARRAY_LEN(channel_list);
413
+
414
+ for (int i = 0; i < chan_count; i++) {
415
+ chan = NUM2INT(rb_ary_entry(channel_list, i));
416
+ if (get_gpio_number(chan, &gpio) || !is_gpio_output(gpio) || check_gpio_priv()) {
417
+ return Qnil;
418
+ } else {
419
+ output_gpio(gpio, 0);
420
+ }
421
+ }
422
+
423
+ return self;
424
+ }
425
+
426
+ // RPi::GPIO.high?(channel)
427
+ VALUE GPIO_test_high(VALUE self, VALUE channel)
428
+ {
429
+ unsigned int gpio;
430
+
431
+ if (get_gpio_number(NUM2INT(channel), &gpio) || !is_gpio_initialized(gpio) || check_gpio_priv()) {
432
+ return Qnil;
433
+ }
434
+
435
+ return input_gpio(gpio) ? Qtrue : Qfalse;
436
+ }
437
+
438
+ // RPi::GPIO.low?(channel)
439
+ VALUE GPIO_test_low(VALUE self, VALUE channel)
440
+ {
441
+ return GPIO_test_high(self, channel) ? Qfalse : Qtrue;
442
+ }
443
+
444
+ // RPi::GPIO.set_warnings(state)
445
+ VALUE GPIO_set_warnings(VALUE self, VALUE setting)
446
+ {
447
+ if (!is_rpi()) {
448
+ return Qnil;
449
+ }
450
+
451
+ gpio_warnings = RTEST(setting);
452
+ return self;
453
+ }