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.
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
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)