diff --git a/README.md b/README.md index acc44a3..89dbfeb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Grammar mistake in this file ? Please open an issue ! + # GPIOEmu GPIOEmu is a python module able to emulate some functions of the RPi.GPIO module, for testing purpose (or educational, but remember that students also need reality). @@ -9,8 +11,8 @@ GPIOEmu is a python module able to emulate some functions of the RPi.GPIO module You need a UNIX-like system to run this emulator, such as GNU/Linux, FreeBSD... It *will not* work on Micro$oft Windows, but it is designed to be easy to port (it mostly uses the standard library or cross-plateform libraries). Dependencies: -* Python 2 or 3 (yes, it runs with both, like the RPi.GPIO module) -* SDL 2 library +* Python3 +* SDL2 library Run, as root: ``` @@ -18,6 +20,8 @@ Run, as root: # python3 setup.py install # Install the GPIOEmu python extension ``` +Note: it should be able to run with Python2, however, there may be bugs. + ## How to use it Documentation: mostly the same as [RPi.GPIO](https://sourceforge.net/p/raspberry-gpio-python/wiki/Home/). See "differences with the RPi.GPIO module" for... differences. @@ -40,7 +44,7 @@ The GPIO is for the moment always rev 3, but the code is already designed to be Functions are the same, warnings are the same... -* The interrupt functions (`add_event_detect`...) are not available yet. +* The interrupt functions (`add_event_detect`, `wait_for_edge`, `remove_event_detect`, `event_detected`, `add_event_callback`) may have different behaviour, because of their different internal implementation. * The RPi.GPIO module provides a RPI_INFO dictionary containing 6 fields. This dictionary is also providded by GPIOEmu, but it contains only one field (P1_REVISION), also accessible as the RPI_REVISION deprecated variable (provided both by RPi.GPIO and GPIOEmu). * The RPi.GPIO module is able to tell you many more modes using `gpio_mode()`. This function of the GPIOEmu module returns either INPUT, OUTPUT or PWM (or -1). @@ -48,7 +52,7 @@ Functions are the same, warnings are the same... Near all the python interface code is from the RPi.GPIO module. -Unlike the RPi.GPIO module, it has two threads: +Unlike the RPi.GPIO module, it has two threads (without counting the optional event thread): * The main thread (your python program). GPIOEmu API calls in the main thread change some variable values, or read them. * The GUI thread, which periodically reads variable values to draw the window and handles X events. diff --git a/setup.py b/setup.py index 3ad50a4..d6402a5 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ url = 'https://github.com/paly2/GPIOEmu', classifiers = classifiers, #packages = ['GPIOEmu'], - ext_modules = [Extension(name='GPIOEmu', sources=['src/py_gpio.c', 'src/GUI.c', 'src/common.c', 'src/constants.c', 'src/py_pwm.c'], libraries=["SDL2", "pthread"])]) + ext_modules = [Extension(name='GPIOEmu', sources=['src/py_gpio.c', 'src/GUI.c', 'src/common.c', 'src/constants.c', 'src/py_pwm.c', 'src/event_gpio.c'], libraries=["SDL2", "pthread"])]) # Copy images to /usr/share/GPIOEmu print("copying src/images/ -> /usr/share/GPIOEmu/images/") diff --git a/src/GUI.c b/src/GUI.c index 3936c5a..6159cf4 100644 --- a/src/GUI.c +++ b/src/GUI.c @@ -8,6 +8,7 @@ #include "GUI.h" #include "common.h" #include "constants.h" +#include "event_gpio.h" static SDL_Window* window = NULL; static SDL_Renderer* renderer = NULL; @@ -27,10 +28,16 @@ void close_GUI(void) { SDL_DestroyTexture(GPIO_image); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); + SDL_DestroyCond(event_cond); + SDL_DestroyMutex(event_lock); SDL_Quit(); } int load_GUI(void) { + // Initialize the event_cond condition and the event_lock mutex + event_cond = SDL_CreateCond(); + event_lock = SDL_CreateMutex(); + // SDL_Init / SDL_Quit Py_AtExit(close_GUI); if (SDL_Init(SDL_INIT_VIDEO) == -1) { @@ -88,6 +95,17 @@ int load_GUI(void) { return 0; } +static void change_state(int gpio) { + gpio_state[gpio] = (gpio_state[gpio] == STATE_LOW) ? STATE_HIGH : STATE_LOW; + + // Signal event_cond + SDL_LockMutex(event_lock); + event_channel = gpio; + event_edge = (gpio_state[gpio] == STATE_HIGH) ? RISING_EDGE : FALLING_EDGE; + SDL_CondBroadcast(event_cond); + SDL_UnlockMutex(event_lock); +} + static void on_click(Sint32 x, Sint32 y) { int row = y/32, column; unsigned int pin; @@ -107,7 +125,7 @@ static void on_click(Sint32 x, Sint32 y) { if (gpio_direction[gpio] != INPUT) return; - gpio_state[gpio] = (gpio_state[gpio] == STATE_LOW) ? STATE_HIGH : STATE_LOW; + change_state(gpio); } static void draw_static(void) { diff --git a/src/common.h b/src/common.h index 8b57963..8d93c17 100644 --- a/src/common.h +++ b/src/common.h @@ -25,6 +25,4 @@ int gpio_state[28]; int pull_up_down[28]; int rpi_p1_revision; int setup_error; -int check_gpio_priv(void); int get_gpio_number(int channel, unsigned int *gpio); -unsigned int bcm_from_board(unsigned int gpio); diff --git a/src/constants.c b/src/constants.c index a986309..cbd4cd2 100644 --- a/src/constants.c +++ b/src/constants.c @@ -1,7 +1,7 @@ #include "Python.h" #include "constants.h" #include "common.h" -//#include "event_gpio.h" +#include "event_gpio.h" void define_constants(PyObject *module) { @@ -46,7 +46,7 @@ void define_constants(PyObject *module) pud_down = Py_BuildValue("i", PUD_DOWN + PY_PUD_CONST_OFFSET); PyModule_AddObject(module, "PUD_DOWN", pud_down); -/* + rising_edge = Py_BuildValue("i", RISING_EDGE + PY_EVENT_CONST_OFFSET); PyModule_AddObject(module, "RISING", rising_edge); @@ -55,5 +55,4 @@ void define_constants(PyObject *module) both_edge = Py_BuildValue("i", BOTH_EDGE + PY_EVENT_CONST_OFFSET); PyModule_AddObject(module, "BOTH", both_edge); -*/ } diff --git a/src/event_gpio.c b/src/event_gpio.c new file mode 100644 index 0000000..f956f33 --- /dev/null +++ b/src/event_gpio.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include "event_gpio.h" + +/* Note about this file: + * I try to respect the functions of the RPi.GPIO event_gpio.c file. However, + * the implementation differs a lot, and their behaviour also differ. We use + * SDL_Thread instead of pthread.h. The callbacks are in the gpios structure. + * The wait_for_edge function does not use an event. Etc. + * Finally the code is much simpler, but I repeat: it may have a different + * behaviour. + */ +const char *stredge[4] = {"none", "rising", "falling", "both"}; + +SDL_mutex* event_lock; +SDL_cond* event_cond; + +struct gpios { + unsigned int gpio; + int edge; + void (*callback)(unsigned int gpio); + int bouncetime; + struct gpios *next; +}; +struct gpios *gpio_list = NULL; + +SDL_Thread* threads; + +int event_occurred[28] = {0}; +int thread_running = 0; + +/********* gpio list functions **********/ +struct gpios *get_gpio(unsigned int gpio) { + struct gpios *g = gpio_list; + while (g != NULL) { + if (g->gpio == gpio) + return g; + g = g->next; + } + return NULL; +} + +struct gpios *new_gpio(unsigned int gpio) { + struct gpios *new_gpio; + + new_gpio = malloc(sizeof(struct gpios)); + if (new_gpio == 0) + return NULL; // out of memory + + new_gpio->gpio = gpio; + new_gpio->callback = NULL; + + if (gpio_list == NULL) + new_gpio->next = NULL; + else + new_gpio->next = gpio_list; + gpio_list = new_gpio; + return new_gpio; +} + +void delete_gpio(unsigned int gpio) { + struct gpios *g = gpio_list; + struct gpios *temp; + struct gpios *prev = NULL; + + while (g != NULL) { + if (g->gpio == gpio) { + if (prev == NULL) + gpio_list = g->next; + else + prev->next = g->next; + temp = g; + g = g->next; + free(temp); + return; + } else { + prev = g; + g = g->next; + } + } +} + +int gpio_event_added(unsigned int gpio) { + struct gpios *g = gpio_list; + while (g != NULL) { + if (g->gpio == gpio) + return g->edge; + g = g->next; + } + return 0; +} + +/******* callback list functions ********/ +int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)) { + struct gpios *g; + + if ((g = get_gpio(gpio)) == NULL) + return -1; + + g->callback = func; + + return 0; +} + + +int poll_thread(void *threadarg) { + struct gpios *g; + + SDL_LockMutex(event_lock); + thread_running = 1; + while (1) { + SDL_CondWait(event_cond, event_lock); + if (event_channel == -1) { // Stop + thread_running = 0; + break; + } + + if ((g = get_gpio(event_channel)) == NULL) + continue; + if (g->edge == event_edge || g->edge == BOTH_EDGE) { + event_occurred[g->gpio] = 1; + if (g->callback != NULL) + g->callback(event_channel); + } + } + SDL_UnlockMutex(event_lock); + + return 0; +} + +void remove_edge_detect(unsigned int gpio) { + struct gpios *g = get_gpio(gpio); + + if (g == NULL) + return; + + delete_gpio(gpio); + + event_occurred[gpio] = 0; +} + +int event_detected(unsigned int gpio) { + if (event_occurred[gpio]) { + event_occurred[gpio] = 0; + return 1; + } else { + return 0; + } +} + +void event_cleanup(unsigned int gpio) +// gpio of -666 means clean every channel used +{ + struct gpios *g = gpio_list; + struct gpios *temp = NULL; + + while (g != NULL) { + if ((gpio == -666) || (g->gpio == gpio)) { + temp = g->next; + remove_edge_detect(g->gpio); + g = temp; + } + } + + // Stop poll thread and pending "wait_for_edge" + SDL_LockMutex(event_lock); + event_channel = -1; + SDL_CondBroadcast(event_cond); + SDL_UnlockMutex(event_lock); +} + +void event_cleanup_all(void) { + event_cleanup(-666); +} + +int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime) +// return values: +// 0 - Success +// 1 - Edge detection already added +// 2 - Other error +{ + int i = -1; + struct gpios* g; + + i = gpio_event_added(gpio); + if (i == 0) { // event not already added + if ((g = new_gpio(gpio)) == NULL) + return 2; + + g->edge = edge; + g->bouncetime = bouncetime; + } else if (i == edge) { // get existing event + g = get_gpio(gpio); + if ((bouncetime != -666 && g->bouncetime != bouncetime)) // different event bouncetime used + return 1; + } else { + return 1; + } + + // start poll thread if it is not already running + if (!thread_running) { + if ((threads = SDL_CreateThread(poll_thread, "PollThread", (void *)NULL)) == NULL) { + remove_edge_detect(gpio); + return 2; + } + } + return 0; +} + +int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout) +// return values: +// 1 - Success (edge detected) +// 0 - Timeout +// -1 - Edge detection already added +// -2 - Other error +{ + int ed; + struct gpios *g = NULL; + int ret; + + if (timeout == -1) // No timeout + timeout = SDL_MUTEX_MAXWAIT; + + /* We add a GPIO event to get a behaviour similar the RPi.GPIO module + * one, but we don't use it. + */ + // add gpio if it has not been added already + ed = gpio_event_added(gpio); + if (ed == edge) { // get existing record + g = get_gpio(gpio); + if (g->bouncetime != -666 && g->bouncetime != bouncetime) + return -1; + } else if (ed == NO_EDGE) { // not found so add event + if ((g = new_gpio(gpio)) == NULL) + return -2; + g->edge = edge; + g->bouncetime = bouncetime; + } else { // ed != edge - event for a different edge + g = get_gpio(gpio); + g->edge = edge; + g->bouncetime = bouncetime; + } + + // wait for edge + SDL_LockMutex(event_lock); + while (1) { + ret = SDL_CondWaitTimeout(event_cond, event_lock, timeout); + if (ret == SDL_MUTEX_TIMEDOUT) { // timed out + SDL_UnlockMutex(event_lock); + return 0; + } + if (event_channel == -1) // Stop + return -2; + if (event_channel == gpio && (event_edge == edge || edge == BOTH_EDGE)) // event detected + break; + } + SDL_UnlockMutex(event_lock); + return 1; +} diff --git a/src/event_gpio.h b/src/event_gpio.h new file mode 100644 index 0000000..2d68a13 --- /dev/null +++ b/src/event_gpio.h @@ -0,0 +1,21 @@ +#include + +#define NO_EDGE 0 +#define RISING_EDGE 1 +#define FALLING_EDGE 2 +#define BOTH_EDGE 3 + +SDL_mutex* event_lock; +SDL_cond* event_cond; +int event_channel; +int event_edge; + +int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime); +void remove_edge_detect(unsigned int gpio); +int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)); +int event_detected(unsigned int gpio); +int gpio_event_added(unsigned int gpio); +int event_initialise(void); +void event_cleanup(unsigned int gpio); +void event_cleanup_all(void); +int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout); diff --git a/src/py_gpio.c b/src/py_gpio.c index cd51646..b6c2837 100644 --- a/src/py_gpio.c +++ b/src/py_gpio.c @@ -1,6 +1,6 @@ #include "Python.h" #include "stdio.h" - +#include "event_gpio.h" #include "GUI.h" #include "common.h" #include "constants.h" @@ -8,15 +8,12 @@ static int gpio_warnings = 1; -/* -struct py_callback -{ +struct py_callback { unsigned int gpio; PyObject *py_cb; struct py_callback *next; }; static struct py_callback *py_callbacks = NULL; -*/ // python function cleanup(channel=None) static PyObject *py_cleanup(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -483,6 +480,267 @@ static PyObject *py_getmode(PyObject *self, PyObject *args) { return value; } +static unsigned int chan_from_gpio(unsigned int gpio) { + int chan; + int chans; + + if (gpio_mode == BCM) + return gpio; + else if (rpi_p1_revision == 1 || rpi_p1_revision == 2) + chans = 26; + else + chans = 40; + for (chan=1; chan<=chans; chan++) + if (*(*pin_to_gpio+chan) == gpio) + return chan; + return -1; +} + +static void run_py_callbacks(unsigned int gpio) { + PyObject *result; + PyGILState_STATE gstate; + struct py_callback *cb = py_callbacks; + + while (cb != NULL) { + if (cb->gpio == gpio) { + // run callback + gstate = PyGILState_Ensure(); + result = PyObject_CallFunction(cb->py_cb, "i", chan_from_gpio(gpio)); + if (result == NULL && PyErr_Occurred()){ + PyErr_Print(); + PyErr_Clear(); + } + Py_XDECREF(result); + PyGILState_Release(gstate); + } + cb = cb->next; + } +} + +static int add_py_callback(unsigned int gpio, PyObject *cb_func) { + struct py_callback *new_py_cb; + struct py_callback *cb = py_callbacks; + + // 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 + new_py_cb->gpio = gpio; + 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) +static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject *kwargs) { + unsigned int gpio; + int channel; + PyObject *cb_func; + char *kwlist[] = {"gpio", "callback", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|i", kwlist, &channel, &cb_func)) + return NULL; + + if (!PyCallable_Check(cb_func)) { + PyErr_SetString(PyExc_TypeError, "Parameter must be callable"); + return NULL; + } + + if (get_gpio_number(channel, &gpio)) + return NULL; + + // check channel is set up as an input + if (gpio_direction[gpio] != INPUT) { + PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); + return NULL; + } + + if (!gpio_event_added(gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Add event detection using add_event_detect first before adding a callback"); + return NULL; + } + + if (add_py_callback(gpio, cb_func) != 0) + return NULL; + + Py_RETURN_NONE; +} + +// python function add_event_detect(gpio, edge, callback=None, bouncetime=None) +static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *kwargs) { + unsigned int gpio; + int channel, edge, result; + int bouncetime = -666; + PyObject *cb_func = NULL; + char *kwlist[] = {"gpio", "edge", "callback", "bouncetime", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|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)) + return NULL; + + // check channel is set up as an input + if (gpio_direction[gpio] != INPUT) { + PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); + return NULL; + } + + // is edge valid value + edge -= PY_EVENT_CONST_OFFSET; + 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; + } + + if (bouncetime <= 0 && bouncetime != -666) { + PyErr_SetString(PyExc_ValueError, "Bouncetime must be greater than 0"); + return NULL; + } + + if ((result = add_edge_detect(gpio, edge, bouncetime)) != 0) { // starts a thread { + if (result == 1) { + PyErr_SetString(PyExc_RuntimeError, "Conflicting 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(gpio, cb_func) != 0) + return NULL; + + Py_RETURN_NONE; +} + +// python function remove_event_detect(gpio) +static PyObject *py_remove_event_detect(PyObject *self, PyObject *args) { + unsigned int gpio; + int channel; + struct py_callback *cb = py_callbacks; + struct py_callback *temp; + struct py_callback *prev = NULL; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (get_gpio_number(channel, &gpio)) + 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) { + unsigned int gpio; + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (get_gpio_number(channel, &gpio)) + return NULL; + + if (event_detected(gpio)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +// python function channel = wait_for_edge(channel, edge, bouncetime=None, timeout=None) +static PyObject *py_wait_for_edge(PyObject *self, PyObject *args, PyObject *kwargs) { + unsigned int gpio; + int channel, edge, result; + int bouncetime = -666; // None + int timeout = -1; // None + + static char *kwlist[] = {"channel", "edge", "bouncetime", "timeout", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", kwlist, &channel, &edge, &bouncetime, &timeout)) + return NULL; + + if (get_gpio_number(channel, &gpio)) + return NULL; + + // check channel is setup as an input + if (gpio_direction[gpio] != INPUT) { + PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); + return NULL; + } + + // is edge a valid value? + edge -= PY_EVENT_CONST_OFFSET; + 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; + } + + if (bouncetime <= 0 && bouncetime != -666) { + PyErr_SetString(PyExc_ValueError, "Bouncetime must be greater than 0"); + return NULL; + } + + if (timeout <= 0 && timeout != -1) { + PyErr_SetString(PyExc_ValueError, "Timeout must be greater than 0"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS // disable GIL + result = blocking_wait_for_edge(gpio, edge, bouncetime, timeout); + Py_END_ALLOW_THREADS // enable GIL + + if (result == 0) { + Py_RETURN_NONE; + } else if (result == -1) { + PyErr_SetString(PyExc_RuntimeError, "Conflicting edge detection events already exist for this GPIO channel"); + return NULL; + } else if (result == -2) { + PyErr_SetString(PyExc_RuntimeError, "Error waiting for edge"); + return NULL; + } else { + return Py_BuildValue("i", channel); + } + +} + // python function value = gpio_function(channel) static PyObject *py_gpio_function(PyObject *self, PyObject *args) { unsigned int gpio; @@ -524,11 +782,11 @@ PyMethodDef rpi_gpio_methods[] = { {"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel. Returns HIGH=1=True or LOW=0=False\nchannel - either board pin number or BCM number depending on which mode is set."}, {"setmode", py_setmode, METH_VARARGS, "Set up numbering mode to use for channels.\nBOARD - Use Raspberry Pi board numbers\nBCM - Use Broadcom GPIO 00..nn numbers"}, {"getmode", py_getmode, METH_VARARGS, "Get numbering mode used for channel numbers.\nReturns BOARD, BCM or None"}, - //{"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\nchannel - either board pin number or BCM number depending on which mode is set."}, - //{"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.\nchannel - either board pin number or BCM number depending on which mode is set."}, - //{"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\nchannel - either board pin number or BCM number depending on which mode is set.\ncallback - a callback function"}, - //{"wait_for_edge", (PyCFunction)py_wait_for_edge, METH_VARARGS | METH_KEYWORDS, "Wait for an edge. Returns the channel number or None on timeout.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[bouncetime] - time allowed between calls to allow for switchbounce\n[timeout] - timeout in ms"}, + {"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\nchannel - either board pin number or BCM number depending on which mode is set."}, + {"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.\nchannel - either board pin number or BCM number depending on which mode is set."}, + {"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\nchannel - either board pin number or BCM number depending on which mode is set.\ncallback - a callback function"}, + {"wait_for_edge", (PyCFunction)py_wait_for_edge, METH_VARARGS | METH_KEYWORDS, "Wait for an edge. Returns the channel number or None on timeout.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[bouncetime] - time allowed between calls to allow for switchbounce\n[timeout] - timeout in ms"}, {"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, PWM, SERIAL, I2C, SPI)\nchannel - either board pin number or BCM number depending on which mode is set."}, {"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, {NULL, NULL, 0, NULL} @@ -547,7 +805,7 @@ static struct PyModuleDef gpioemumodule = { #if PY_MAJOR_VERSION > 2 PyMODINIT_FUNC PyInit_GPIOEmu(void) #else -PyMODINIT_FUNC init_GPIOEmu(void) +PyMODINIT_FUNC initGPIOEmu(void) #endif { int i;