0

I'm struggling with my C written Python library. The code is meant to write a register in a cp210x in order to control a Relay card. The code works, however Python clears the object somehow.

With other words, the C variable ttyPort is cleared after ending a function.

Here's the C code:

#include <Python.h>
#include <fcntl.h>
#include <stropts.h>

// C variable that holds the tty address (e.g. /dev/ttyUSB0)
const char* ttyPort;

int setRelay(int action, int relayNumber)
{
    /* more magic over here */
    printf("Port :: %s\n", ttyPort);

}
// All python wrappers below

static PyObject*setPort(PyObject* self, PyObject* args)
{
    Py_INCREF(self)
    // Copy python argument to ttyPort
    if (!PyArg_ParseTuple(args, "s", &ttyPort))
        return NULL;

    printf("RelayModule :: Port set (%s)\n", ttyPort);

    Py_RETURN_NONE;
}

static PyObject*fanOff(PyObject* self, PyObject* args)
{
    //fanMode = 0;
    printf("RelayModule :: Fan off %s\n",ttyPort);
    if (setRelay(RELAY_OFF, 0) == 1){
        // more magic
    }
    Py_RETURN_NONE;
}
/* more functions over here */
static PyMethodDef RelayMethods[] =
{
     {"setPort", setPort, METH_VARARGS, "Set tty port."},
     {"fanOff", fanOff, METH_NOARGS, "Fan off."},
     {"fanHalf", fanHalf, METH_NOARGS, "Fan half speed."},
     {"fanFull", fanFull, METH_NOARGS, "Fan full on."},
     {"pumpOn", pumpOn, METH_NOARGS, "Pump on."},
     {"pumpOff", pumpOff, METH_NOARGS, "Pump off."},
     {"isPumpOn", isPumpOn, METH_NOARGS, "Check if pump is on."},
     {"getFanMode", getFanMode, METH_NOARGS, "Get fan mode."},
     {NULL, NULL, 0, NULL}
};

static struct PyModuleDef RelayDefs = { 
    PyModuleDef_HEAD_INIT,"RelayModule","A Python module that controls the Conrad 4ch relay card.", -1, RelayMethods 
};


PyMODINIT_FUNC PyInit_Relay(void)
{
    Py_Initialize();
    return PyModule_Create(&RelayDefs);
}

The python code for example:

import Relay

def settty():
    Relay.setPort("/dev/ttyUSB0")

def gettty():
    Relay.fanOff()

def doAll():
    Relay.setPort("/dev/ttyUSB0")
    Relay.fanOff()

settty()
gettty() #Error, prints 'Port :: [garbage]'
doAll() #works!

How can I somehow declare an object? In other languages I'd do: RelayObj = new Relay()

Or how can I store a variable correctly?

Kramer
  • 35
  • 1
  • 7
  • Embed it in an object and make the functions member functions. – wildplasser Jan 18 '17 at 11:05
  • I think one of the main questions was how I'd do that – Kramer Jan 18 '17 at 11:31
  • With your current implementation, try storing `PyModule_Create`'s return value in static var. I suspect that returning it right away may cause the optimizer to remove it right away (or disable compiler optimizations to see if it indeed interferes). – daragua Jan 18 '17 at 11:37
  • Like : `class myrelay(Relay): ... def __init__(self, name): ...` (plus other functions). Then you can instantiate by using `thisrelay = myrelay( '/dev/ttyS0' )` – wildplasser Jan 18 '17 at 11:47
  • `class myrelay(Relay): def __init__(self, name): self.setPort(name)` doesn't work, return error like `module.__init__() takes at most 2 arguments (3 given)` – Kramer Jan 18 '17 at 13:10

1 Answers1

0

One possible fix would be to use 'es' format specifier in PyArg_ParseTuple(args, "s", &ttyPort) instead of 's'. From documentation:

In general, when a format sets a pointer to a buffer, the buffer is managed by the corresponding Python object, and the buffer shares the lifetime of this object

unless you use 'es'. You will have to free memory manually, however.

Michail
  • 1,843
  • 1
  • 17
  • 21
  • Looks like this solved my problem, however I also needed to add the encoding like this: `if (!PyArg_ParseTuple(args, "es", "utf-8", &ttyPort))` – Kramer Jan 18 '17 at 13:07