Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readbytesb -- return bytes object for efficiency #80

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ Connects to the specified SPI device, opening `/dev/spidev<bus>.<device>`

readbytes(n)

Read n bytes from SPI device.
Read n bytes from SPI device, returning a list of integers.

readbytesb(n)

Read n bytes from SPI device, returning an (immutable) bytes object (more efficient for larger transfers).

writebytes(list of values)

Expand Down
115 changes: 91 additions & 24 deletions spidev_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,27 +223,41 @@ SpiDev_writebytes(SpiDevObject *self, PyObject *args)
return Py_None;
}

PyDoc_STRVAR(SpiDev_read_doc,
"read(len) -> [values]\n\n"
"Read len bytes from SPI device.\n");

static PyObject *
SpiDev_readbytes(SpiDevObject *self, PyObject *args)
SpiDev_readbytes_generic(SpiDevObject *self, PyObject *args, int resultType)
{
uint8_t rxbuf[SPIDEV_MAXPATH];
int status, len, ii;
PyObject *list;

if (!PyArg_ParseTuple(args, "i:read", &len))
return NULL;

/* read at least 1 byte, no more than SPIDEV_MAXPATH */
if (len < 1)
len = 1;
else if ((unsigned)len > sizeof(rxbuf))
len = sizeof(rxbuf);
static uint8_t stackbuf[SPIDEV_MAXPATH];
int status, len;
Py_buffer pybuff;
uint8_t* rxbuf = stackbuf;

if (resultType == 2) { //existing bytearray
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove magic numbers (Add #define for resultType)

if (!PyArg_ParseTuple(args, "y*", &pybuff))
return NULL;
if (!PyBuffer_IsContiguous(&pybuff, 'A')) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: buffer must be contiguous");
return NULL;
}
len = pybuff.len;
rxbuf = pybuff.buf;
if (len < 1) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: buffer must not be empty");
return NULL;
}
}
else {
if (!PyArg_ParseTuple(args, "i:read", &len))
return NULL;

/* read at least 1 byte, no more than SPIDEV_MAXPATH */
if (len < 1)
len = 1;
else if ((unsigned)len > sizeof(stackbuf))
len = sizeof(stackbuf);
memset(rxbuf, 0, sizeof(stackbuf));
}

memset(rxbuf, 0, sizeof rxbuf);
status = read(self->fd, &rxbuf[0], len);

if (status < 0) {
Expand All @@ -252,20 +266,69 @@ SpiDev_readbytes(SpiDevObject *self, PyObject *args)
}

if (status != len) {
perror("short read");
printf("Short Read: %d/%d bytes\n", status, len);
PyErr_SetString(PyExc_IOError, "SpiDev.readbuffer: I/O error: short read.");
return NULL;
}

list = PyList_New(len);
if (resultType == 0) { // list
int ii;
PyObject *list;
list = PyList_New(len);

for (ii = 0; ii < len; ii++) {
PyObject *val = Py_BuildValue("l", (long)rxbuf[ii]);
PyList_SET_ITEM(list, ii, val);
}
for (ii = 0; ii < len; ii++) {
PyObject *val = Py_BuildValue("l", (long)rxbuf[ii]);
PyList_SET_ITEM(list, ii, val);
}

return list;
}
else if (resultType == 1) { // bytes
PyObject *bytes;
bytes = Py_BuildValue("y#", rxbuf, len);
return bytes;
}
else if (resultType == 2) { // bytearray
Py_RETURN_NONE;
}
else { // unknown type
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: internal problem. Type was not 0, 1 or 2. Weird.");
return NULL;
}
}


PyDoc_STRVAR(SpiDev_read_doc,
"read(len) -> [values]\n\n"
"Read len bytes from SPI device, returning a list.\n");

static PyObject *
SpiDev_readbytes(SpiDevObject *self, PyObject *args)
{
return SpiDev_readbytes_generic(self, args, 0);
}

return list;
PyDoc_STRVAR(SpiDev_readb_doc,
"read(len) -> [values]\n\n"
"Read len bytes from SPI device, returning a bytes object.\n");

static PyObject *
SpiDev_readbytesb(SpiDevObject *self, PyObject *args)
{
return SpiDev_readbytes_generic(self, args, 1);
}

PyDoc_STRVAR(SpiDev_readbuffer_doc,
"read(bytearray) -> [values]\n\n"
"Read bytes from SPI device, filling the supplied byte array.\n");

static PyObject *
SpiDev_readbuffer(SpiDevObject *self, PyObject *args)
{
return SpiDev_readbytes_generic(self, args, 2);
}


static PyObject *
SpiDev_writebytes2_buffer(SpiDevObject *self, Py_buffer *buffer)
{
Expand Down Expand Up @@ -861,6 +924,7 @@ SpiDev_xfer3(SpiDevObject *self, PyObject *args)
return rx_tuple;
}


static int __spidev_set_mode( int fd, __u8 mode) {
__u8 test;
if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
Expand Down Expand Up @@ -1365,6 +1429,8 @@ static PyMethodDef SpiDev_methods[] = {
SpiDev_fileno_doc},
{"readbytes", (PyCFunction)SpiDev_readbytes, METH_VARARGS,
SpiDev_read_doc},
{"readbytesb", (PyCFunction)SpiDev_readbytesb, METH_VARARGS,
SpiDev_readb_doc},
{"writebytes", (PyCFunction)SpiDev_writebytes, METH_VARARGS,
SpiDev_write_doc},
{"writebytes2", (PyCFunction)SpiDev_writebytes2, METH_VARARGS,
Expand All @@ -1375,6 +1441,7 @@ static PyMethodDef SpiDev_methods[] = {
SpiDev_xfer2_doc},
{"xfer3", (PyCFunction)SpiDev_xfer3, METH_VARARGS,
SpiDev_xfer3_doc},
{"readbuffer", (PyCFunction)SpiDev_readbuffer, METH_VARARGS, SpiDev_readbuffer_doc},
{"__enter__", (PyCFunction)SpiDev_enter, METH_VARARGS,
NULL},
{"__exit__", (PyCFunction)SpiDev_exit, METH_VARARGS,
Expand Down
31 changes: 31 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import sys
sys.path = ['.'] + sys.path

# https://pypi.org/project/spidev/
import spidev
import numpy
import timeit
class testspi (object):
def __init__(self):
spi=spidev.SpiDev()
spi.open(0, 0)
print("devspi readbytes", spi.readbytes(50))
spibytes = spi.readbytesb(50)
npbytes = numpy.frombuffer(spibytes, dtype=numpy.int16)
print("devspi readbytesb", npbytes)
bytes = 1000
buffer = bytearray(1000)
count = 1000
def read(): spi.readbytes(bytes)
def readb(): spi.readbytesb(bytes)
def readbuffer(): spi.readbuffer(buffer)
duration = timeit.timeit(read, number=count)
print('list: total dur %f; secs/byte=%f; bytes/sec=%f' % (duration, duration/(bytes*count), (bytes*count)/duration))
duration = timeit.timeit(readb, number=count)
print('bytes: total dur %f; secs/byte=%f; bytes/sec=%f' % (duration, duration/(bytes*count), (bytes*count)/duration))
duration = timeit.timeit(readbuffer, number=count)
print('bytes: total dur %f; secs/byte=%f; bytes/sec=%f' % (duration, duration/(bytes*count), (bytes*count)/duration))
spi.close()


testspi()