Getting Started with Pymata4
Contents
Getting Started with Pymata4#
Pymata4 is a Python library that allows you to monitor and control Arduino hardware from a host computer. The library uses the Firmata protocol for communicating with the Arduino hardware. Pymata4 supports the StandardFirmata server included with the Arduino IDE, and also StandardFirmataWiFi, and an enhanced server FirmataExpress distributed with Pymata4.
Pymata4 uses concurrent Python threads to manage interaction with the Arduino. The concurrency model enables development of performant and interactive Arduino applications using Python on a host computer. Changes in the status of an Arduino pin can be processed with callbacks. It’s sibling, pymata-express, is available using the Python asyncio package.
Support for common \(I^2C\) devices, including stepper motors, is included in FirmataExpress. Applications using unsupported \(I^2C\) devices may require modifications to the Firmata server sketch.
Useful links:
Hardware Setup and Software Installations#
The Arduino must be attached to the host by USB with either the StandardFirmata or Firmata-express sketch installed using the Arduino IDE. For use with WiFi, install StandardFirmataWiFi.
The Python pymata4 package can be installed with pip.
!pip install pymata4
Requirement already satisfied: pymata4 in /Users/jeff/opt/anaconda3/lib/python3.8/site-packages (1.10)
Requirement already satisfied: pyserial in /Users/jeff/opt/anaconda3/lib/python3.8/site-packages (from pymata4) (3.5)
Basic Usage#
pymata4.Pymata()
board.shutdown()
from pymata4 import pymata4
# create a board instance
board = pymata4.Pymata4()
# remember to shutdown
board.shutdown()
pymata4: Version 1.10
Copyright (c) 2020 Alan Yorinks All Rights Reserved.
Opening all potential serial ports...
/dev/cu.usbmodem14301
Waiting 4 seconds(arduino_wait) for Arduino devices to reset...
Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301
Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino
Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins
Blinker#
board.digital_write(pin, value)
Pymata4 has two methods for writing a 1 or a 0 to a digital output. digital_write(pin, value)
hides details of the Firmata protocol from the user. The user can refer to digital pins just as they would in standard Arduino coding. A second method, digital_pin_write(pin, value)
allows writing to multiples at the same time, but requires the user to understand further details of the Firmata protocol.
from pymata4 import pymata4
import time
LED_PIN = 13
board = pymata4.Pymata4()
# set the pin mode
board.set_pin_mode_digital_output(LED_PIN)
for n in range(5):
print("LED ON")
board.digital_write(LED_PIN, 1)
time.sleep(1)
print("LED OFF")
board.digital_write(LED_PIN, 0)
time.sleep(1)
board.shutdown()
pymata4: Version 1.10
Copyright (c) 2020 Alan Yorinks All Rights Reserved.
Opening all potential serial ports...
/dev/cu.usbmodem14301
Waiting 4 seconds(arduino_wait) for Arduino devices to reset...
Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301
Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino
Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins
LED ON
LED OFF
LED ON
LED OFF
LED ON
LED OFF
LED ON
LED OFF
LED ON
LED OFF
Handling a Keyboard Interrupt#
Pymata4 sets up multiple concurrent processes upon opening connection to the Arduino hardware. If Python execution is interrupted, it isimportant to catch the interrupt and shutdown the board before exiting the code. Otherwise the Arduino may continue to stream data requiring the Arduino to be reset.
from pymata4 import pymata4
import time
def blink(board, pin, N=20):
board.set_pin_mode_digital_output(LED_PIN)
for n in range(N):
board.digital_write(LED_PIN, 1)
time.sleep(0.5)
board.digital_write(LED_PIN, 0)
time.sleep(0.5)
board.shutdown()
LED_PIN = 13
board = pymata4.Pymata4()
try:
blink(board, LED_PIN)
except KeyboardInterrupt:
print("Operation interrupted. Shutting down board.")
board.shutdown()
pymata4: Version 1.10
Copyright (c) 2020 Alan Yorinks All Rights Reserved.
Opening all potential serial ports...
/dev/cu.usbmodem14201
Waiting 4 seconds(arduino_wait) for Arduino devices to reset...
Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14201
Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino
Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins
Operation interrupted. Shutting down board.
Getting Information about the Arduino#
from pymata4 import pymata4
import time
board = pymata4.Pymata4()
print("Board Report")
print(f"Firmware version: {board.get_firmware_version()}")
print(f"Protocol version: {board.get_protocol_version()}")
print(f"Pymata version: {board.get_pymata_version()}")
def print_analog_map(board):
analog_map = board.get_analog_map()
for pin, apin in enumerate(analog_map):
if apin < 127:
print(f"Pin {pin:2d}: analog channel = {apin}")
def print_pin_state_report(board):
pin_modes = {
0x00: "INPUT",
0x01: "OUTPUT",
0x02: "ANALOG INPUT",
0x03: "PWM OUTPUT",
0x04: "SERVO OUTPUT",
0x06: "I2C",
0x08: "STEPPER",
0x0b: "PULLUP",
0x0c: "SONAR",
0x0d: "TONE",
}
analog_map = board.get_analog_map()
for pin in range(len(analog_map)):
state = board.get_pin_state(pin)
print(f"Pin {pin:2d}: {pin_modes[state[1]]:>15s} = {state[2]}")
print_pin_state_report(board)
board.digital_write(13, 1)
print_pin_state_report(board)
print_analog_map(board)
capability_report = board.get_capability_report()
board.shutdown()
# get capability report
print("\nCapability Report")
modes = {
0x00: "DIN", # digital input
0x01: "DO", # digital output
0x02: "AIN", # analog input
0x03: "PWM", # pwm output
0x04: "SRV", # servo output
0x05: "SFT", # shift
0x06: "I2C", # I2C
0x07: "WIR", # ONEWIRE
0x08: "STP", # STEPPER
0x09: "ENC", # ENCODER
0x0A: "SRL", # SERIAL
0x0B: "INP", # INPUT_PULLUP
}
pin_report = {}
pin = 0
k = 0
while k < len(capability_report):
pin_report[pin] = {}
while capability_report[k] < 127:
pin_report[pin][modes[capability_report[k]]] = capability_report[k+1]
k += 2
k += 1
pin += 1
mode_set = set([mode for pin in pin_report.keys() for mode in pin_report[pin].keys()])
print(" " + "".join([f" {mode:>3s} " for mode in sorted(mode_set)]))
for pin in pin_report.keys():
s = f"Pin {pin:2d}:"
for mode in sorted(mode_set):
s += f" {pin_report[pin][mode]:>3d} " if mode in pin_report[pin].keys() else " "*5
print(s)
pymata4: Version 1.10
Copyright (c) 2020 Alan Yorinks All Rights Reserved.
Opening all potential serial ports...
/dev/cu.usbmodem14301
Waiting 4 seconds(arduino_wait) for Arduino devices to reset...
Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301
Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino
Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins
Board Report
Firmware version: 2.5 StandardFirmata.ino
Protocol version: 2.5
Pymata version: 1.10
Pin 0: OUTPUT = 0
Pin 1: OUTPUT = 0
Pin 2: OUTPUT = 0
Pin 3: OUTPUT = 0
Pin 4: OUTPUT = 0
Pin 5: OUTPUT = 0
Pin 6: OUTPUT = 0
Pin 7: OUTPUT = 0
Pin 8: OUTPUT = 0
Pin 9: OUTPUT = 0
Pin 10: OUTPUT = 0
Pin 11: OUTPUT = 0
Pin 12: OUTPUT = 0
Pin 13: OUTPUT = 0
Pin 14: OUTPUT = 0
Pin 15: OUTPUT = 0
Pin 16: OUTPUT = 0
Pin 17: OUTPUT = 0
Pin 18: ANALOG INPUT = 0
Pin 19: ANALOG INPUT = 0
Pin 20: ANALOG INPUT = 0
Pin 21: ANALOG INPUT = 0
Pin 22: ANALOG INPUT = 0
Pin 23: ANALOG INPUT = 0
Pin 24: ANALOG INPUT = 0
Pin 25: ANALOG INPUT = 0
Pin 26: ANALOG INPUT = 0
Pin 27: ANALOG INPUT = 0
Pin 28: ANALOG INPUT = 0
Pin 29: ANALOG INPUT = 0
Pin 0: OUTPUT = 0
Pin 1: OUTPUT = 0
Pin 2: OUTPUT = 0
Pin 3: OUTPUT = 0
Pin 4: OUTPUT = 0
Pin 5: OUTPUT = 0
Pin 6: OUTPUT = 0
Pin 7: OUTPUT = 0
Pin 8: OUTPUT = 0
Pin 9: OUTPUT = 0
Pin 10: OUTPUT = 0
Pin 11: OUTPUT = 0
Pin 12: OUTPUT = 0
Pin 13: OUTPUT = 1
Pin 14: OUTPUT = 0
Pin 15: OUTPUT = 0
Pin 16: OUTPUT = 0
Pin 17: OUTPUT = 0
Pin 18: ANALOG INPUT = 0
Pin 19: ANALOG INPUT = 0
Pin 20: ANALOG INPUT = 0
Pin 21: ANALOG INPUT = 0
Pin 22: ANALOG INPUT = 0
Pin 23: ANALOG INPUT = 0
Pin 24: ANALOG INPUT = 0
Pin 25: ANALOG INPUT = 0
Pin 26: ANALOG INPUT = 0
Pin 27: ANALOG INPUT = 0
Pin 28: ANALOG INPUT = 0
Pin 29: ANALOG INPUT = 0
Pin 18: analog channel = 0
Pin 19: analog channel = 1
Pin 20: analog channel = 2
Pin 21: analog channel = 3
Pin 22: analog channel = 4
Pin 23: analog channel = 5
Pin 24: analog channel = 6
Pin 25: analog channel = 7
Pin 26: analog channel = 8
Pin 27: analog channel = 9
Pin 28: analog channel = 10
Pin 29: analog channel = 11
Capability Report
Pin :AINDINDOI2CINPPWMSRV
Pin 0: 1 1 1 14
Pin 1: 1 1 1 14
Pin 2: 1 1 1 1 14
Pin 3: 1 1 1 1 8 14
Pin 4: 1 1 1 14
Pin 5: 1 1 1 8 14
Pin 6: 1 1 1 8 14
Pin 7: 1 1 1 14
Pin 8: 1 1 1 14
Pin 9: 1 1 1 8 14
Pin 10: 1 1 1 8 14
Pin 11: 1 1 1 8 14
Pin 12: 1 1 1 14
Pin 13: 1 1 1 8 14
Pin 14: 1 1 1 14
Pin 15: 1 1 1 14
Pin 16: 1 1 1 14
Pin 17: 1 1 1 14
Pin 18: 10 1 1 1 14
Pin 19: 10 1 1 1 14
Pin 20: 10 1 1 1 14
Pin 21: 10 1 1 1 14
Pin 22: 10 1 1 1 14
Pin 23: 10 1 1 1 14
Pin 24: 10 1 1 1 14
Pin 25: 10 1 1 1 14
Pin 26: 10 1 1 1 14
Pin 27: 10 1 1 1 14
Pin 28: 10 1 1 1 14
Pin 29: 10 1 1 1 14
AIN DIN DO I2C INP PWM SRV
Pin 0: 1 1 1 14
Pin 1: 1 1 1 14
Pin 2: 1 1 1 1 14
Pin 3: 1 1 1 1 8 14
Pin 4: 1 1 1 14
Pin 5: 1 1 1 8 14
Pin 6: 1 1 1 8 14
Pin 7: 1 1 1 14
Pin 8: 1 1 1 14
Pin 9: 1 1 1 8 14
Pin 10: 1 1 1 8 14
Pin 11: 1 1 1 8 14
Pin 12: 1 1 1 14
Pin 13: 1 1 1 8 14
Pin 14: 1 1 1 14
Pin 15: 1 1 1 14
Pin 16: 1 1 1 14
Pin 17: 1 1 1 14
Pin 18: 10 1 1 1 14
Pin 19: 10 1 1 1 14
Pin 20: 10 1 1 1 14
Pin 21: 10 1 1 1 14
Pin 22: 10 1 1 1 14
Pin 23: 10 1 1 1 14
Pin 24: 10 1 1 1 14
Pin 25: 10 1 1 1 14
Pin 26: 10 1 1 1 14
Pin 27: 10 1 1 1 14
Pin 28: 10 1 1 1 14
Pin 29: 10 1 1 1 14
Temperature Control Lab Shield#
from pymata4 import pymata4
import time
class tclab():
def __init__(self):
self.board = pymata4.Pymata4()
self.LED_PIN = 9
self.Q1_PIN = 3
self.Q2_PIN = 5
self.T1_PIN = 0
self.T2_PIN = 2
self.board.set_pin_mode_pwm_output(self.LED_PIN)
self.board.set_pin_mode_pwm_output(self.Q1_PIN)
self.board.set_pin_mode_pwm_output(self.Q2_PIN)
self.board.set_pin_mode_analog_input(self.T1_PIN)
self.board.set_pin_mode_analog_input(self.T2_PIN)
self._Q1 = 0
self._Q2 = 0
time.sleep(0.1)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
return
def close(self):
self.Q1(0)
self.Q2(0)
self.board.shutdown()
def read_temperature(self, pin):
# firmata doesn't provide a means to use the 3.3 volt reference
adc, ts = self.board.analog_read(pin)
return round(adc*513/1024 - 50.0, 1)
def Q1(self, val):
val = int(255*max(0, min(100, val))/100)
self.board.pwm_write(self.Q1_PIN, val)
def Q2(self, val):
val = int(255*max(0, min(100, val))/100)
self.board.pwm_write(self.Q2_PIN, val)
def T1(self):
return self.read_temperature(self.T1_PIN)
def T2(self):
return self.read_temperature(self.T2_PIN)
def LED(self, val):
val = max(0, min(255, int(255*val/100)))
self.board.pwm_write(self.LED_PIN, val)
with tclab() as lab:
lab.Q1(100)
lab.Q2(100)
for n in range(30):
print(lab.T1(), lab.T2())
lab.LED(100)
time.sleep(0.5)
lab.LED(0)
time.sleep(0.5)
lab.Q1(0)
lab.Q2(0)
pymata4: Version 1.10
Copyright (c) 2020 Alan Yorinks All Rights Reserved.
Opening all potential serial ports...
/dev/cu.usbmodem14301
Waiting 4 seconds(arduino_wait) for Arduino devices to reset...
Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301
Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino
Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins
22.6 22.1
23.1 22.6
23.1 22.1
23.1 22.1
23.1 22.6
23.1 22.6
22.6 22.6
23.1 22.1
23.1 22.6
23.1 22.1
22.6 22.6
23.1 22.6
23.1 22.6
22.6 22.1
23.1 22.6
23.1 22.6
22.6 22.6
23.1 22.1
22.6 22.6
22.6 22.1
23.1 22.6
23.1 22.6
22.6 22.1
23.1 22.1
23.1 22.6
22.6 22.6
23.1 22.1
23.1 22.6
23.1 22.1
23.1 22.6