Logitech Pan/Tilt Python C Extension

I just started learning C++ in earnest about a month ago. This weekend I felt like I knew enough to start looking at Python C extensions, so I wrote one to control the pan and tilt functions of my Logitech Orbit. I used this camera previously for my OLPC telepresence project.

There's already a similar module out there, called lpantilt, that does this using Cython. But, I wanted to take a crack at it myself and do it with straight C.
#include <Python.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include "linux/videodev2.h"
#include "uvcvideo.h"

static int pantilt(int pan, int tilt, int reset) {
struct v4l2_ext_control xctrls[2];
struct v4l2_ext_controls ctrls;

if (reset) {
xctrls[0].id = V4L2_CID_PAN_RESET;
xctrls[0].value = 1;
xctrls[1].id = V4L2_CID_TILT_RESET;
xctrls[1].value = 1;
} else {
xctrls[0].id = V4L2_CID_PAN_RELATIVE;
xctrls[0].value = pan;
xctrls[1].id = V4L2_CID_TILT_RELATIVE;
xctrls[1].value = tilt;

ctrls.count = 2;
ctrls.controls = xctrls;

int fd;
if (-1 == (fd = open("/dev/video0", O_RDWR))) {
PyErr_SetString(PyExc_IOError, "Couldn't open /dev/video0.");
return 0;

if (-1 == ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) {
PyErr_SetString(PyExc_IOError, "ioctl failed.");
return 0;

if (-1 == close(fd)) {
PyErr_SetString(PyExc_IOError, "Failed to close /dev/video0.");
return 0;

return 1;

static PyObject* pantilt_reset(PyObject* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "")) {
return NULL;
if (!pantilt(0, 0, 1)) {
return NULL;

static PyObject* pantilt_pantilt(PyObject* self, PyObject* args) {
int pan;
int tilt;
if (!PyArg_ParseTuple(args, "ii", &pan, &tilt)) {
return NULL;
if (!pantilt(pan * 64, tilt * 64, 0)) {
return NULL;

static PyMethodDef PantiltMethods[] = {
{"pantilt", pantilt_pantilt, METH_VARARGS, "Set relative pan and tilt of the camera."},
{"reset", pantilt_reset, METH_VARARGS, "Reset the pan and tilt of the camera."},

PyMODINIT_FUNC initpantilt(void) {
(void) Py_InitModule("pantilt", PantiltMethods);
And here is the associated setup.py script to build it:
from distutils.core import Extension
from distutils.core import setup

m = Extension('pantilt', sources=['pantilt.c'])

description='Control pan and tilt of supported webcams.',
To get this to build and run on Ubuntu, I had to:
  • Download and install libwebcam.
  • Execute uvcdynctrl -i logitech.xml (logitech.xml can be found in the source for libwebcam).
  • Install the linux-source-* package and extract the /usr/src/linux-source*.tar.bz2 to disk.
  • Copy uvcvideo.h in to the same directory as pantilt.c.
  • Execute python setup.py install
Finally, here's a sample usage of the pantilt module:
import pantilt, time

pantilt.reset() # Reset the pan and tilt to the origin.
pantilt.pantilt(10, 0) # Increase relative pan by 10 degrees.
pantilt.pantilt(0, 10) # Increase relative tilt by 10 degrees.

1 comment:

  1. This helped me out a lot, thanks! I did have to use the standalone version of uvcvideo.h as explained here: http://svn.quickcamteam.net/svn/qct/Linux/README

    I'm having trouble getting smooth motion. I'm not sure if that's a timing issue I need to work out or what...