| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # gpio_led.py | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| import RPi.GPIO as GPIO | ||
| from threading import Timer | ||
|
|
||
| class GPIO_LED: | ||
|
|
||
| def __init__(self, GPIO_BCM=21): | ||
| """ Interface with an LED connected to a GPIO pin | ||
| Control an LED connected to a GPIO pin (and ground) | ||
| Args: | ||
| GPIO_BCM (int, optional): The BCM number of the GPIO pin to use. | ||
| Defaults to 21. | ||
| """ | ||
| super().__init__() | ||
| self.led_bcm = GPIO_BCM | ||
| GPIO.setmode(GPIO.BCM) | ||
| GPIO.setup(self.led_bcm, GPIO.OUT) | ||
| self.__timer = None | ||
|
|
||
| def turn_on(self, flash_on_duration=None, flash_off_duration=0.1): | ||
| """ Turn on the LED and optionally flash it | ||
| Args: | ||
| flash_on_duration (float, optional): Seconds to remain lit, | ||
| or None for constant. | ||
| flash_off_duration (float, optional): Seconds to go dark. | ||
| Defaults to 0.1. | ||
| """ | ||
| if self.__timer: | ||
| self.__timer.cancel() | ||
| self.timer = None | ||
| GPIO.output(self.led_bcm, GPIO.HIGH) | ||
| if flash_on_duration: | ||
| self.__timer = Timer(flash_on_duration, self.__flasher, | ||
| [True, flash_on_duration, flash_off_duration]) | ||
| self.__timer.start() | ||
|
|
||
| def turn_off(self): | ||
| """ Illuminate the LED """ | ||
| if self.__timer: | ||
| self.__timer.cancel() | ||
| self.timer = None | ||
| GPIO.output(self.led_bcm, GPIO.LOW) | ||
|
|
||
| def __flasher(self, is_on, on_duration, off_duration): | ||
| if self.__timer: | ||
| self.__timer.cancel() | ||
| self.timer = None | ||
| if is_on: | ||
| GPIO.output(self.led_bcm, GPIO.HIGH) | ||
| next_change = off_duration | ||
| else: | ||
| GPIO.output(self.led_bcm, GPIO.LOW) | ||
| next_change = on_duration | ||
| self.__timer = Timer(next_change, self.__flasher, | ||
| [not is_on, on_duration, off_duration]) | ||
| self.__timer.start() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # picroft_enclosure.py | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| # This file defines the base enclosure for your Picroft. By default | ||
| # it supports: | ||
| # * A button connected to the GPIO-23 (and ground) as a "Stop" | ||
| # * A LED connected to the GPIO-21 as an activity indicator | ||
| # * Reboot and shutdown administrative actions | ||
| # | ||
| # Customizations should be built on top of this. See the ../templates | ||
| # folder for examles, and my_enclosure.py which the setup wizard | ||
| # creates for your own special behavior. | ||
|
|
||
| from mycroft.client.enclosure.generic import EnclosureGeneric | ||
| from mycroft.messagebus.message import Message | ||
| from mycroft.util import create_signal | ||
| from time import sleep | ||
| import os | ||
| import sys | ||
|
|
||
| from .gpio_button import GPIO_Button | ||
| from .gpio_led import GPIO_LED | ||
|
|
||
| ########################################################################## | ||
|
|
||
| class Picroft_Enclosure(EnclosureGeneric): | ||
|
|
||
| def __init__(self, button_gpio_bcm=23, led_gpio_bcm=21): | ||
| super().__init__() | ||
|
|
||
| # Administrative messages | ||
| self.bus.on("system.shutdown", self.on_shutdown) | ||
| self.bus.on("system.reboot", self.on_reboot) | ||
| self.bus.on("system.update", self.on_software_update) | ||
|
|
||
| # Interaction feedback | ||
| self.bus.on("recognizer_loop:wakeword", self.indicate_listening) | ||
| self.bus.on("recognizer_loop:record_begin", self.indicate_listening) | ||
| self.bus.on("recognizer_loop:record_end", self.indicate_listening_done) | ||
|
|
||
| self.bus.on("recognizer_loop:sleep", self.indicate_sleeping) | ||
| self.bus.on("mycroft.awoken", self.indicate_waking) | ||
|
|
||
| self.bus.on("recognizer_loop:audio_output_start", self.indicate_talking) | ||
| self.bus.on("recognizer_loop:audio_output_end", self.indicate_talking_done) | ||
| self.bus.on("mycroft.skill.handler.start", self.indicate_thinking) | ||
| self.bus.on("mycroft.skill.handler.complete", self.indicate_thinking_done) | ||
|
|
||
| # Visual indication that system is booting | ||
| self.bus.on("mycroft.skills.initialized", self.on_ready) | ||
|
|
||
| # Setup to support a button on a GPIO -- default is GPIO23 | ||
| if button_gpio_bcm: | ||
| self.button = GPIO_Button(GPIO_BCM=button_gpio_bcm, | ||
| on_press=self.on_button_pressed, | ||
| on_double_press=self.on_button_double_press, | ||
| on_release=self.on_button_released, | ||
| on_hold=self.on_button_held) | ||
|
|
||
| # Indicate activity using a LED on selected GPIO | ||
| if led_gpio_bcm: | ||
| self.indicator = GPIO_LED(led_gpio_bcm) # Feedback LED light | ||
| else: | ||
| self.indicator = None | ||
|
|
||
| self.asleep = False | ||
|
|
||
| self.bus.on("mycroft.skills.initialized", self.indicate_booting_done) | ||
| self.indicate_booting() | ||
|
|
||
| def indicate_booting(self): | ||
| # Placeholder, override to start something during the bootup sequence | ||
| if self.indicator: | ||
| self.indicator.turn_on(flash_on_duration=0.5) | ||
|
|
||
| def indicate_booting_done(self, message): | ||
| # Boot sequence has completed, turn off booting visualization | ||
| # Override if visualization is done differently | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
|
|
||
| def indicate_sleeping(self, message): | ||
| # Turn lights when asleep | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
| self.asleep = True | ||
|
|
||
| def indicate_waking(self, message): | ||
| if self.indicator: | ||
| self.indicator.turn_on(flash_on_duration=0.25) | ||
| sleep(2) | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
| self.asleep = False | ||
|
|
||
| ###################################################################### | ||
| # Basic button support. By default behave like the Mark 1 button where | ||
| # pressing it either stops a current action or starts listening | ||
| # | ||
| # Override these actions to react to button events | ||
| # NOTE: Basic "button click" actions should happen on the Release instead | ||
| # of the Press. This allows the Held to occur without triggering | ||
| # a "button click". | ||
|
|
||
| def on_button_pressed(self): | ||
| """ The button has been depressed once """ | ||
| pass | ||
|
|
||
| def on_button_double_press(self): | ||
| """ The button has been depressed within 1 sec of a previous pressing | ||
| NOTE: No on_button_pressed() is generated for the second press. | ||
| """ | ||
| pass | ||
|
|
||
| def on_button_held(self): | ||
| """ The button has been pressed and held for a second """ | ||
| pass | ||
|
|
||
| def on_button_released(self): | ||
| """ The button has been released after a press | ||
| NOTE: No on_button_released() occurs when held or double-pressed. | ||
| """ | ||
| # Generate a Listen/Stop signal like the top button press on a Mark 1. | ||
| create_signal('buttonPress') | ||
| self.bus.emit(Message("mycroft.stop")) | ||
|
|
||
| ###################################################################### | ||
| # Interaction sequence indicators. The trypical sequence is: | ||
| # - indicate_listening() | ||
| # - indicate_listening_done() | ||
| # - indicate_thinking() | ||
| # - indicate_talking() | ||
| # - indicate_talking_done() | ||
| # - indicate_thinking_done() | ||
| # There are variations on this, for example an inadvertant recording might | ||
| # begin a handler, thus no "thinking". Or there might be multiple talking | ||
| # sequences within once thinking session. | ||
|
|
||
| # Illuminate lights when listening | ||
| def indicate_listening(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_on() | ||
|
|
||
| def indicate_listening_done(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_off() | ||
|
|
||
| def indicate_thinking(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_on() | ||
|
|
||
| def indicate_thinking_done(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_off() | ||
|
|
||
| def indicate_talking(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_on(flash_on_duration=0.25) | ||
|
|
||
| def indicate_talking_done(self, message): | ||
| if self.asleep or not self.indicator: | ||
| return | ||
| self.indicator.turn_off() | ||
|
|
||
| ###################################################################### | ||
| # Administrative actions | ||
| # | ||
| # Many of the following require root access, but can operate because | ||
| # this process is launched using sudo. | ||
|
|
||
| def on_software_update(self, message): | ||
| # Mycroft updates itself on reboots | ||
| self.speak("Updating system, please wait") | ||
| sleep(5) | ||
| os.system("cd /home/pi/mycroft-core && git pull") | ||
| sleep(2) | ||
|
|
||
| self.speak("Rebooting to apply update") | ||
| sleep(5) | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
| os.system("shutdown --reboot now") | ||
|
|
||
| def on_shutdown(self, message): | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
| os.system("shutdown --poweroff now") | ||
|
|
||
| def on_reboot(self, message): | ||
| # Example of using the self.speak() helper function | ||
| self.speak("I'll be right back") | ||
| sleep(5) | ||
| if self.indicator: | ||
| self.indicator.turn_off() | ||
| os.system("shutdown --reboot now") | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # AIY_Enclosure.sh | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| # This file defines a simple custom enclosure for your Picroft. By default | ||
| # it supports: | ||
| # * The AIY button connected as a "Stop" | ||
| # * The AIY button's LED as an activity indicator | ||
| # * Reboot and shutdown administrative actions | ||
| # | ||
| # Feel free to modify this code to your own purposes. Changes will not be | ||
| # overwritten by the update process will not overwrite it. This code monitors | ||
| # the messagebus for system events, listens to GPIOs, and can do just about | ||
| # anything you'd like. | ||
| # | ||
| # Changes made to the file will restart the enclosure process automatically | ||
| # but be careful -- syntax errors will require manual relaunching or reboot | ||
| # after the error is fixed. Relaunch manually via: | ||
| # cd ~/enclosure | ||
| # python ~/my_enclosure.py | ||
|
|
||
| from lib.picroft_enclosure import Picroft_Enclosure | ||
| import lib.file_watchdog as watchdog | ||
| import lib.GPIO_Button | ||
| import lib.GPIO_LED | ||
|
|
||
| watchdog.watch(__file__) | ||
|
|
||
| ########################################################################## | ||
|
|
||
| class AIY_Enclosure(Picroft_Enclosure): | ||
|
|
||
| def __init__(self): | ||
| super().__init__() | ||
|
|
||
| # Support the standard AIY button | ||
| self.stop_button = lib.GPIO_Button(self.bus, GPIO_BCM=23) | ||
|
|
||
| # Support the standard AIY button's LED as an activity indicator | ||
| self.visual = lib.GPIO_LED(GPIO_BCM=25) | ||
|
|
||
|
|
||
| enc = AIY_Enclosure() | ||
| enc.run() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # Generic_Enclosure.py | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| # This file defines a basic enclosure for your Picroft. By default it supports: | ||
| # * LED activity light on GPIO-21 | ||
| # * A button connected to the GPIO-23 (and ground) as a Stop button | ||
| # * Administrative actions such as reboot and shutdown | ||
| # | ||
| # Feel free to modify this code to your own purposes. Changes will not be | ||
| # overwritten by the update process will not change it. This code monitors | ||
| # the messagebus for system events, listens to GPIOs, and can do just about | ||
| # anything you'd like. | ||
| # | ||
| # Changes to my_enclosure.py will restart the enclosure process automatically | ||
| # but be careful -- syntax errors will require manual relaunching or reboot | ||
| # after the error is fixed. | ||
|
|
||
| from lib.picroft_enclosure import Picroft_Enclosure | ||
| from time import sleep | ||
|
|
||
| import lib.file_watchdog as watchdog | ||
| watchdog.watch(__file__) | ||
|
|
||
| ########################################################################## | ||
|
|
||
| class Generic_Enclosure(Picroft_Enclosure): | ||
|
|
||
| def __init__(self): | ||
| super().__init__(button_gpio_bcm=23, led_gpio_bcm=21) | ||
|
|
||
| enc = Generic_Enclosure() | ||
| enc.run() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # Matrix_Enclosure.sh | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| # This file defines a simple custom enclosure for your Picroft. By default | ||
| # it supports: | ||
| # * A button connected to the GPIO-23 (and ground) as a Stop button | ||
| # * LED activity light on GPIO-21 | ||
| # * Administrative actions such as reboot and shutdown | ||
| # | ||
| # Feel free to modify this code to your own purposes. Changes will not be | ||
| # overwritten by the update process will not change it. This code monitors | ||
| # the messagebus for system events, listens to GPIOs, and can do just about | ||
| # anything you'd like. | ||
| # | ||
| # Changes made to the file will restart the enclosure process automatically | ||
| # but be careful -- syntax errors will require manual relaunching or reboot | ||
| # after the error is fixed. Relaunch manually via: | ||
| # cd ~/enclosure | ||
| # python ~/my_enclosure.py | ||
|
|
||
| from lib.picroft_enclosure import Picroft_Enclosure | ||
| import lib.file_watchdog as watchdog | ||
|
|
||
| watchdog.watch(__file__) | ||
|
|
||
| ########################################################################## | ||
|
|
||
| class Matrix_Enclosure(Picroft_Enclosure): | ||
|
|
||
| def __init__(self): | ||
| super().__init__() | ||
|
|
||
| # TODO: Use the Matrix light array to indicate status | ||
|
|
||
|
|
||
| enc = Matrix_Enclosure() | ||
| enc.run() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #!/usr/bin/env python | ||
| ########################################################################## | ||
| # ReSpeaker_Enclosure.py | ||
| # | ||
| # Copyright 2019, Stephen Penrod | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| ########################################################################## | ||
|
|
||
| # This file defines a custom enclosure for your Picroft using a Seeed ReSpeaker | ||
| # Mic Array. By default it supports: | ||
| # * Animations on the pixel ring on the mic array | ||
| # * A button connected to the GPIO-23 as a Stop button | ||
| # * Reboot and shutdown administrative actions | ||
| # | ||
| # Feel free to modify this code to your own purposes. Changes will not be | ||
| # overwritten by the update process will not overwrite it. This code monitors | ||
| # the messagebus for system events, listens to GPIOs, and can do just about | ||
| # anything you'd like. | ||
| # | ||
| # Changes to my_enclosure.py will restart the enclosure process automatically | ||
| # but be careful -- syntax errors will require manual relaunching or reboot | ||
| # after the error is fixed. | ||
|
|
||
| from lib.picroft_enclosure import Picroft_Enclosure | ||
| from time import sleep | ||
|
|
||
| # The pixel_ring code is installed from Github in the home directory | ||
| import sys | ||
| sys.path.insert(1, '/home/pi/usb_4_mic_array/pixel_ring') | ||
| from pixel_ring import pixel_ring | ||
|
|
||
| import lib.file_watchdog as watchdog | ||
| watchdog.watch(__file__) | ||
|
|
||
| ########################################################################## | ||
|
|
||
| class ReSpeaker_Enclosure(Picroft_Enclosure): | ||
|
|
||
| def __init__(self): | ||
| super().__init__(led_gpio_bcm=None) # Pixel Ring is used to show state | ||
| # instead of a LED on a GPIO | ||
|
|
||
| def indicate_booting(self): | ||
| # Visual indication that system is booting | ||
| pixel_ring.set_brightness(2) | ||
| pixel_ring.spin() | ||
|
|
||
| def indicate_booting_done(self, message): | ||
| # Boot has completed, turn off booting visualization | ||
| pixel_ring.off() | ||
|
|
||
| def indicate_sleeping(self, message): | ||
| # Turn lights orange when asleep | ||
| pixel_ring.set_color(False, 250, 60, 0) | ||
| self.asleep = True | ||
|
|
||
| def indicate_waking(self, message): | ||
| # Restore lights when woken | ||
| pixel_ring.spin() | ||
| sleep(2) | ||
| pixel_ring.off() | ||
| self.asleep = False | ||
|
|
||
| ###################################################################### | ||
| # Interaction sequence indicators. The trypical sequence is: | ||
| # - indicate_listening() | ||
| # - indicate_listening_done() | ||
| # - indicate_thinking() | ||
| # - indicate_talking() | ||
| # - indicate_talking_done() | ||
| # - indicate_thinking_done() | ||
| # There are variations on this, for example an inadvertant recording might never | ||
| # begin a handler sequence. Or there might be multiple output begin/end pairs | ||
| # within the handler. | ||
|
|
||
| # Illuminate lights when listening | ||
| def indicate_listening(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.listen() | ||
|
|
||
| def indicate_listening_done(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.off() | ||
|
|
||
| def indicate_thinking(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.think() | ||
|
|
||
| def indicate_thinking_done(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.off() | ||
|
|
||
| def indicate_talking(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.speak() | ||
|
|
||
| def indicate_talking_done(self, message): | ||
| if self.asleep: | ||
| return | ||
| pixel_ring.off() | ||
|
|
||
|
|
||
| enc = ReSpeaker_Enclosure() | ||
| enc.run() |