Category Archives: arduino

Control an Arduino from Python with PyCmdMessenger

March 6, 2020

There are many methods that can be used to provide communication with a host computer and an Arduino device. ArduinoCmdMessenger is a stable and relatively easy to use choice. It enables simple messages to be sent to the Arduino to control actions and return data.

Command Messenger itself can be installed via the Arduino Library Manager. The latest is version 4.0. It comes with examples in both C# and Visual Basic. My requirement is Python, so the PyCmdMessenger will be used. PyCmdMessenger can be installed via pip3. 

Both the projects have not had many updates in the recent years, but I find in combination they address the needs of my projects.

The problem:

I want to use Raspberry PI for IoT work. I want to use an Arduino to run some sensors that the PI will poll and publish.

True, the PI can handle most sensors and do the work for me. But in this particular case I have one of Adafruit’s CCS811  air quality sensors. The only way to get the sensor to work directly on the Raspberry PI is to slow the i2c bus speed down as described here -> https://learn.adafruit.com/adafruit-ccs811-air-quality-sensor/raspberry-pi-wiring-test .

I don’t really want to slow the i2c bus down on the PI as I have it busy with some other sensors. Plus — the implementation I have in mind is the CCS811 and a BME280 comprise an add on module for other projects. Using an Arduino Leonardo clone, the DF Robot Beetle, I can treat the implementation as a plug and play add on for other projects such as our up coming Raspberry Alarm Clock.

There are other options like Firmata. But I was not looking to couple the Arduino code so closely. I could roll my own implementation, but I’d rather use something that is already available. I  just want to be able to reliably pass commands and data back and forth asyhronously.  PyCmdMessenger provides a solution for this.

An example project can be found in the m2ag.labs examples repo. The code is documented and can be modified to use alternate sensors.

Prototype Sensor module.
Prototype Beetle sensor module. There is plenty of unused capacity on the Beetle that could be put to use.

Links:

https://github.com/thijse/Arduino-CmdMessenger

https://github.com/harmsm/PyCmdMessenger

Using Ohmite Force sensitive potentiometers.

Force sensing potentiometers offer an alternative to mechanical potentiometers and rotary encoders. These devices do away the mechanical action of the switch and instead offer a touch sensitive alternative. After looking around the web I was not able to find a suitable library for implementing these devices so I ended up creating my own for both Arduino and Circuitpython.

The libraries implement functionality for Ohmite’s FSP series of devices. The devices include one round sensor (FPS03CE), and two linear devices (FSP01CE and FSP02CE). The linear devices come in two lengths.

All devices are single touch. This means that you can only detect one touch location on the device at a time. If we tried to detect a finger on each end of a linear device it would register as being in the middle.

The libraries have a similar api, each implement the functions necessary to get the force applied to the device as well as the position of the touch.

For all devices the force is reported as voltage. Generally 3.2 volts is the highest we will see on a 3.3 volt system. The position is reported as an integer. For the FPS03CE the integer represents 0 to 360 degrees from the tail (connector) of the device. For the linear devices it is 0 to 100 mm for the FSP01CE and 0 to 55 mm for the FSP02CE. Default for the two linear devices is for zero at the tail, but the call to position command can take a parameter to return with 0 at the head end of the device.

Integration Guide:

I was only able to find one source for the integration guide — this pdf at Mouser. It is pretty comprehensive, but contains a couple of errors that an integrator should be aware of.

First: In the section for FSP0(1/2)CE on page 4:

This section from the integration guide specifies V2 as an analog input. It does not need to be.

The document specifies that V2 should be an ADC pin. In measuring the force and position I have not read an Analog value from there. Not a show stopper, but there is no need to tie up an analog input unnecessarily. I changed this to a digital input and these sensors work correctly.

Second: In the section for FSP03CE on page 7:

This table for the FSP03CE pin out is not correct.

Using Figure 10: Pin 4 is actually the the wiper pin, while pin 1 is the third drive electrode. Plus the Pin name for pin 2 is incorrect. Luckily this diagram makes it clear where the connections go.

From the integration guide.

Hook up:

Refer to this diagram when hooking up one the FSP devices.

Use this schematic when wiring up one of the FSP devices. VRef will use a digital I/O

Each device requires one analog input and multiple digital I/O lines. I used 22K for the FSP03CE voltage divider and 18K for both the liner variants. All are 1/4 watt 5%. VRef will use a digital I/O line. These resistor values worked for my application. The sensitivity of response to touch is effected by these values. This is detailed in the integration guide.

The libraries for Arduino and CircuitPython are on on git hub. I tried to make them work similarly but there are a couple differences.

Arduino:
#include "M2aglabs_Ohmite.h"

//Set this to the one the Arduino uses
#define ANALOG_RESOLUTION 12
#define LINEAR_THRESHOLD  0.3
#define ROUND_THRESHOLD  0.3


/*
	Round sensor
	WIPER, VREF, D0, D120, D240
	Linear Sensor
	WIPER, VREF, V1, V2, true/false (Short or Long)
	 
    WIPER and VREF are on two sides of a resistor. VREF floats for position measurements,
	PULLS low for force. 
*/

M2aglabs_Ohmite roundSensor(A5, 0, 2, 1, 3);
M2aglabs_Ohmite lLinear(A2, 7, 6, 10, true);  //true means short
M2aglabs_Ohmite sLinear(A4, 5, 4, 9, false);  //false is long sensor 



void setup() {

	Serial.begin(115200);
	/*
		The lib is set for a default of 10 for analog resolution and 3.3. for voltage. If the voltage is 5.0,
		set it here. 
	*/
	analogReadResolution(ANALOG_RESOLUTION);
	roundSensor.begin(ANALOG_RESOLUTION);
	sLinear.begin(ANALOG_RESOLUTION);
	lLinear.begin(ANALOG_RESOLUTION); 

	//Set options --
	/*
	Serial.println(roundSensor.readRange());
	Serial.println(roundSensor.readRange(1500)); 
	Serial.println(roundSensor.zeroOffset());
	Serial.println(roundSensor.zeroOffset(500));
	*/

}

void loop() {
	roundSensorActions();
	linearSensorActions();
}

void linearSensorActions() {

	int spos, lpos; //Position is an integer 
	float fsp, flp; //Force is a float

	fsp = sLinear.getForce(); 
	
	if (fsp > LINEAR_THRESHOLD) { 
		//False reads from tail to tip. 
		spos = sLinear.getPosition(false);
		Serial.print("s: ");
		Serial.print(fsp);
		Serial.print(" : ");
		Serial.println(spos);

	}

	flp = lLinear.getForce(); 
	if (flp > LINEAR_THRESHOLD) {
		lpos = lLinear.getPosition(false);	
		Serial.print("l: ");
		Serial.print(flp);
		Serial.print(" : ");
		Serial.println(lpos);
	}
}



void roundSensorActions() {

	//Get the force from the round sensor 
	float force = roundSensor.getForce();
	//IF it looks like we are touching it, calculate the position. 
	if (force > ROUND_THRESHOLD) {
		
		Serial.print("force: ");
		Serial.print(force);

		int angle = roundSensor.getPosition(); 
		Serial.print(" raw angle: ");
		Serial.print(angle);

		angle = constrain(angle, 0, 360);

		Serial.print(" adjusted: ");
		Serial.println(angle);
	}
	return;
} 

The function calls are documented in the header for the library.

CircuitPython
import board
from digitalio import DigitalInOut, Direction, Pull
from analogio import AnalogIn
import time
from m2aglabs_fsp import Ohmite

# Round sensor (FSP03CE) -- it needs a lot of inputs
wiper = board.A5
v_ref = board.D0
D_0 = board.D2
D_120 = board.D1
D_240 = board.D3

# Long linear sensor (FSP01CE)
l_wiper = board.A4
l_ref = board.D5
l_v1 = board.D4
l_v2 = board.D9

# Long linear sensor (FSP02CE)
s_wiper = board.A2
s_ref = board.D7
s_v1 = board.D6
s_v2 = board.D10

s_lin = Ohmite(s_wiper, s_ref, s_v1, s_v2, type=2) # FSP02
l_lin = Ohmite(l_wiper, l_ref, l_v1, l_v2, type=1) # FSP01
s_rnd = Ohmite(wiper, v_ref, D_0, D_120, D_240) #FSP03 can add type=0, but default is 0

######################### MAIN LOOP ##############################

s_rnd.begin()
l_lin.begin()
s_lin.begin()

while True:

    s_force = s_lin.get_force()
    force = s_rnd.get_force()
    l_force = l_lin.get_force()

    if s_force > 0.4:
        position = s_lin.get_position(False)
        print(s_force, position)
      
    # for long linear
    if l_force > 0.4:
        position = l_lin.get_position()
        print(l_force, position)
       
    # for round sensor
    if force > 0.09:
        angle = s_rnd.get_position()
        print(force, angle)

The circuit python code is a lot simpler. The differences from Arduino are:

  • the ‘type=’ key word argument sets the type of sensor. 0, or round, is the default
  • the analog resolution for CircuitPython is always 65536 there is no need to set it
  • I didn’t add setters for zero offset and read range. These can be adjusted by editing the library for now. This will be added shortly.
Using the library:

Usage is fairly straightforward. The general steps are:

  • Instantiate the object
  • Call begin
  • Poll for force
  • If there is force applied read the position

These libraries have only been tested on a Metro M4 and Itsybitsy M4 to date. There is nothing that is SAMD51 specific so the Arduino library should work on other devices. I’ll be using some of these sensors on a pro-micro soon, we’ll see how it goes.

Two settings to be aware of is the _ZERO_OFFSET and _READ_RANGE. These effect each sensors overall range. The _ZERO_OFFSET specifies the normal zero reading of the ADC. With a finger at the 0 position of the sensor there is still a voltage present. Depending on the sensor, the voltage will be in the 200 to 800 millivolt range. If the sensor will not go to zero try adjusting this. For round sensors the 0’s are at 0 degrees (at the tail) then clockwise to 120 degrees, then 240. This is detailed in the integration guide.

Read range sets the maximum value of the voltage at the max end of the sensor. So if the lengths come out short, or max is hit before the end reached try adjusting this setting.

Both libraries are available on git hub.

https://github.com/m2ag-labs/m2aglabs_ohmite

https://github.com/m2ag-labs/m2aglabs_ohmite_python