Skip to content

Commit

Permalink
Add in some halloween node code and memory analysis. (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbloy committed Aug 7, 2024
1 parent c6788cc commit fe69134
Show file tree
Hide file tree
Showing 25 changed files with 493 additions and 112 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ the end of this readme for more information about the license.
* [ ] UART
* [ ] Wi-Fi
* [ ] Messaging/discovery/coordination
* [ ] Make a pico-interactive release (see [Creating and sharing a CircuitPython library](https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library?view=all#mpy-2982472-11)):
* [ ] Compile to`.mpy` files

## Setting up the development environment

Expand Down
Binary file not shown.
Binary file not shown.
Binary file modified halloween/2024/3d_models/ultrasonic/Small Ultrasonic Box.stl
Binary file not shown.
Binary file modified halloween/2024/3d_models/ultrasonic/Ultrasonic Box Plugs.stl
Binary file not shown.
Binary file modified halloween/2024/3d_models/ultrasonic/Ultrasonic Box.stl
Binary file not shown.
8 changes: 3 additions & 5 deletions halloween/2024/path/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import board

from interactive.animation import Flicker
from interactive.interactive import Interactive
from interactive.log import info
from interactive.polyfills.animation import ORANGE, BLACK
from interactive.polyfills.pixel import new_pixels
Expand Down Expand Up @@ -39,6 +40,7 @@ async def stop_display() -> None:


async def start_display() -> None:
interactive.audio_controller.queue("lion.mp3")
for pixel in pixels:
pixel.brightness = SKULL_BRIGHTNESS

Expand Down Expand Up @@ -66,11 +68,7 @@ async def test_task_2() -> None:
info("End test task 2")


config = get_node_config()
config.trigger_start = start_display
config.trigger_run = run_display
config.trigger_stop = stop_display

config = get_node_config(button=False, buzzer=False, ultrasonic=False)
interactive = Interactive(config)


Expand Down
30 changes: 30 additions & 0 deletions halloween/2024/sensor/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Perform import of configuration here to allow for overrides from the config file.
from interactive.configuration import *
from interactive.interactive import Interactive


async def stop_display() -> None:
pass


async def start_display() -> None:
pass


async def run_display() -> None:
pass


config = get_node_config(button=False, buzzer=False)
config.trigger_start = start_display
config.trigger_run = run_display
config.trigger_stop = stop_display

interactive = Interactive(config)


async def callback() -> None:
pass


interactive.run(callback)
45 changes: 0 additions & 45 deletions halloween/2024/spiders/code.py
Original file line number Diff line number Diff line change
@@ -1,45 +0,0 @@
# Entry point for a spiders node.

from interactive.environment import are_pins_available
from interactive.interactive import Interactive
from interactive.log import set_log_level, info, INFO

BUTTON_PIN = None
BUZZER_PIN = None
BUZZER_VOLUME = 0.1

# Default settings
if are_pins_available():
# noinspection PyPackageRequirements
import board

BUTTON_PIN = board.GP27
BUZZER_PIN = board.GP2

if __name__ == '__main__':

set_log_level(INFO)

# Try loading local device settings as overrides.
try:
# noinspection PyPackageRequirements
from config import *

info("Config file loaded")

except ImportError:
info("No config file was found")

config = Interactive.Config()
config.buzzer_pin = BUZZER_PIN
config.button_pin = BUTTON_PIN
config.buzzer_volume = BUZZER_VOLUME

interactive = Interactive(config)


async def callback() -> None:
pass


interactive.run(callback)
105 changes: 93 additions & 12 deletions interactive/configuration.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# This file is used to setup the default configuration for a typical node.
# Using it removes some boilerplate from the node code. It also sets up
# the logging level.
# the logging level. The values in here are expected can be overridden
# through settings in a config.py file.
from interactive.environment import are_pins_available
from interactive.interactive import Interactive
from interactive.log import set_log_level, INFO
from interactive.log import set_log_level, INFO, log
from interactive.memory import report_memory_usage

REPORT_RAM = False
REPORT_RAM_PERIOD = 5 # This is the period in seconds between each report.

GARBAGE_COLLECT = False
GARBAGE_COLLECT_PERIOD = 10 # This is the period in seconds between each forced execution of the garbage collector.

LOG_LEVEL = INFO

Expand All @@ -12,6 +19,8 @@
BUZZER_PIN = None
BUZZER_VOLUME = 0.1

AUDIO_PIN = None

ULTRASONIC_TRIGGER_PIN = None
ULTRASONIC_ECHO_PIN = None

Expand All @@ -25,6 +34,7 @@
# Default settings
BUTTON_PIN = board.GP27
BUZZER_PIN = board.GP2
AUDIO_PIN = board.GP26
ULTRASONIC_TRIGGER_PIN = board.GP7
ULTRASONIC_ECHO_PIN = board.GP6

Expand All @@ -41,14 +51,85 @@
set_log_level(LOG_LEVEL)


def get_node_config() -> Interactive.Config:
config = Interactive.Config()
config.buzzer_pin = BUZZER_PIN
config.button_pin = BUTTON_PIN
config.buzzer_volume = BUZZER_VOLUME
config.ultrasonic_trigger_pin = ULTRASONIC_TRIGGER_PIN
config.ultrasonic_echo_pin = ULTRASONIC_ECHO_PIN
config.trigger_distance = TRIGGER_DISTANCE
config.trigger_duration = TRIGGER_DURATION
class Config:
"""
Holds the configuration settings required for constructing an instance of
Interactive.
"""

def __init__(self):
self.report_ram = False
self.report_ram_period = 9999
self.garbage_collect = False
self.garbage_collect_period = 9999
self.button_pin = None
self.buzzer_pin = None
self.buzzer_volume = 1.0
self.audio_pin = None
self.ultrasonic_trigger_pin = None
self.ultrasonic_echo_pin = None
self.trigger_distance = 9999
self.trigger_duration = 0
self.trigger_start = None
self.trigger_run = None
self.trigger_stop = None

def __str__(self):
return f"""
Ram:
Report ............ : {self.report_ram}
Period ............ : {self.report_ram_period} seconds
Garbage Collection:
Force ............. : {self.garbage_collect}
Period ............ : {self.garbage_collect_period} seconds
Button:
Pin ............... : {self.button_pin}
Buzzer:
Pin ............... : {self.buzzer_pin}
Volume ............ : {self.buzzer_volume}
Audio:
Pin ............... : {self.audio_pin}
Ultrasonic Sensor:
Trigger ........... : {self.ultrasonic_trigger_pin}
Echo .............. : {self.ultrasonic_echo_pin}
Trigger:
Distance .......... : {self.trigger_distance} cm
Duration .......... : {self.trigger_duration} seconds
"""

def log(self, level):
for s in self.__str__().split('\n'):
log(level, s)


def get_node_config(button=True, buzzer=True, audio=True, ultrasonic=True) -> Config:
if REPORT_RAM:
report_memory_usage("get_node_config")

config = Config()

if REPORT_RAM:
config.report_ram = True
config.report_ram_period = REPORT_RAM_PERIOD

if GARBAGE_COLLECT:
config.garbage_collect = True
config.garbage_collect_period = GARBAGE_COLLECT_PERIOD

if button:
config.button_pin = BUTTON_PIN

if buzzer:
config.buzzer_pin = BUZZER_PIN
config.buzzer_volume = BUZZER_VOLUME

if audio:
config.audio_pin = AUDIO_PIN

if ultrasonic:
config.ultrasonic_trigger_pin = ULTRASONIC_TRIGGER_PIN
config.ultrasonic_echo_pin = ULTRASONIC_ECHO_PIN
config.trigger_distance = TRIGGER_DISTANCE
config.trigger_duration = TRIGGER_DURATION

return config
3 changes: 2 additions & 1 deletion interactive/control.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# This file contains common control values.
# This file contains common control values that are "hard-coded" and not expected
# to be changed by configuration.

NS_PER_SECOND = 1_000_000_000

Expand Down
85 changes: 41 additions & 44 deletions interactive/interactive.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import gc

from interactive.audio import AudioController
from interactive.button import ButtonController
from interactive.buzzer import BuzzerController
from interactive.configuration import Config
from interactive.environment import is_running_on_desktop
from interactive.log import info, INFO, debug, log
from interactive.log import critical, info, debug, CRITICAL
from interactive.memory import report_memory_usage, report_memory_usage_and_free
from interactive.polyfills.audio import new_mp3_player
from interactive.polyfills.button import new_button
from interactive.polyfills.buzzer import new_buzzer
from interactive.polyfills.ultrasonic import new_ultrasonic
from interactive.runner import Runner
from interactive.scheduler import new_triggered_task, Triggerable
from interactive.scheduler import new_triggered_task, Triggerable, TriggerableAlwaysOn, terminate_on_cancel
from interactive.ultrasonic import UltrasonicController

if is_running_on_desktop():
Expand All @@ -24,47 +28,11 @@ class Interactive:
valid properties are provided. This allows a large range of boards to be supported.
"""

class Config:
"""
Holds the configuration settings required for constructing an instance of
Interactive.
"""
def __init__(self, config: Config):

def __init__(self):
self.button_pin = None
self.buzzer_pin = None
self.buzzer_volume = 1.0
self.audio_pin = None
self.ultrasonic_trigger_pin = None
self.ultrasonic_echo_pin = None
self.trigger_distance = 9999
self.trigger_duration = 0
self.trigger_start = None
self.trigger_run = None
self.trigger_stop = None

def __str__(self):
return f"""
Button:
Pin ............... : {self.button_pin}
Buzzer:
Pin ............... : {self.buzzer_pin}
Volume ............ : {self.buzzer_volume}
Audio:
Pin ............... : {self.audio_pin}:
Ultrasonic Sensor:
Trigger ........... : {self.ultrasonic_trigger_pin}
Echo .............. : {self.ultrasonic_echo_pin}
Trigger:
Distance .......... : {self.trigger_distance} cm
Duration .......... : {self.trigger_duration} seconds
"""

def log(self, level):
for s in self.__str__().split('\n'):
log(level, s)
if config.report_ram:
report_memory_usage("Interactive.__init__() start")

def __init__(self, config: Config):
self.config = config
self.runner = Runner()
self.runner.add_loop_task(self.__cancel_operations)
Expand Down Expand Up @@ -93,7 +61,9 @@ def __init__(self, config: Config):
self.audio_controller = None

if self.config.audio_pin:
self.audio = new_mp3_player(self.config.audio_pin, "dummy.mp3")
# we need a valid tiny file to load otherwise it will error. I got this file from:
# https://github.com/mathiasbynens/small
self.audio = new_mp3_player(self.config.audio_pin, "interactive/mp3.mp3")
self.audio_controller = AudioController(self.audio)
self.audio_controller.register(self.runner)

Expand All @@ -116,6 +86,33 @@ def __init__(self, config: Config):
stop=self.config.trigger_stop)
self.runner.add_loop_task(trigger_loop)

if self.config.report_ram:
async def report_memory() -> None:
report_memory_usage("Interactive.report_memory()")

triggerable = TriggerableAlwaysOn()
report_memory_task = (
new_triggered_task(
triggerable, self.config.report_ram_period, start=report_memory,
cancel_func=terminate_on_cancel(self)))
self.runner.add_task(report_memory_task)

if self.config.garbage_collect:
async def garbage_collect() -> None:
report_memory_usage_and_free("Interactive.garbage_collect()")

triggerable = TriggerableAlwaysOn()
garbage_collect_task = (
new_triggered_task(
triggerable, self.config.garbage_collect_period, stop=garbage_collect,
cancel_func=terminate_on_cancel(self)))
self.runner.add_task(garbage_collect_task)

gc.collect()

if self.config.report_ram:
report_memory_usage("Interactive.__init__() start")

@property
def cancel(self) -> bool:
return self.runner.cancel
Expand All @@ -125,8 +122,8 @@ def cancel(self, cancel: bool) -> None:
self.runner.cancel = cancel

def run(self, callback: Callable[[], Awaitable[None]] = None) -> None:
info('Running with config:')
self.config.log(INFO)
critical('Running with config:')
self.config.log(CRITICAL)
self.runner.run(callback)

async def __cancel_operations(self) -> None:
Expand Down
Loading

0 comments on commit fe69134

Please sign in to comment.