Skip to content
Snippets Groups Projects

Arduino - Python communication wrapper

Description

There are many times you want to connect between Arduino(s) and some Python script. This can be for exmple data collection reasons or coordinating between multiple devices (Robot arms (e.g.: UR5 arms), digital servos (e.g.: Dynamixels), webcams, etc ).

However, for many, using a middleware communication system such as ROS can be a huge learning curve and an unnecessary overhead.

The point for this wrapper is to provide a simple solution to estabilish a reliable two-way communication channel between mulitple arduinos and a central python script.

The aim is to be able to simple messages back and forth without going through the often troublesome process of synchronising messages, opening serial communication channels, etc. The general functionality is limited to keep everything straightforward and simple.

The project is constantly developing so please report bugs or any problems.


Core functionality

  • Estabilish a stable communication channel between multiple Arduinos and a single Python script
  • Send multiple strings form the Python side to the Arduino side (e.g.: can be used to tell the arduino "stop the motor" while sending the motor demand position)
  • Send mulitple messages with specified messages names from the Arduino side to the Python side (e.g.: can be used to transfer multiple sensor values, status messages, timestamps, etc)
  • Simplified keyboard keypress reading functionality to test your mechatronic setup

Points to bare in mind

  • On both sides, the messages reflect the most recent message. For now, there is no buffer or queue of messages (in order to simplify and increase the robustness the communication channel from edge cases).

Known issues

  • Certain combinations of microcontrollers and OS does not work well (see table below)

How to use this tool

Download the release

Download the latest release from here. You can see rough changes from the previous versions in the CHANGELOG.

In the release you will find three files: comms_wrapper.py, pyCommsLib.cpp, and pyCommsLib.h. These three files will be used as "Libraries" to provide the communication wrapper functionality.

Initial setup (Python side)

Copy and paste the comms_wrapper.py into the same directory as your Python script. In your Python script, import the contents of comms_wrapper.py by writing from comms_wrapper import *.

Initial setup (Arduino side)

Copy and paste the pyCommsLib.cpp and pyCommsLib.h into the same directory as your Arduino .ino file. If you do this correctly, you should see the two files as two tabs on the Arduino IDE.

How to use the wrapper

To help you get started with this wrapper, look through the commented example code Exmples/ folder (the implementation for the Python and Arduino side is shown the corresponding Exmples/Python/ and Exmples/Arduino/ folders).

You can also find detailed description of the different functions and variables used in the implementation to understand their functions. You can find this at the bottom of this README file.


Python libraries you need to install

pyserial

pynput


Testing and compatability

Arduino Uno Arduino Nano Arduino Nano Every Orange Pip Teensy 4.0
Windows 10 ✔️ ✔️ ✔️
Ubuntu 20.04 ✔️ ✔️ ✔️ ✔️

Testing has been perfromed on an XPS 15 9000 model.


Function and variable definitions

Here, all of the public functions and variables are defined and explained for usage.

Python side

  1. Defining the arduino object:
    Call Arduino(descriptiveDeviceName, portName, baudrate) to create your arduino class instance. For every arduino you connect, you need to create a new class instance. Example: myArduino = Arduino("Arduino Uno", "COM4", 115200)

    • Parameters:
      • descriptiveDeviceName: Name given to your arduino controller
      • portName: Port your arduino is connected
      • baudrate: Baudrate set on the Arduino side
  2. Connecting to the Arduino:
    Call the connect_and_handshake() function to connect to your arduino. The connection is two stages - first it connects via the serial port, and then it performs a handshake to be certain the communication is successful

    • Return:
      • Boolean whether the connection + handshake was successful
  3. Receiveing messages:
    Call the receive_message() function obtain new messages from the Arduino

    • Parameters:
      • printOutput: Set to True to printout the received message. Default is False
      • verbose: Set to True to printout verbose information of the received message. This assumes printOutput is True. Default is False
    • Return:
      • Boolean whether a valid message was received
  4. Sending messages: Call the send_message() function to send messages to the arduino. You can send a single message or multiple messages. Example: myArduino.send_message("stop motor"), myArduino.send_message([1,2,"stop motor"])

    • Parameters:
      • msg:
        • Option 1, single message: You can include any type of object as argument, but it will be transformed into a string before sending.
        • Option 2, multiple messages: You can specify a list of any objects as argument. Each element in the list will be converted into a string before sending.
  5. Debugging:
    Call the debug() function to debug the communication pipeline. It will show you the messages received from the arduino side, and what message is stored on the arduino (i.e.: tells you if the message you sent from the python side has indeed been receieved by the arduino side).

    • Parameters:
      • verbose: Set to True to printout verbose information of the received message. Default is False

Arduino side

  1. Initializing the communication:
    Call init_python_communication() within void setup() to begin the communication with the python side. The arduino code will hang on this command until the python side is activated.

  2. latest_received_msg():
    Call latest_received_msg() to fetch the most recent message from the python side. Note: This does not actually read the message from the python side, but rather is an interface the store message on the arduino to be accessed. To actually receive messages, you must call sync() (see below).

    • Parameters:
      • int index: If specified, it will fetch the message sent on that particular index from the python side. If unspecified, it will fetch the full message.
    • Return:
      • String of the received message
  3. Prepare to send a message to python side:
    Call load_msg_to_python() to load the message which will be sent to the python side. You must specify the name of each message and the payload itself. A good way of doing this is shown in the example code. Example: load_msg_to_python(msgName, dataCarrier, size_of_array(msgName));

    • Parameters:
      • String* msgName: Array of String variables. Here you should specify the name associated with each message
      • String* msgName: Array of String variables. Here you should specify the message content
      • int numOfMsg: The number of messages you are sending. This is done automatically by calling size_of_array(msgName)
  4. Send and receive messages:
    Call sync() to send and receive messages over the serial bus. This is where the actualy communiocation takes place and must be called.