For low-level programming we will use C/C++; however, for higher tasks (such as the dashboard), we will use Python.
Some C compiler such as gcc
or clang
. Your system probably already has one installed, but if not look up C compiler install instructions for your machine.
Python 3: https://www.python.org/downloads/
Install the cffi
package for python: python3 -m pip install cffi
We will use the foreign function interface (FFI) to call C functions in Python. Let's first create a C library.
Consider a header file such as:
void hello(void);
int adder(int x, int y);
And an implementation:
void hello(void) {
printf("Hello from C!\n");
}
int adder(int x, int y) {
return x + y;
}
Compile the demo shared library:
gcc -shared -o libdemo.so -fPIC src/demo.c
ctypes
moduleDocumentation: https://docs.python.org/3/library/ctypes.html
from ctypes import CDLL
lib = CDLL('./libdemo.so')
lib.hello()
print('result', lib.adder(2, 3))
Pros: included in the standard Python modules
Cons: requires casting Python to C types for many data types
cffi
moduleDocumentation: https://cffi.readthedocs.io/
from cffi import FFI
ffi = FFI()
ffi.cdef('''
void hello(void);
int adder(int x, int y);
''')
lib = ffi.dlopen('./libdemo.so')
lib.hello()
print('result', lib.adder(2, 3))
Pros: easier to deal with types
Cons: requires an external import
Documentation: https://docs.python.org/3/extending/extending.html
The source c file should look like this:
static PyObject *hello(PyObject *self, PyObject *args) {
printf("Hello from C!\n");
return Py_None;
}
static PyObject *adder(PyObject *self, PyObject *args) {
int x, y;
if (!PyArg_ParseTuple(args, "ii", &x, &y)) return NULL;
return PyLong_FromLong(x + y);
}
static PyMethodDef DemoMethod[] = {
{"hello", hello, METH_VARARGS, "hello"},
{"adder", adder, METH_VARARGS, "adder"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef demomod = {
PyModuleDef_HEAD_INIT,
"demo",
NULL,
-1,
DemoMethod
};
PyMODINIT_FUNC PyInit_demo(void) {
return PyModule_Create(&demomod);
}
To build this module create a setup.py
file:
from distutils.core import setup, Extension
module = Extension('demo', sources=['src/demomod.c'])
setup(name='Demo Module',
version='0.1',
description='Demo extension',
ext_modules=[module])
Compile the module with:
python3 setup.py build --build-lib=.
Now we can use the C extension in python:
import demo
demo.hello()
print('result', demo.adder(2, 3))
python3 ctypes_demo.py
python3 cffi_demo.py
python3 cext_demo.py
Each script should produce an output similar to:
Hello from C!
Python: 5
Run python3 speed_demo.py
to see the speed of each method.
Sample output:
Loading ctypes.CDLL took 0.012s
Loading cffi.FFI took 0.045s
Loading c extension took 0.004s
Running adder for 20,000,000 iterations...
ctypes took 3.146s
cffi took 2.720s
cext took 1.384s
You had to be there, but we demonstrated the previous robot dashboard after connecting to the raspberry pi. We were able to init the CAN bus and get the currents from each TalonSRX.
We will mostly be working on tasks in parallel. In order to do this effectively, we need to define the explicit API between each layer. Here is an example of what this API might look like between the C, Python, and Web layers. We need to make some design decisions in regard to what the outward facing API is for each layer before we start heavy implementation.
liblk: C library
hardware:
- Talon.cpp
- set_power(float) -> void
- get_current(void) -> float
- get_ticks(void) -> float
- Stepper.cpp
- step(void) -> void
- step_by(int) -> void
- get_steps() -> int
- IMU.cpp
- get_heading() -> float
- get_yaw() -> float
- get_pitch() -> float
robot:
- Base.cpp
- turn(float) -> void
- travel(float) -> void
- Intake.cpp
- Depo.cpp
- Robot.cpp
- init(void) -> void
- start_heartbeat() -> void
- stop_all() -> void
utils:
- Logger.cpp
lkpy: Python C extension module
- lkpymodule.cpp
- create_robot(void) -> Robot
- kill(void) -> boolean
- get_status(void) -> Status
web api:
POST:
- /gamepad {input_id: value}
- /kill
GET:
- /dashboard
- /sensor?id=
Besides thinking about some API design choices, here are some concepts you may want to look at in preparing for the implementation phase. Some of these resources will also help us make choices about the design.
Some design choices to be made here are:
Some design choices to be made here are:
Some design choices to be made here are: