Utilize Pybind11 to encapsulate the Python version of WiringPi!

The original WiringPi is a GPIO library for Raspberry Pi, developed in C language, warehouse address: https://github.com/WiringPi/WiringPi. The library allows users to programmatically access and control raspberry PI’s GPIO pins. With the rapid development of Python in embedded devices, its operation of the underlying pin has become more and more, so it is particularly important to encapsulate the API interface in WiringPi into the corresponding Python interface.

At present, there is the Python version of this library: https://github.com/WiringPi/WiringPi-Python, which uses this tool to automatically read header files to complete the packaging of relevant interfaces, but this method has the following problems from the perspective of use:

· Not strong enough flexibility. For example, some function return values are passed through the input pointer, and thiswig cannot be effectively identified.

· The annotation is not encapsulated and the API cannot be popped up during development. You can’t know what functions are in the so file, so you can only try to find the relevant usage.

· There is a redefinition problem. WiringPi Is in C, and some headers have redefinition problems.

The biggest advantage of Python is to make it less difficult for developers to use, so this warehouse does not show this feature well. Given that Pybind11 is a widely used packaging tool, the familiar pytorch packages its C + + interface as python. So I’ll borrow Pybind11 to provide a super good Python version of WiringPi!!!

??? Warehouse address: https://github.com/Li-Zhaoxi/Pybind11-WiringPi

The following is about how to install the compiled Python version of WiringPi, how to use it, and how to develop it during this time.

??? Special thanks to Shengge for his valuable advice on the use experience???

One Toolkit compilation

Before compilation, the following points are noted: · The warehouse address of the currently packaged WiringPi is https://gitee.com/study-dp/WiringPi, only for the Horizon development board. Other development boards, such as Raspberry Pi, which I do not have yet. I will gradually make up in the subsequent development. You can pay more attention to the warehouse home page and Release information. · The Python package relies on the C + + library and is installed into the system environment when compiled. In the later iteration, I plan to put all the compiled so files under the package path, and the compilation process is all automated. I say the idea of encapsulation, Python package is a secondary encapsulation based on the existing C + + dynamic library, so that in the C + + project and in the Python project, it is memory saving.

  • · If you want to compile this project from scratch, the compilation and installation process is as follows:

    sudo pip3 install mypy ninja

    git clone --recursive GitHub - Li-Zhaoxi/Pybind11-WiringPi: The python version of WiringPi, that is packaged by Pybind11
    cd Pybind11-WiringPi

    cd 3rdparty/WiringPi-RDK
    ./build
    cd ../..

    cd 3rdparty/pybind11
    mkdir build && cd build
    cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF ..
    sudo make install
    cd ../../..

    python3 setup.py bdist_wheel

    sudo pip3 install dist/WiringPi*.whl

· If you want to use my compiled whl file, you can download the compiled whl file WiringPi-0.1.0-cp38-cp38-linux_aarch64.whl from here, and then the compilation and installation process is as follows:

git clone --recursive https://github.com/Li-Zhaoxi/Pybind11-WiringPi
cd Pybind11-WiringPi

cd 3rdparty/WiringPi-RDK
./build
cd ../..

sudo pip3 install <path>/WiringPi-0.1.0-cp38-cp38-linux_aarch64.whl

Enter python3, and if you can import WiringPi correctly, you have installed the current project correctly and can develop happily.

Two Usage methods

First, put in the usage rendering. After importing WireingPi, you can directly see all the function interfaces and related comments in the package. I have migrated the annotations from C language for each function, and I have made fine-tuning of the function’s API from a usage perspective!!! (Not yet, thank you, Xiao Xixi → _ →)

I have added the hierarchical relationships and interface declarations of all functions/variables in the package in the README section of the project homepage. In this way, if we know the name of the function to be called, we can directly search for the function name in the project homepage to obtain the calling method. For example, the calling method of the softPwmCreate function is from WiringPi. softdrive import softPwmCreate.

Three Development process

Let me talk about my development details here. First, why did I come up with the idea of encapsulating WiringPi? Firstly, last year when Arui was using the PWM interface of X3, the official Python version of the control PWM had a jitter issue. The C++version of WiringPi met the requirements, but could not be called in Python. At that time, I quickly packaged the interface he wanted in Python and it has been verified. However, recently another developer is going to use this thing, but it can’t run on his board. I don’t have much time to teach him how to configure it. In order to reduce the time you spend on building wheels like this, I want to completely package the WiringPi library.

The project was created on January 10th and a version has been released for a month. While constructing this project, I have been thinking about what to consider when building a “user-friendly” package. Currently, the main points that come to my mind are as follows: ·Interface design. The habit of Python is to use return values to return data, while C language outputs data by passing pointers. I need to understand the usage of each function and develop relevant optimization methods. ·Simplified installation. Compile and install the project with minimal instructions. That is, use the pip install package and generate the package using Python setup.py. ·Complete annotations/assist in development. Make good use of the automatic pop-up function developed by VSCode to display the function interface and related comments, making the content transparent. ·Write useful development documentation. Reduce user learning costs from the perspective of blogs/project readme, etc. Except for the interface design part which is the development part of the work, all other work revolves around the ecosystem, which also shows how important the ecosystem is. Encapsulating WireingPi is a big project. Below is a list of the efforts I have made during development to improve the user experience ·Checked the interfaces of all functions and optimized the usage of some of them. such as

·A new definition has been made using lambda expressions for returning values by inputting parameter types as pointers. The version information returned by the void wiringPiVersion function (int * major, int * minor) is stored in major, minor, so that I can encapsulate it in the return value, which can be called in py through the method of major, minor=wiringPiVersion (). ·Optimized some functions that actually return an int but should actually be bool. In C language, using 1,0 to represent true and false to prevent confusion when using py, I changed the return type of the function that only returns 0,1 to bool from its implementation code. ·A function with an array as input parameters, adapted to the form of input np.ndarray. For example, the function void ds1302clockWrite (const int clockData [8]); You need to input an array containing 8 elements, with the corresponding Python declaration as def ds1302clockWrite (clockData: numpy. ndarray [numpy. int32]) ->None, and the code will automatically verify the number of elements. ·Classified and integrated according to the type of sensor. For example, GPIO expansion chips mcp23s08 and pcf8574, I package the relevant functions in the WiringPi. gpio module.

·Simplify the installation process, compiling this package only requires python3 setup.py bdist_wheel.

·The construction of auxiliary development has already been completed during the setup. py process. Encapsulating the so file generated by C++, vscode cannot pop up the functions inside, which means that the so file is opaque to users. So it is necessary to generate the corresponding. py declaration file based on the so file. ·I initially used pybind11 stub gen, but there were many issues and the C++17 feature support was average. After testing for a period of time, I gave up. ·Currently, mypy is used to export the declarations and annotations of most functions. But strangely, the doc for each module cannot be exported, so we can only add a post-processing function in setup.py to solve this problem. ·Compile the module using cmakelists.txt in setup.py. It is to organize the instructions associated with the normal compilation of the CMake project together to complete automatic compilation. Of course, due to the inclusion of post-processing modules, some key functions have been overloaded here.

·Organized comments for all functions. Many comments in WireingPi are written in. c files, and I have encapsulated these comments in pybind, which is really a huge amount???.

I often encounter problems with undefined symbols during development. Here are several situations that led to this problem.

·The header file forgot to extern “C”. For example, an error message stating that _Z10rht03Setupii is an undefined symbol,

·Firstly, use c++filt_Z10rht03Setupii to parse the function declaration: rht03Setup (int, int)

·Then nm g/usr/local/lib/libwiringPi.so lists whether this library contains rht03Setup

·I found that the library contains this function, so when I looked at the header file, I found that extern “C” was not written. This will result in C++projects being unable to reference related functions when linked to this library. Just add it.

·Makefile missed some. c files, such as softServo, which was not compiled. Similarly, I used the nm tool and found that this function was not present in the library, so I directly checked whether it was compiled. Fix this issue and you will be able to use it normally.

Four Summary

This month, after returning home from work, I started researching packaging related technologies and tools, almost every day from 11am to 2pm. In fact, if it’s just packaging, it’s not difficult, mainly because I need to classify many sensors and study their usage. And often in the early morning, I would discuss with Brother Sheng how to design the structure, gain user experience, and so on (really thank you). I sincerely hope that everyone will not be trapped in the use of interfaces in the future. There is still a lot of work to be done on the WiringPi project, and it needs to be further improved and optimized in future work. Please pay more attention to the warehouse homepage: Pybind11 WiringPi. The follow-up work mainly focuses on the following points: Add support for BPU inference Python custom layer. At present, the custom layer is only used for C++inference, which leads to a significant increase in deployment difficulty for some models, such as the HED edge detection algorithm. Increase the adaptation of Raspberry Pi pins. The WiringPi I am currently using is dedicated to the Horizon X3 development board. In order to avoid interference with Raspberry Pi user development, a global variable will be added to solve the problem. This part of the work is actually about solving device compatibility issues. We welcome Raspberry users to join us. Try to supplement the use demo of various module functions as much as possible. Reduce user learning/development costs.