Hackster is hosting Hackster Holidays, Ep. 4: Livestream & Giveaway Drawing. Start streaming on Wednesday!Stream Hackster Holidays, Ep. 4 on Wednesday!
Grant Doherty
Published

Metro-gnome

This cool little garden-dweller keeps time with the beats from Amazon Music, powered by Alexa and LEGO MINDSTORMS EV3.

BeginnerFull instructions provided2 hours784

Things used in this project

Story

Read more

Schematics

Metrognome LEGO model

LEGO build instrutions. Use Bricklink's Studio 2.0 to open this file.

Code

metrognome.py

Python
This is the Python to run on ev3dev
import os
import sys
import logging
import threading
import random

from time import time
from agt import AlexaGadget

from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import OUTPUT_A, LargeMotor

# Set the logging level to INFO to see messages from AlexaGadget
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(message)s')
logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
logger = logging.getLogger(__name__)


class MindstormsGadget(AlexaGadget):
    """
    A Mindstorms gadget that performs movement in sync with music tempo.
    """

    def __init__(self):
        """
        Performs Alexa Gadget initialization routines and ev3dev resource allocation.
        """
        super().__init__()

        # Ev3dev initialization
        self.leds = Leds()
        self.sound = Sound()
        self.motor = LargeMotor(OUTPUT_A)

        # Gadget states
        self.bpm = 0
        self.trigger_bpm = "off"

    def on_connected(self, device_addr):
        """
        Gadget connected to the paired Echo device.
        :param friendly_name: the friendly name of the gadget that has connected to the echo device
        """
        self.leds.set_color("LEFT", "GREEN")
        self.leds.set_color("RIGHT", "GREEN")
        logger.info("{} connected to Echo device".format(self.friendly_name))

    def on_disconnected(self, device_addr):
        """
        Gadget disconnected from the paired Echo device.
        :param friendly_name: the friendly name of the gadget that has disconnected from the echo device
        """
        self.leds.set_color("LEFT", "BLACK")
        self.leds.set_color("RIGHT", "BLACK")
        logger.info("{} disconnected from Echo device".format(self.friendly_name))

    def on_alexa_gadget_musicdata_tempo(self, directive):
        """
        Provides the music tempo of the song currently playing on the Echo device.
        :param directive: the music data directive containing the beat per minute value
        """
        tempo_data = directive.payload.tempoData
        for tempo in tempo_data:

            print("tempo value: {}".format(tempo.value), file=sys.stderr)
            if tempo.value > 0:
                # dance pose
                self.trigger_bpm = "on"
                threading.Thread(target=self._beat_loop, args=(tempo.value,)).start()

            elif tempo.value == 0:
                # stops the dance loop
                self.trigger_bpm = "off"

    def _beat_loop(self, bpm):
        """
        Perform motor movement in sync with the beat per minute value from tempo data.
        :param bpm: beat per minute from AGT
        """
        self.motor.position = 0
        pos = 40
        speed = min(1000,bpm*2)
        seconds_per_beat = 60/bpm
        next_time = time() + seconds_per_beat
        print("Adjusted seconds per beat: {}".format(seconds_per_beat), file=sys.stderr)
        while self.trigger_bpm == "on":

            # wait until next movement time
            while time() < next_time:
                pass

            # move motor to next position
            self.motor.run_to_abs_pos(position_sp=pos, speed_sp=speed, stop_action="hold")
            # change so next position is on opposite side
            pos = -pos
            # set time for next movement
            next_time = next_time + seconds_per_beat
        
        # move to straight position when tempo has stopped
        self.motor.run_to_abs_pos(position_sp=0, speed_sp=speed, stop_action="hold")
        print("Exiting BPM process.", file=sys.stderr)


if __name__ == '__main__':

    gadget = MindstormsGadget()

    # Set LCD font and turn off blinking green LEDs
    os.system('setfont Lat7-Terminus12x6')
    gadget.leds.set_color("LEFT", "BLACK")
    gadget.leds.set_color("RIGHT", "BLACK")

    # Startup sequence 
    gadget.sound.play_song((('C4', 'e'), ('D4', 'e'), ('E5', 'q')))
    gadget.leds.set_color("LEFT", "GREEN")
    gadget.leds.set_color("RIGHT", "GREEN")

    # Gadget main entry point
    gadget.main()

    # Shutdown sequence
    gadget.sound.play_song((('E5', 'e'), ('C4', 'e')))
    gadget.leds.set_color("LEFT", "BLACK")
    gadget.leds.set_color("RIGHT", "BLACK")

Credits

Grant Doherty

Grant Doherty

1 project • 1 follower

Comments