Python programming example: Control via CAN bus
The following example of a Python application is intended to demonstrate and support the theoretical advantages described so far in a practical application. The application is executed on the sysWORXX CTR-700 and uses, in addition to some standard libraries, the IO driver library for the sysWORXX CTR-700 and thus the existing hardware of the device. The program establishes a CAN connection and waits for incoming commands from a special "master" device.
The complete application is of course available for download, so that it can be used and reproduced in a comfortable and uncomplicated way. In the download archive, which is part of the application description, all steps are summarized again as a shell script, so that only this script has to be executed. The installation of the Python interpreter is not necessary here, it is pre-installed by default.
Prerequisite
For the application described here, a remote station on the CAN bus is required via which messages can be sent to the sysWORXX CTR-700. In this example, the sysWORXX USB-CANmodul1 is used for this purpose, together with the CANinterpreter from emotas.
It is possible to replace both components with other, comparable products, but the steps described below refer directly to the components mentioned above.
In addition to the hardware, corresponding software components are also required. As interpreter language Python, in contrast to C, or C++, does not need a compiler and thereby also no complicated Toolchain or IDE. For development on any platform, only the Python interpreter must be installed, which is already pre-installed on the sysWORXX CTR-700. For other platforms, the instructions for this can be found under the following link:www.python.org/downloads/.
In principle, any text editor is suitable for software development in Python. For this example Visual Studio Code is used, because here the configuration effort is minimal and many comfort functions (autocomplete, linting, format checker, etc.) can be integrated by extensions.
In addition, further software components are required on the sysWORXX CTR-700. These are not pre-installed on the device by default and must be installed later if required. These include "python3-pip", "python3-dev", "libffi-dev" and the Python library "cffi". To install these, the following series of commands must be executed:
apt install python3-pip, python3-dev, libffi-dev
pip3 install cffi
Note:It is advisable that these components (especially "libffi-dev" and "cffi") are installed on the development system. Otherwise, unpredictable and untraceable runtime errors may occur.
Using the library for the sysWORXX CTR-700
Using the class library is identical to using regular standard Python libraries. The file "ctr700drv.py" must be located in the execution and project directory of the application. The following figure on the right shows how the project structure should look like.
The "import" statement is used to include the library into the Python application:
from ctr700drv import Ctr700Drv
This imports the class "Ctr700Drv" from the driver library and can be used by the application. To use the driver, an instance of the driver class must be created and then initialized. Subsequently, any of the imported methods can be accessed. The following simple sequence describes such a simple application. After creating an instance and initializing it, the digital output 0 is set totrueand the driver is de-initialized.
ctr700 = Ctr700Drv()
ctr700.init()
ctr700.set_digi_out(0, True)
ctr700.shutdown()
Program flow
I Start the application
The program is implemented in the file "python_can_demo.py". This must be stored together with the driver library of the sysWORXX CTR-700 in the same directory. The driver library can be taken from the linked download directory or from the virtual machine offered by SYS TEC for the development with the sysWORXX CTR-700.
The application is called with the following command:
python3 python_can_demo.py can0
The transfer parameter "can0" represents the interface to be used.
II Structure and flow of the application
Before the "main()" function is executed, all necessary libraries are first imported and necessary constants are defined. Imported are beside the standard libraries "socket", "struct", "sys" and "subprocess" also the driver library "ctr700drv". In the case of the two last-mentioned, in the special in each case the class "call" and "Ctr700Drv" is imported. A short explanation of the respective libraries can be found in the following table. Further notes on usage can be found on the official Python documentation website:https://docs.python.org/3.5/.
The constants used make it easier to make subsequent changes to the program, for example to adjust identifications. Specifically, six variables are created:
CAN_FRAME_FMT = "=IB3x8s".
CAN_EFF_FLAG = 0x80000000
CAN_MASTER = CAN_EFF_FLAG | 0x01000100
CAN_CTR700 = CAN_EFF_FLAG | 0x01000101
CAN_CMD_BYTE_DO = 0xa9
CAN_CMD_BYTE_D1 = 0xa8
After the import and the constant definition the "main()" function is executed. In this function it is checked whether a transfer parameter was specified when the program was called and if not, the application is terminated with a note about the absence.
def main():
if len(sys.argv) != 2:
print('Usage: {} INTERFACE'.format(sys.argv[0]))
print('INTERFACE can be can0, can1, etc.')
sys.exit(0)
with App() as app:
app.run()
If the parameter is present, the class "App" is initialized and executed directly with the method "run()". This class contains all main functions of the program. At this point a special feature of Python comes directly into play: for classes there are special methods with special meaning, if they are used with the "with" call: Python's magic, or dunder methods short for:doubleunder(score). This call replaces in two lines the "try-except-finally" block known from other programming languages and also usable in Python.
For example, the "__init__" method starts whenever a new instance of a class is created, in this example before the "run()" method is executed.
The following table shows an overview of the Dunder methods used in the application:
The use of these methods is optional, but recommended, as it allows the actively used methods to be kept leaner and thus more readable (de/initializations are omitted, etc.).
The "__init__" method fulfills the purpose here that this creates a class instance for the driver of the sysWORXX CTR-700.
def __init__(self):
self.ctr700 = Ctr700Drv()
When entering the "with" call, the "__enter__" method is executed, which initializes the driver for the sysWORXX CTR-700, sets all digital outputs to 0, then initializes the CAN interface with 1000 kbit/s, configures the socket and binds to the interface (passed as parameter).
def __enter__(self):
"""
Initialize CTR-700 Driver, reset digital inputs, set CAN
interface and create CAN socket
"""
self.ctr700.init()
# Clear all 16 digital outputs
for element in range(16):
self.ctr700.set_digi_out(element, False)
set_can_interface_up()
# Create a raw socket and bind it to the CAN interface
self.socket_can = socket.socket(
socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
self.socket_can.bind((sys.argv[1],))
return self
After initializing all components, now the "run()" method is executed.
First it waits for the reception of a CAN message. This is also again an own method, which translates received messages and then outputs them in the terminal readable for humans. It is divided into the CAN-ID, the length of the messages in bytes and the actual data content.
As soon as a message arrives, it is checked whether it was sent by the previously defined "CAN_MASTER". If this is not the case, the message is discarded and a corresponding evaluation is output. In case the message comes from the defined "CAN_MASTER", the first data byte is checked for further processing.
The first byte (byte 0) contains the information, which further actions are to be executed by the application: Switching on or off a digital output or reading a digital input. Depending on the action, a callback function is selected in the form of the variable "command_handler" and then called in the
called in the "run()" method. If the byte does not correspond to either of the two possibilities, or the data length of the message does not correspond to the default (too long or too short), a corresponding callback function is also called here.
In the respective callback functions it is evaluated which content is present in the second and third data byte (byte 1 and 2) of the CAN message. These bytes determine which digital input is to be read or which digital output is to be written and then executes the corresponding action. If a digital input has been read out, a CAN message with the read-out status is then sent. An evaluation of the executed action is also output to the console or terminal.
Finally, in the last step the complete run of the application is completed. Within the "run()" method, the next CAN message is now waited for in order to react to it again as described.
Control of the sysWORXX CTR-700 via the USB-CANmodul
In the description of the program flow above, the structure of the CAN message has already been discussed several times. The following table shows how they have to be structured concretely for this application.
The CAN identifier is used to tell the application that the message is sent by the user device. However, its real purpose is to prioritize messages and to identify the content. The "extended frame format" is also used. It ensures that the usage space of the CAN-ID is increased (29 instead of 11 bits). In this case the use of 29 bit identifiers is only for illustration. Most CAN applications used in practice usually use 11 bit identifiers.
The data byte 0 specifies which action is to be executed: 0xA8 means read digital inputs, 0xA9 means change digital outputs; while the next data byte specifies the used input or output. It must not exceed the range from 0x00 to 0x0F. Otherwise the message is interpreted as faulty and discarded.
When using the digital outputs the third data byte is additionally evaluated - if it is equal to 0x00 the state is set to "False" (low level), if it is not equal to 0x00 it is set to "True" (high level).
The application itself sends CAN messages when the state of a digital input is read. These are structured as shown in the following table.
The structure of the message is similar as already described above:
- CAN-ID: Identification of the application
- Byte 0: 0xA8 - digital input was read
- Byte 1: 0x00 to 0x0F - read input
- Byte 2: 0x00 to 0x01 - state of the input
Additionally it has to be considered that all nodes at the CAN bus must use the same baud rate. Here in the example it is 1000 kbit/s.
Configuration of the USB-CANmodule and CANinterpreter
As already mentioned, a sysWORXX USB-CANmodul1 together with the CANinterpreter from emotas is used for this application. In order for the module to successfully communicate with the application on the sysWORXX CTR-700, it must first be configured. In the started CANinterpreter the correct setting is entered via "Connectionà CAN Interface Settings". The figure shows how these should look like.
Once the settings have been made and the module is connected to the PC, the connection can be established via the "Connect" icon at the top left. The following figure shows the main window of the CANinterpreter in the connected state and after sending all three messages.
The information output on the console of the sysWORXX CTR-700 can be seen in the following figure:
Final notes
This example clearly shows how easy it is to develop applications in Python for sysWORXX CTR-700. Every Python developer can develop his own applications on this basis, which interact directly with the interfaces of the device. Instead of this application, a much more complex and secure transmission protocol can be developed. It is also possible to include other hardware (analog inputs, relays, etc.) of the sysWORXX CTR-700, like the RUN switch, analog inputs, relays, etc.
For getting started and general use of the CTR-700, we offer a Linux VM for download on our product website. This includes a preconfigured Xubuntu installation. It also contains the class library, which is needed for software development with Python. There you can also find further information material, like the device manual, SD card image (operating system, etc.), but also further documentation for the sysWORXX CTR-700.