# SPDX-FileCopyrightText: 2023 Trevor Beaton for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import ssl
import math
import board
import microcontroller
import wifi
import socketpool
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
from adafruit_adxl34x import ADXL345
from adafruit_lc709203f import LC709203F, PackSize

aio_username = os.getenv('aio_username')
aio_key = os.getenv('aio_key')

# Wi-Fi
try:
    print("Connecting to %s" % os.getenv('CIRCUITPY_WIFI_SSID'))
    wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
    print("Connected to %s!" % os.getenv('CIRCUITPY_WIFI_SSID'))
# Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad.
except Exception as e:  # pylint: disable=broad-except
    print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username= aio_username,
    password= aio_key,
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)

try:
    if not io.is_connected:
    # Connect the client to the MQTT broker.
        print("Connecting to Adafruit IO...")
        io.connect()
        print("Connected to Adafruit IO!")
except Exception as e:  # pylint: disable=broad-except
    print("Failed to get or send data, or connect. Error:", e,
    "\nBoard will hard reset in 30 seconds./n")
    time.sleep(30)
    microcontroller.reset()

threshold = 20 # set threshold value here
time_interval = 0.5 # set the time interval in seconds

# create the I2C bus object
i2c = board.STEMMA_I2C()

# For ADXL345
accelerometer = ADXL345(i2c)

# To monitor the battery
battery_monitor = LC709203F(i2c)
battery_monitor.pack_size = PackSize.MAH400

t0 = time.monotonic()

while True:
    x, y, z = accelerometer.acceleration
    t1 = time.monotonic()
    dt = t1 - t0

    total_acceleration = math.sqrt(x**2 + y**2 + z**2)
    if total_acceleration >= threshold:
        print("Battery Percent: {:.2f} %".format(battery_monitor.cell_percent))
        print("Collision strength: %.2f" % total_acceleration)
        io.publish("punch-strength", total_acceleration)

        # add code here to trigger an event or alert the user
    t0 = t1
    time.sleep(time_interval)

This code is doing several things:

  • Connecting to the internet
  • Connecting to Adafruit IO
  • Gathering accelerometer data and battery percentage data
  • Displaying accelerometer and battery data on the TFT
  • Sending accelerometer data to a feed in Adafruit IO

Acquiring WiFi credentials

These few lines of code extract the values for the Adafruit IO username and Adafruit IO API key from the settings.toml file mentioned here.

The values for aio_username and aio_key are in the settings.toml file.

aio_username = os.getenv('aio_username')
aio_key = os.getenv('aio_key')
Have you set up your Adafruit IO account yet? If not, you can check out the previous "Getting Started with Adafruit IO" page to get started!

Connecting to WiFi

This code block connects to a WiFi network using the wifi.radio.connect function and passes in the network’s SSID and password as arguments. The values of the SSID and password are read from environment variables CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD.

# Wi-Fi
try:
    print("Connecting to %s" % os.getenv('CIRCUITPY_WIFI_SSID'))
    wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
    print("Connected to %s!" % os.getenv('CIRCUITPY_WIFI_SSID'))
# Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad.
except Exception as e:  # pylint: disable=broad-except
    print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

Creating a Socket Pool

Next, the code initializes a socket pool and an MQTT client for communicating with the Adafruit IO cloud service.

The socketpool.SocketPool function creates a pool of sockets for management of network connections. It takes the wifi.radio object as an argument to allow for network communication over the WiFi connection.

pool = socketpool.SocketPool(wifi.radio)

Initializing a MQTT Client

The MQTT.MQTT function is then used to initialize a new MQTT client object, which will be used to send and receive messages with the Adafruit IO cloud service. This function takes several arguments:

  • broker: the hostname of the MQTT broker (Adafruit IO in this case).
  • username and password: the credentials for authenticating with the Adafruit IO service are stored in the settings.toml file.
  • socket_pool: the socket pool created in the previous step.
  • ssl_context: an SSL context created using the create_default_context method from the ssl module. This provides secure communication between the device and the Adafruit IO service.

Finally, the IO_MQTT function is used to initialize an "Adafruit IO MQTT helper", a convenient wrapper around the MQTT client that provides a simple API for working with Adafruit IO.

mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username= aio_username,
    password= aio_key,
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)

Connecting to Adafruit IO

Now to check the connection status of Adafruit IO, and if it is not connected, it is trying to connect to the MQTT broker using the io.connect() method.

If the connection to Adafruit IO fails, an error message is printed, and the board will hard reset after 30 seconds.

The except block is broad to catch all exceptions and the broad-except.

try:
    # If Adafruit IO is not connected...
    if not io.is_connected:
    # Connect the client to the MQTT broker.
        print("Connecting to Adafruit IO...")
        io.connect()
        print("Connected to Adafruit IO!")
except Exception as e:  # pylint: disable=broad-except
    print("Failed to get or send data, or connect. Error:", e,
    "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

Adding a threshold

These lines of code set two variables, threshold and time_interval.

threshold is set to 20; this value filters out movement that might trigger punch detection.

time_interval is set to 0.5, which is the time interval in seconds; I added this to limit the amount of data points created. You can change this to cater to your Adafruit IO plan.

threshold = 20 # set threshold value here
time_interval = 0.5 # set the time interval in seconds

Accessing the Accelerometer 

Now to create an I2C bus object using the board.STEMMA_I2C() method, and then create an instance of an ADXL345 accelerometer using that I2C bus object.

The ADXL345 class is imported from an external library and represents an ADXL345 3-axis accelerometer, which can measure acceleration along the X, Y, and Z axes.

The accelerometer is being initialized using the I2C bus object so that the accelerometer can communicate with the microcontroller board over the I2C bus.

# create the I2C bus object
i2c = board.STEMMA_I2C()
# For ADXL345
accelerometer = ADXL345(i2c)

Accessing the built-in battery monitor 

Next to create an instance of the LC709203F class named battery_monitor and set the pack_size attribute of the battery_monitor object to PackSize.MAH400.

The pack_size attribute is used to set the capacity of the battery being monitored by the LC709203F. In this case, PackSize.MAH400 is used to specify a battery capacity of 400 milliampere-hours (mAh).

battery_monitor = LC709203F(i2c)
battery_monitor.pack_size = PackSize.MAH400

Setting the Current Time

This line t0 = time.monotonic() sets the variable t0 to the current time as returned by the monotonic method of the time module. The monotonic method returns the number of seconds that have passed since an unspecified time in the past, guaranteed never to go back even if the system time is changed.

t0 = time.monotonic()

Storing Acceleration Data

These few lines use a while loop to read the x, y, and z components of the accelerometer’s acceleration and then store the variables.

while True:
    x, y, z = accelerometer.acceleration
    t1 = time.monotonic()
    dt = t1 - t0

    total_acceleration = math.sqrt(x**2 + y**2 + z**2)

The sleep interval between two acceleration readings is determined by the time_interval variable. The code runs in a continuous loop until manually stopped.

if total_acceleration >= threshold:
        print("Battery Percent: {:.2f} %".format(battery_monitor.cell_percent))
        print("Collision strength: %.2f" % total_acceleration)
        io.publish("punch-strength", total_acceleration)

        # add code here to trigger an event or alert the user
    t0 = t1
    time.sleep(time_interval)

Displaying Collision Strength Data in Adafruit IO

In this if-statement, if the total acceleration is greater than or equal to the threshold, the battery percent and total acceleration will be displayed on the TFT, and the total acceleration value will be published to a feed named "punch-strength" in Adafruit IO.

Great! Let's attach components to the glove!

This guide was first published on Feb 14, 2023. It was last updated on Feb 14, 2023.

This page (PyLeap Boxing Glove Tracker Code) was last updated on Mar 28, 2023.

Text editor powered by tinymce.