Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an enclosure service for Picroft #122

Open
wants to merge 3 commits into
base: buster
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 45 additions & 11 deletions home/pi/auto_run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

if [ "$SSH_CLIENT" = "" ] && [ "$(/usr/bin/tty)" != "/dev/tty1" ]; then
# Quit immediately when running on a local non-primary terminal,
# e.g. when you hit Ctrl+Alt+F2 to open the second term session
# e.g. when you hit Ctrl+Alt+F2 to open the second term session.

# But go ahead an enter the Mycroft venv...
source mycroft-core/venv-activate.sh -q
return 0
fi

Expand Down Expand Up @@ -110,7 +113,7 @@ function network_setup() {
echo " 3) Edit wpa_supplicant.conf directly"
echo " 4) Force reboot"
echo " 5) Skip network setup for now"
echo -n "${HIGHLIGHT}Choice [1-6]:${RESET} "
echo -n "${HIGHLIGHT}Choice [1-5]:${RESET} "
show_prompt=0
fi

Expand Down Expand Up @@ -273,10 +276,16 @@ function setup_wizard() {
save_choices
fi

if [ -z "$setup_stage" ] ; then
# At the start of setup, install libraries and update to latest Picroft code

# Install Python library to interact with GPIO
sudo /home/pi/mycroft-core/.venv/bin/pip3 install RPi.GPIO

# Check for/download new software (including mycroft-core dependencies, while we are at it).
echo '{"use_branch":"master", "auto_update": true}' > ~/mycroft-core/.dev_opts.json
update_software
# Check for/download new software (including mycroft-core dependencies, while we are at it).
echo '{"use_branch":"master", "auto_update": true}' > ~/mycroft-core/.dev_opts.json
update_software
fi

# installs Pulseaudio if not already installed
if [ $(dpkg-query -W -f='${Status}' pulseaudio 2>/dev/null | grep -c "ok installed") -eq 0 ];
Expand All @@ -285,6 +294,10 @@ function setup_wizard() {
fi

if [ -z "$audio" ] ; then
# Start with the generic enclosure, create a soft link
cp /opt/mycroft/enclosure/templates/Generic_Enclosure.py /opt/mycroft/enclosure/my_enclosure.py
ln -s /opt/mycroft/enclosure/my_enclosure.py ~/my_enclosure.py

echo
echo "========================================================================="
echo "HARDWARE SETUP"
Expand Down Expand Up @@ -366,6 +379,9 @@ function setup_wizard() {
# rebuild venv
bash mycroft-core/dev_setup.sh

# install the AIY-specific enclosure
cp /opt/mycroft/enclosure/templates/AIY_Enclosure.py /opt/mycroft/enclosure/my_enclosure.py

echo "Reboot is required, restarting in 5 seconds..."
audio="google_aiy"
setup_stage="aiy_reboot"
Expand All @@ -380,7 +396,7 @@ function setup_wizard() {

# Flash latest Seeed firmware
echo "Downloading and flashing latest firmware from Seeed..."
sudo /home/pi/mycroft-core/.venv/bin/pip install pyusb click
sudo /home/pi/mycroft-core/.venv/bin/pip3 install pyusb click

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that on systems with python2 AND python3, this might be needed .. BUT, afaict, since we should be within the picroft venv ... shouldn't technically be required; and anyway pip and pip3 are the same on picroft venv systems

(.venv) pi@picroft:~ $ diff /home/pi/mycroft-core/.venv/bin/pip /home/pi/mycroft-core/.venv/bin/pip3ip3
(.venv) pi@picroft:~ $ 

(.venv) pi@picroft:~ $ which pip
/home/pi/mycroft-core/.venv/bin/pip

(.venv) pi@picroft:~ $ ll /home/pi/mycroft-core/.venv/bin/ | grep pip
-rwxr-xr-x 1 pi pi   243 Jun 20 02:19 pip
-rwxr-xr-x 1 pi pi   243 Jun 20 02:19 pip3
-rwxr-xr-x 1 pi pi   243 Jun 20 02:19 pip3.7

git clone https://github.com/respeaker/usb_4_mic_array.git
cd usb_4_mic_array
sudo /home/pi/mycroft-core/.venv/bin/python dfu.py --download 1_channel_firmware.bin
Expand All @@ -392,6 +408,16 @@ function setup_wizard() {
-e "s/mpg123 -a hw:0,0 %1/mpg123 -a plughw:ArrayUAC10,0 %1/" \
/etc/mycroft/mycroft.conf

# Install the Python libraries to control the ring for the enclosure
cd usb_4_mic_array
git clone https://github.com/respeaker/pixel_ring.git
cd pixel_ring
sudo /home/pi/mycroft-core/.venv/bin/pip3 install setuptools
sudo /home/pi/mycroft-core/.venv/bin/python setup.py install

# Install the Seeed ReSpeaker-specific enclosure
cp /opt/mycroft/enclosure/templates/ReSpeaker_Enclosure.py /opt/mycroft/enclosure/my_enclosure.py

audio="seed_mic_array_20"
break
;;
Expand Down Expand Up @@ -497,6 +523,9 @@ function setup_wizard() {
skip_mic_test=true
skip_last_prompt=true
mic="matrix_voice"

# install the Matrix-specific enclosure
cp /opt/mycroft/enclosure/templates/Matrix_Enclosure.py /opt/mycroft/enclosure/my_enclosure.py
break
;;
4)
Expand Down Expand Up @@ -755,7 +784,7 @@ then
echo "Mycroft quick and easy. Would you like help setting up your system?"
echo " Y)es, I'd like the guided setup."
echo " N)ope, just get me a command line and get out of my way!"

# Something in the boot sequence is sending a CR to the screen, so wait
# briefly for it to be sent for purely cosmetic purposes.
sleep 1
Expand Down Expand Up @@ -901,19 +930,24 @@ then

# Auto-update to latest version of Picroft scripts and mycroft-core
update_software

# Launch Mycroft Services ======================
bash "$HOME/mycroft-core/start-mycroft.sh" all

# Start the Picroft enclosure service in the background
cd /opt/mycroft/enclosure
sudo -- bash -c 'source /home/pi/mycroft-core/venv-activate.sh; python3 my_enclosure.py >> /var/log/mycroft/enclosure.log 2>&1 &'
cd ~

# Display success/welcome message for user
echo
echo
mycroft-help
echo
if [ "$initial_setup" = true ]; then

if [ "$initial_setup" = true ]; then
echo "Mycroft is completing startup, ensuring all of the latest versions"
echo "of skills are installed. Within a few minutes you will be prompted"
echo "of skills are installed. Within a few minutes you will be prompted"
echo "to pair this device with the required online services at:"
echo "https://home.mycroft.ai"
echo "where you can enter the pairing code."
Expand Down
22 changes: 22 additions & 0 deletions opt/mycroft/enclosure/launch_enclosure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
##########################################################################
# file_watchdog.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.
##########################################################################

# Gain root access (needed for GPIO and Admin actions), load the Mycroft
# virtual environment, and launch the enclosure script
sudo -- bash -c 'source /home/pi/mycroft-core/venv-activate.sh; python3 my_enclosure.py'
Empty file.
64 changes: 64 additions & 0 deletions opt/mycroft/enclosure/lib/file_watchdog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python
##########################################################################
# file_watchdog.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 implements a watchdog to reload the given script(s) when changes
# are made to the files. In other words, it automatically restarts the given
# program when the program or other watched files are edited and saved to disk.
#
# Usage:
# import lib.file_wachdog as watchdog
# watchdog.watch(__file__)

import os, sys
import threading
from os.path import getmtime

__watched_files_mtimes = []


def __checkForModification():
for f, mtime in __watched_files_mtimes:
if getmtime(f) != mtime:
# Code modification detected, re-launch the main program to
# pick up the changes.
return os.execv(sys.executable, ['python'] + sys.argv)

# Rerun the check in 5 seconds
t = threading.Timer(5, __checkForModification)
t.daemon = True # don't block the program from ending if main block exits
t.start()


def watch(files):
"""Start watchdog to relaunch the program when the file(s) change

Args:
files (string or [string]): File(s) to monitor for changes
"""
global __watched_files_mtimes

if isinstance(files, list):
__watched_files = files
else:
__watched_files = [files] # support a single filename
__watched_files_mtimes = [(f, getmtime(f)) for f in __watched_files]

# Kick-off monitor which checks every 5 seconds for code changes
__checkForModification()
114 changes: 114 additions & 0 deletions opt/mycroft/enclosure/lib/gpio_button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env python
##########################################################################
# gpio_button.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
from time import time

class GPIO_Button:

def __init__(self, GPIO_BCM=23, on_press=None, on_release=None,
on_hold=None, on_double_press=None):
""" Interface for a button connected to a GPIO pin

Respond to a button connected to a GPIO pin (and ground)

Args:
GPIO_BCM (int, optional): The BCM number of the GPIO pin to use.
Defaults to 23.
"""
self.button_bcm = GPIO_BCM

self._on_press = on_press
self._on_release = on_release
self._on_hold = on_hold
self._on_double_press = on_double_press

GPIO.setmode(GPIO.BCM)
GPIO.setup(self.button_bcm, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(23, GPIO.BOTH, self._on_gpio_button)
self.__timer = None
self.__timer2 = None
self.__last_press = 0
self.__pressed = False
self.__double_pressed = False

def __del__(self):
GPIO.remove_event_detect(self.button_bcm)

def _on_gpio_button(self, channel):
if GPIO.input(channel) == GPIO.HIGH:
# High == button unpressed (pulled high to 3.3v)
self.__pressed = False
if self.__timer:
self.__timer.cancel()
if self.__timer2:
self.__timer2.cancel()
# Wait briefly before firing event to de-bounce
self.__timer = Timer(0.05, self._on_gpio_released)
self.__timer.start()
else:
# Low == button pressed (grounded)
self.__pressed = True
if self.__timer:
self.__timer.cancel()
if self.__timer2:
self.__timer2.cancel()
# Wait briefly before firing event to de-bounce
self.__timer = Timer(0.05, self._on_gpio_pressed)

# Hold for 1 second to get a "hold" action
self.__timer2 = Timer(1, self._on_gpio_held)
self.__timer.start()
self.__timer2.start()

def _on_gpio_pressed(self):
self.__timer = None
if self.__pressed:
now = time()
time_since_last = now - self.__last_press
self.__last_press = now
if time_since_last < 1:
# Two presses within a second == double press event
self.__double_pressed = True
if self._on_double_press:
self._on_double_press()
else:
# Single press event
self.__double_pressed = False
if self._on_press:
self._on_press()

def _on_gpio_released(self):
self.__timer = None
if not self.__pressed and not self.__double_pressed:
if time() - self.__last_press < 1:
# Release event
if self._on_release:
self._on_release()
else:
# Either button was held or a spurrious signal with no press
pass

def _on_gpio_held(self):
self.__timer2 = None
if self.__pressed:
# Button held event
if self._on_hold:
self._on_hold()
Loading