Building An Activity Monitor with Circuit Playground Express.

Introduction

Earlier this year, the Chicago Python Meetup Group held a workshop on CircuitPython. I entered an idea to build an activity monitor for the workshop and was selected to participate and received a Circuit Playground Express. idea for activity monitor

The Circuit Playground Express offers a number of built-in sensors, an accelerometer, LEDs, and buttons. You can do a lot with just the microcontroller itself and I found it much easier to work with than the Arduino. There are also various ways to code it (MakeCode, CircuitPython, Arduino, Code.org’s CS Discoveries), for this project I used CircuitPython. I learned a lot about electronics and coding for electronics and wanted to briefly show the activity monitor.

Demonstration

Bill of Materials

![activity monitor bill of materials](/images/posts/activity-monitor/bill of materials.jpg)

Schematics

schematic bill of materials

The Code

The code is posted on Github. https://github.com/smithsa/activity-monitor

import time
import adafruit_hcsr04
import board
from adafruit_circuitplayground.express import cpx

BLACK = (0, 0, 0)
ALERT_COLORS = [(180, 0, 0), (255, 0, 100), (180, 0, 255)]
ALERT_CYCLE_DELAY = .05
REPEAT_ALERT_AUDIO = False

sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.A2, echo_pin=board.A1)
alert_triggered = False
alert_muted_status = False

seconds_counter = 0
sensor_reads = 5 # will get 5 readings from sonar and take the average
sensor_read_interval = 30  # every 30 seconds I will get a reading on how far a user is away
sensor_read_count = 0
aggregated_sensor_data = []
should_read_sensor = False
seating_threshold = 50
movement_check_interval = 3600 # every hour check to alert user to moves
user_outside_seating_threshold_count = 0
min_amount_of_times_away = 4
times_to_play_alert = 10
seconds_break_after_alert = 120

def cycle_lights(color):
    for i in range(len(cpx.pixels)):
        cpx.pixels[i] = color
        time.sleep(ALERT_CYCLE_DELAY)

def dectect_distance(sonar):
    try:
        return sonar.distance
    except RuntimeError:
        print("Retrying!")

    return 0

def alert(is_muted = False):
    global alert_triggered
    if REPEAT_ALERT_AUDIO:
        cpx.play_file("alert.wav")
    elif is_muted == False and alert_triggered == False:
        cpx.play_file("alert.wav")

    for alertColor in ALERT_COLORS:
        cycle_lights(alertColor)
    cycle_lights(BLACK)

    alert_triggered = True

def reset_sensor_data():
    global sensor_read_count
    global should_read_sensor
    global aggregated_sensor_data
    sensor_read_count = 0
    should_read_sensor =  False
    aggregated_sensor_data = []

def reset_movement_check_data():
    global user_outside_seating_threshold_count
    global seconds_counter
    global alert_triggered
    user_outside_seating_threshold_count = 0
    seconds_counter = 0
    alert_triggered = False

while True:
    time.sleep(1)
    seconds_counter = seconds_counter + 1
    if seconds_counter % sensor_read_interval == 0:
        should_read_sensor = True

    if should_read_sensor and sensor_read_count <= sensor_reads:
        aggregated_sensor_data.append(dectect_distance(sonar))
        sensor_read_count += 1

    if sensor_read_count == sensor_reads:
        average_distance = sum(aggregated_sensor_data)/sensor_reads
        if average_distance > seating_threshold:
            user_outside_seating_threshold_count = user_outside_seating_threshold_count + 1
        reset_sensor_data()

    if seconds_counter == movement_check_interval:
        if user_outside_seating_threshold_count < min_amount_of_times_away:
            for _ in range(times_to_play_alert):
                alert(alert_muted_status)

        reset_movement_check_data()
        reset_sensor_data()
        time.sleep(seconds_break_after_alert)

    print("seconds_counter:", seconds_counter)
    print("should_read_sensor:",should_read_sensor)
    print("sensor_read_count:", sensor_read_count)
    print("aggregated_sensor_data:", aggregated_sensor_data)