1
0
mirror of https://github.com/xtacocorex/CHIP_IO synced 2025-07-19 20:33:21 +00:00
Files
CHIP_IO/source/py_gpio.c

818 lines
26 KiB
C

/*
Copyright (c) 2016 Robert Wolterman
Original BBIO Author Justin Cooper
Modified for CHIP_IO Author Robert Wolterman
This file incorporates work covered by the following copyright and
permission notice, all modified code adopts the original license:
Copyright (c) 2013 Adafruit
Original RPi.GPIO Author Ben Croston
Modified for BBIO Author Justin Cooper
This file incorporates work covered by the following copyright and
permission notice, all modified code adopts the original license:
Copyright (c) 2013 Ben Croston
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdlib.h"
#include "Python.h"
#include "constants.h"
#include "common.h"
#include "event_gpio.h"
static int gpio_warnings = 1;
int max_gpio = -1;
dyn_int_array_t *gpio_direction = NULL;
struct py_callback
{
char channel[32];
int gpio;
PyObject *py_cb;
unsigned long long lastcall;
unsigned int bouncetime;
struct py_callback *next;
};
static struct py_callback *py_callbacks = NULL;
static int init_module(void)
{
module_setup = 1;
return 0;
}
static void remember_gpio_direction(int gpio, int direction)
{
dyn_int_array_set(&gpio_direction, gpio, direction, -1);
}
// python function cleanup()
static PyObject *py_cleanup(PyObject *self, PyObject *args)
{
clear_error_msg();
// clean up any exports
event_cleanup();
Py_RETURN_NONE;
}
// python function setup(channel, direction, pull_up_down=PUD_OFF, initial=None)
static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs)
{
int gpio;
char *channel;
int direction;
int pud = PUD_OFF;
int initial = 0;
static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL};
clear_error_msg();
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|ii", kwlist, &channel, &direction, &pud, &initial))
return NULL;
if (!module_setup) {
init_module();
}
if (direction != INPUT && direction != OUTPUT)
{
PyErr_SetString(PyExc_ValueError, "An invalid direction was passed to setup()");
return NULL;
}
if (direction == OUTPUT)
pud = PUD_OFF;
if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP)
{
PyErr_SetString(PyExc_ValueError, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN");
return NULL;
}
if (get_gpio_number(channel, &gpio) < 0) {
char err[2000];
snprintf(err, sizeof(err), "Invalid channel %s. (%s)", channel, get_error_msg());
PyErr_SetString(PyExc_ValueError, err);
return NULL;
}
if (gpio_export(gpio) < 0) {
char err[2000];
snprintf(err, sizeof(err), "Error setting up channel %s, maybe already exported? (%s)", channel, get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
if (gpio_set_direction(gpio, direction) < 0) {
char err[2000];
snprintf(err, sizeof(err), "Error setting direction %d on channel %s. (%s)", direction, channel, get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
if (direction == OUTPUT) {
if (gpio_set_value(gpio, initial) < 0) {
char err[2000];
snprintf(err, sizeof(err), "Error setting initial value %d on channel %s. (%s)", initial, channel, get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
}
remember_gpio_direction(gpio, direction);
Py_RETURN_NONE;
} /* py_setup_channel */
// python function output(channel, value)
static PyObject *py_output_gpio(PyObject *self, PyObject *args)
{
int gpio;
int value;
char *channel;
clear_error_msg();
if (!PyArg_ParseTuple(args, "si", &channel, &value))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != OUTPUT)
{
char err[2000];
snprintf(err, sizeof(err), "Channel %s not set up or is not an output", channel);
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
int result = gpio_set_value(gpio, value);
if (result < 0) {
char err[2000];
snprintf(err, sizeof(err), "Could no write %d on channel %s. (%s)", value, channel, get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
Py_RETURN_NONE;
} /* py_output_gpio */
// python function value = input(channel)
static PyObject *py_input_gpio(PyObject *self, PyObject *args)
{
int gpio;
char *channel;
unsigned int value;
PyObject *py_value;
clear_error_msg();
if (!PyArg_ParseTuple(args, "s", &channel))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
// check channel is set up as an input or output
if (!module_setup || (dyn_int_array_get(&gpio_direction, gpio, -1) == -1))
{
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel first");
return NULL;
}
if (gpio_get_value(gpio, &value) < 0) {
char err[1024];
snprintf(err, sizeof(err), "Could not get value ('%s')", get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
py_value = Py_BuildValue("i", value);
return py_value;
}
static void run_py_callbacks(int gpio)
{
PyObject *result;
PyGILState_STATE gstate;
struct py_callback *cb = py_callbacks;
struct timeval tv_timenow;
unsigned long long timenow;
clear_error_msg();
while (cb != NULL)
{
if (cb->gpio == gpio)
{
gettimeofday(&tv_timenow, NULL);
timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
if (cb->bouncetime == 0 || timenow - cb->lastcall > cb->bouncetime*1000 || cb->lastcall == 0 || cb->lastcall > timenow) {
// save lastcall before calling func to prevent reentrant bounce
cb->lastcall = timenow;
// run callback
gstate = PyGILState_Ensure();
result = PyObject_CallFunction(cb->py_cb, "s", cb->channel);
if (result == NULL && PyErr_Occurred())
{
PyErr_Print();
PyErr_Clear();
}
Py_XDECREF(result);
PyGILState_Release(gstate);
}
cb->lastcall = timenow;
}
cb = cb->next;
}
}
static int add_py_callback(char *channel, int gpio, unsigned int bouncetime, PyObject *cb_func)
{
struct py_callback *new_py_cb;
struct py_callback *cb = py_callbacks;
clear_error_msg();
// add callback to py_callbacks list
new_py_cb = malloc(sizeof(struct py_callback));
if (new_py_cb == 0)
{
PyErr_NoMemory();
return -1;
}
new_py_cb->py_cb = cb_func;
Py_XINCREF(cb_func); // Add a reference to new callback
memset(new_py_cb->channel, 0, sizeof(new_py_cb->channel));
strncpy(new_py_cb->channel, channel, sizeof(new_py_cb->channel) - 1);
new_py_cb->gpio = gpio;
new_py_cb->lastcall = 0;
new_py_cb->bouncetime = bouncetime;
new_py_cb->next = NULL;
if (py_callbacks == NULL) {
py_callbacks = new_py_cb;
} else {
// add to end of list
while (cb->next != NULL)
cb = cb->next;
cb->next = new_py_cb;
}
add_edge_callback(gpio, run_py_callbacks);
return 0;
}
// python function add_event_callback(gpio, callback, bouncetime=0)
static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject *kwargs)
{
int gpio;
char *channel;
unsigned int bouncetime = 0;
PyObject *cb_func;
char *kwlist[] = {"gpio", "callback", "bouncetime", NULL};
clear_error_msg();
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|i", kwlist, &channel, &cb_func, &bouncetime))
return NULL;
if (!PyCallable_Check(cb_func))
{
PyErr_SetString(PyExc_TypeError, "Parameter must be callable");
return NULL;
}
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
// check to ensure gpio is one of the allowed pins
if (gpio != lookup_gpio_by_name("AP-EINT3")
&& gpio != lookup_gpio_by_name("AP-EINT1")
&& !(gpio >= lookup_gpio_by_name("XIO-P0") && gpio <= lookup_gpio_by_name("XIO-P7"))) {
PyErr_SetString(PyExc_ValueError, "Callbacks currently available on AP-EINT1, AP-EINT3, and XIO-P0 to XIO-P7 only");
return NULL;
}
// check channel is set up as an input
if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT)
{
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
return NULL;
}
if (!gpio_is_evented(gpio))
{
PyErr_SetString(PyExc_RuntimeError, "Add event detection using add_event_detect first before adding a callback");
return NULL;
}
if (add_py_callback(channel, gpio, bouncetime, cb_func) != 0)
return NULL;
Py_RETURN_NONE;
}
// python function add_event_detect(gpio, edge, callback=None, bouncetime=0
static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *kwargs)
{
int gpio;
char *channel;
int edge, result;
unsigned int bouncetime = 0;
PyObject *cb_func = NULL;
char *kwlist[] = {"gpio", "edge", "callback", "bouncetime", NULL};
clear_error_msg();
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|Oi", kwlist, &channel, &edge, &cb_func, &bouncetime))
return NULL;
if (cb_func != NULL && !PyCallable_Check(cb_func))
{
PyErr_SetString(PyExc_TypeError, "Parameter must be callable");
return NULL;
}
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
// check to ensure gpio is one of the allowed pins
if (gpio != lookup_gpio_by_name("AP-EINT3")
&& gpio != lookup_gpio_by_name("AP-EINT1")
&& !(gpio >= lookup_gpio_by_name("XIO-P0") && gpio <= lookup_gpio_by_name("XIO-P7"))) {
PyErr_SetString(PyExc_ValueError, "Edge Detection currently available on AP-EINT1, AP-EINT3, and XIO-P0 to XIO-P7 only");
return NULL;
}
// is edge valid value
if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE)
{
PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH");
return NULL;
}
// check channel is set up as an input
if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT)
{
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
return NULL;
}
if ((result = add_edge_detect(gpio, edge)) != 0) // starts a thread
{
if (result == 1)
{
PyErr_SetString(PyExc_RuntimeError, "Edge detection already enabled for this GPIO channel");
return NULL;
} else {
PyErr_SetString(PyExc_RuntimeError, "Failed to add edge detection");
return NULL;
}
}
if (cb_func != NULL)
if (add_py_callback(channel, gpio, bouncetime, cb_func) != 0)
return NULL;
Py_RETURN_NONE;
}
// python function remove_event_detect(gpio)
static PyObject *py_remove_event_detect(PyObject *self, PyObject *args)
{
int gpio;
char *channel;
struct py_callback *cb = py_callbacks;
struct py_callback *temp;
struct py_callback *prev = NULL;
clear_error_msg();
if (!PyArg_ParseTuple(args, "s", &channel))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
// check to ensure gpio is one of the allowed pins
if (gpio != lookup_gpio_by_name("AP-EINT3")
&& gpio != lookup_gpio_by_name("AP-EINT1")
&& !(gpio >= lookup_gpio_by_name("XIO-P0") && gpio <= lookup_gpio_by_name("XIO-P7"))) {
PyErr_SetString(PyExc_ValueError, "Edge Detection currently available on AP-EINT1, AP-EINT3, and XIO-P0 to XIO-P7 only");
return NULL;
}
// remove all python callbacks for gpio
while (cb != NULL)
{
if (cb->gpio == gpio)
{
Py_XDECREF(cb->py_cb);
if (prev == NULL)
py_callbacks = cb->next;
else
prev->next = cb->next;
temp = cb;
cb = cb->next;
free(temp);
} else {
prev = cb;
cb = cb->next;
}
}
remove_edge_detect(gpio);
Py_RETURN_NONE;
}
// python function value = event_detected(channel)
static PyObject *py_event_detected(PyObject *self, PyObject *args)
{
int gpio;
char *channel;
clear_error_msg();
if (!PyArg_ParseTuple(args, "s", &channel))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
if (event_detected(gpio))
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
// python function py_wait_for_edge(gpio, edge)
static PyObject *py_wait_for_edge(PyObject *self, PyObject *args)
{
int gpio;
int edge, result;
char *channel;
char error[81];
clear_error_msg();
if (!PyArg_ParseTuple(args, "si", &channel, &edge))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
// check to ensure gpio is one of the allowed pins
if (gpio != lookup_gpio_by_name("AP-EINT3")
&& gpio != lookup_gpio_by_name("AP-EINT1")
&& !(gpio >= lookup_gpio_by_name("XIO-P0") && gpio <= lookup_gpio_by_name("XIO-P7"))) {
PyErr_SetString(PyExc_ValueError, "Edge Detection currently available on AP-EINT1, AP-EINT3, and XIO-P0 to XIO-P7 only");
return NULL;
}
// is edge a valid value?
if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE)
{
PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH");
return NULL;
}
// check channel is setup as an input
if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT)
{
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
return NULL;
}
Py_BEGIN_ALLOW_THREADS // disable GIL
result = blocking_wait_for_edge(gpio, edge);
Py_END_ALLOW_THREADS // enable GIL
if (result == 0) {
Py_INCREF(Py_None);
return Py_None;
} else if (result == 2) {
PyErr_SetString(PyExc_RuntimeError, "Edge detection events already enabled for this GPIO channel");
return NULL;
} else {
snprintf(error, sizeof(error), "Error #%d waiting for edge", result); BUF2SMALL(error);
PyErr_SetString(PyExc_RuntimeError, error);
return NULL;
}
Py_RETURN_NONE;
}
// python function value = gpio_function(gpio)
static PyObject *py_gpio_function(PyObject *self, PyObject *args)
{
int gpio;
unsigned int value;
PyObject *func;
char *channel;
clear_error_msg();
if (!PyArg_ParseTuple(args, "s", &channel))
return NULL;
if (get_gpio_number(channel, &gpio)) {
PyErr_SetString(PyExc_ValueError, "Invalid channel");
return NULL;
}
if (setup_error)
{
PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!");
return NULL;
}
if (gpio_get_direction(gpio, &value) < 0) {
char err[1024];
snprintf(err, sizeof(err), "Could not get direction ('%s')", get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
func = Py_BuildValue("i", value);
return func;
}
// python function setwarnings(state)
static PyObject *py_setwarnings(PyObject *self, PyObject *args)
{
clear_error_msg();
if (!PyArg_ParseTuple(args, "i", &gpio_warnings))
return NULL;
if (setup_error)
{
PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!");
return NULL;
}
Py_RETURN_NONE;
}
// python function base = get_xio_base()
static PyObject *py_gpio_base(PyObject *self, PyObject *args)
{
unsigned int value;
PyObject *py_value;
clear_error_msg();
value = get_xio_base();
if (value <= 0) {
char err[1024];
snprintf(err, sizeof(err), "Could not get XIO base ('%s')", get_error_msg());
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
py_value = Py_BuildValue("i", value);
return py_value;
}
// Internal unit tests
extern pins_t pins_info[];
static PyObject *py_selftest(PyObject *self, PyObject *args)
{
int input;
clear_error_msg();
if (!PyArg_ParseTuple(args, "i", &input))
return NULL;
printf("Testing get_xio_base\n");
int first_base = get_xio_base(); ASSRT(first_base > 0);
int second_base = get_xio_base(); ASSRT(second_base == first_base);
printf("base=%d\n", first_base);
printf("Testing lookup_gpio_by_key\n");
ASSRT(-1 == lookup_gpio_by_key("U13_1"));
ASSRT(48 == lookup_gpio_by_key("U13_9"));
ASSRT(47 == lookup_gpio_by_key("U13_11"));
ASSRT(first_base == lookup_gpio_by_key("U14_13"));
ASSRT((first_base+1) == lookup_gpio_by_key("U14_14"));
ASSRT((first_base+6) == lookup_gpio_by_key("U14_19"));
ASSRT((first_base+7) == lookup_gpio_by_key("U14_20"));
ASSRT(193 == lookup_gpio_by_key("U14_23"));
ASSRT(139 == lookup_gpio_by_key("U14_38"));
ASSRT(-1 == lookup_gpio_by_key("U14_40"));
ASSRT(-1 == lookup_gpio_by_key("NOTFOUND"));
ASSRT(-1 == lookup_gpio_by_key("U14_"));
ASSRT(-1 == lookup_gpio_by_key("U14_4000"));
printf("Testing lookup_gpio_by_name\n");
ASSRT(-1 == lookup_gpio_by_name("GND"));
ASSRT(48 == lookup_gpio_by_name("TWI1-SDA"));
ASSRT(47 == lookup_gpio_by_name("TWI1-SCK"));
ASSRT(first_base == lookup_gpio_by_name("XIO-P0"));
ASSRT((first_base+6) == lookup_gpio_by_name("XIO-P6"));
ASSRT((first_base+7) == lookup_gpio_by_name("XIO-P7"));
ASSRT(139 == lookup_gpio_by_name("CSID7"));
ASSRT(-1 == lookup_gpio_by_name("NOTFOUND"));
ASSRT(-1 == lookup_gpio_by_name("CSID"));
ASSRT(-1 == lookup_gpio_by_name("CSID777"));
printf("Testing lookup_ain_by_key\n");
ASSRT(-1 == lookup_ain_by_key("U14_1"));
ASSRT(0 == lookup_ain_by_key("U14_11"));
ASSRT(-1 == lookup_ain_by_key("NOTFOUND"));
ASSRT(-1 == lookup_ain_by_key("U14_"));
ASSRT(-1 == lookup_ain_by_key("U14_1111"));
printf("Testing lookup_ain_by_name\n");
ASSRT(-1 == lookup_ain_by_name("GND"));
ASSRT(0 == lookup_ain_by_name("LRADC"));
ASSRT(-1 == lookup_ain_by_name("NOTFOUND"));
ASSRT(-1 == lookup_ain_by_name("LR"));
ASSRT(-1 == lookup_ain_by_name("LRADCCC"));
char k[9];
printf("Testing copy_key_by_key\n");
ASSRT(1 == copy_key_by_key("U13_1", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_1", k));
ASSRT(1 == copy_key_by_key("U14_40", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U14_40", k));
ASSRT(0 == copy_key_by_key("NOTFOUND", k));
ASSRT(0 == copy_key_by_key("U14_", k));
ASSRT(0 == copy_key_by_key("U14_4000", k));
printf("Testing copy_pwm_key_by_key\n");
ASSRT(1 == copy_pwm_key_by_key("U13_18", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_18", k));
ASSRT(0 == copy_pwm_key_by_key("U13_1", k));
ASSRT(0 == copy_pwm_key_by_key("U14_40", k));
ASSRT(0 == copy_pwm_key_by_key("NOTFOUND", k));
ASSRT(0 == copy_pwm_key_by_key("U13_", k));
ASSRT(0 == copy_pwm_key_by_key("U13_1888", k));
printf("Testing get_key_by_name\n");
ASSRT(1 == get_key_by_name("GND", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_1", k));
ASSRT(1 == get_key_by_name("CSID7", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U14_38", k));
ASSRT(0 == get_key_by_name("NOTFOUND", k));
ASSRT(0 == get_key_by_name("CSID", k));
ASSRT(0 == get_key_by_name("CSID777", k));
printf("Testing get_pwm_key_by_name\n");
ASSRT(1 == get_pwm_key_by_name("PWM0", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_18", k));
ASSRT(0 == get_pwm_key_by_name("NOTFOUND", k));
ASSRT(0 == get_pwm_key_by_name("PWM", k));
ASSRT(0 == get_pwm_key_by_name("PWM000", k));
char fp[80];
printf("Testing build_path\n");
ASSRT(1 == build_path("/home", "ch", fp, sizeof(fp))); ASSRT(0 == strcmp("/home/chip", fp));
ASSRT(1 == build_path("/home", "chip", fp, sizeof(fp))); ASSRT(0 == strcmp("/home/chip", fp));
ASSRT(0 == build_path("/home", "NOTFOUND", fp, sizeof(fp)));
ASSRT(0 == build_path("/home", "chipp", fp, sizeof(fp)));
ASSRT(0 == build_path("/home", "ip", fp, sizeof(fp)));
ASSRT(0 == build_path("/NOTFOUND", "ch", fp, sizeof(fp)));
printf("Testing get_spi_bus_path_number\n");
ASSRT(2 == get_spi_bus_path_number(0)); /* doesn't really work on CHIP */
ASSRT(2 == get_spi_bus_path_number(1)); /* doesn't really work on CHIP */
printf("Testing error message buffer\n");
clear_error_msg();
ASSRT(0 == strlen(get_error_msg()));
char *s100 = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
add_error_msg(s100); ASSRT(100 == strlen(get_error_msg()));
// Subsequent messages added include a newline separator.
add_error_msg(s100); add_error_msg(s100); add_error_msg(s100); ASSRT(403 == strlen(get_error_msg()));
add_error_msg(s100); add_error_msg(s100); add_error_msg(s100); ASSRT(706 == strlen(get_error_msg()));
add_error_msg(s100); add_error_msg(s100); add_error_msg(s100); ASSRT(1009 == strlen(get_error_msg()));
add_error_msg(s100); add_error_msg(s100); add_error_msg(s100); ASSRT(1023 == strlen(get_error_msg()));
printf("Testing dynamic integer array\n");
dyn_int_array_t *my_array = NULL;
ASSRT(-2 == dyn_int_array_get(&my_array, 29, -2)); ASSRT(my_array->num_elements == 45);
dyn_int_array_set(&my_array, 44, 3, -2); ASSRT(my_array->num_elements == 45);
ASSRT(3 == dyn_int_array_get(&my_array, 44, -2)); ASSRT(my_array->num_elements == 45);
dyn_int_array_set(&my_array, 45, 6, -2); ASSRT(my_array->num_elements == 69);
ASSRT(6 == dyn_int_array_get(&my_array, 45, -2)); ASSRT(my_array->num_elements == 69);
dyn_int_array_delete(&my_array);
int value = input;
PyObject *py_value = Py_BuildValue("i", value);
return py_value;
}
static const char moduledocstring[] = "GPIO functionality of a CHIP using Python";
PyMethodDef gpio_methods[] = {
{"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: CHIP board pin number (not R8 GPIO 00..nn number). Pins start from 1\n or : CHIP GPIO name\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel"},
{"cleanup", py_cleanup, METH_VARARGS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection"},
{"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel\ngpio - gpio channel\nvalue - 0/1 or False/True or LOW/HIGH"},
{"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel. Returns HIGH=1=True or LOW=0=False\ngpio - gpio channel"},
{"add_event_detect", (PyCFunction)py_add_event_detect, METH_VARARGS | METH_KEYWORDS, "Enable edge detection events for a particular GPIO channel.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[callback] - A callback function for the event (optional)\n[bouncetime] - Switch bounce timeout in ms for callback"},
{"remove_event_detect", py_remove_event_detect, METH_VARARGS, "Remove edge detection for a particular GPIO channel\ngpio - gpio channel"},
{"event_detected", py_event_detected, METH_VARARGS, "Returns True if an edge has occured on a given GPIO. You need to enable edge detection using add_event_detect() first.\ngpio - gpio channel"},
{"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\ngpio - gpio channel\ncallback - a callback function\n[bouncetime] - Switch bounce timeout in ms"},
{"wait_for_edge", py_wait_for_edge, METH_VARARGS, "Wait for an edge.\ngpio - gpio channel\nedge - RISING, FALLING or BOTH"},
{"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, ALT0)\ngpio - gpio channel"},
{"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"},
{"get_gpio_base", py_gpio_base, METH_VARARGS, "Get the XIO base number for sysfs"},
{"selftest", py_selftest, METH_VARARGS, "Internal unit tests"},
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION > 2
static struct PyModuleDef rpigpiomodule = {
PyModuleDef_HEAD_INIT,
"GPIO", // name of module
moduledocstring, // module documentation, may be NULL
-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables.
gpio_methods
};
#endif
#if PY_MAJOR_VERSION > 2
PyMODINIT_FUNC PyInit_GPIO(void)
#else
PyMODINIT_FUNC initGPIO(void)
#endif
{
PyObject *module = NULL;
clear_error_msg();
#if PY_MAJOR_VERSION > 2
if ((module = PyModule_Create(&rpigpiomodule)) == NULL)
return NULL;
#else
if ((module = Py_InitModule3("GPIO", gpio_methods, moduledocstring)) == NULL)
return;
#endif
define_constants(module);
if (!PyEval_ThreadsInitialized())
PyEval_InitThreads();
if (Py_AtExit(event_cleanup) != 0)
{
setup_error = 1;
event_cleanup();
#if PY_MAJOR_VERSION > 2
return NULL;
#else
return;
#endif
}
#if PY_MAJOR_VERSION > 2
return module;
#else
return;
#endif
}