In this blog post I would like to go into more detail about setting up a Snips satellite. A Google AIY Voice Kit supplemented by a Raspberry Pi Zero W is used - all installed together in a 3d printed custom case. If you followed my other blog posts about Snips, you know how easy it is to get started with Snips. You should have a working Snips installation, because it should act as main unit for the satellite - which is a prerequisite for accomplishing the following steps. If not, hurry up and read "The Best Way to your Private Voice Assistant" here on my blog.

A main unit contains a fully fledged installation of Snips, which mainly includes the components ASR, NLU, DIALOGUE and TTS. While a satellite is a stripped-down version of Snips, which is mainly used for audio input and output only, namely the AUDIO-SERVER and possibly HOTWORD components. For all other services the satellite forwards to the main unit and communicates via MQTT. This is why a satellite can run on much smaller hardware and a Raspberry Pi Zero is a perfect device. A Raspberry Pi 3 would of course also be possible, but somewhat oversized for our satellite device.

List of materials

For my Snips satellite I used the following hardware:

Read the documentation from Google on how to assemble your Google Voice Kit.

Setup your Satellite

So let's get started: at this point we start with an SD card on which we flashed a Raspbian Stretch Lite image. SSH access should be activated and if necessary the Wifi configuration should be made. Use ssh to log in to your Raspberry Pi.

1. Configure Google AIY Voice Hat

Skip the steps specific to Google Voice Hat if you don't use it for your satellite or customize them for your hardware (e.g. Respeaker mic-array). In principle, the following steps can be used in all possible hardware configurations.

The installation of the necessary drivers for the Google AIY Voice Hat is fortunately quite simple. Google has uploaded scripts to Github, which we clone and execute using Git.

$ cd ~
$ sudo apt-get install git
$ git clone https://github.com/google/aiyprojects-raspbian.git
$ cd aiyprojects-raspbian
$ git checkout voicekit
$ sudo scripts/configure-driver.sh
$ sudo scripts/install-alsa-config.sh

Just check if the file /etc/asound.conf was created with the following content.

options snd_rpi_googlevoicehat_soundcard index=0

pcm.softvol {
    type softvol
    slave.pcm dmix
    control {
        name Master
        card 0
    }
}

pcm.micboost {
    type route
    slave.pcm dsnoop
    ttable {
        0.0 30.0
        1.1 30.0
    }
}

pcm.!default {
    type asym
    playback.pcm "plug:softvol"
    capture.pcm "plug:micboost"
}

ctl.!default {
    type hw
    card 0
}

At the end of the file /boot/config.txt some Google Voice Hat specific options will be added and activated.

# ...
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
dtparam=i2s=on
#dtparam=spi=on

# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
#dtparam=audio=on
dtoverlay=i2s-mmap
dtoverlay=googlevoicehat-soundcard

Now reboot your Raspberry Pi by running sudo reboot and check for existing playback and capture device afterwards.

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 []
  Subdevices: 0/1
  Subdevice #0: subdevice #0

The microphone and speaker of Voice Hat are configured now and we can deal with the installation of the Snips components in the next step.

2. Install Snips Audio Server

The steps that follow now are similar to installing Snips on the main unit. But note that we do not install the entire Snips Voice Platform, but only the Snips Audio Server and Snips Watch component.

$ sudo apt-get update
$ sudo apt-get install -y dirmngr
$ sudo bash -c  'echo "deb https://raspbian.snips.ai/$(lsb_release -cs) stable main" > /etc/apt/sources.list.d/snips.list'
$ sudo apt-key adv --keyserver pgp.mit.edu --recv-keys D4F50CDCA10A2849
$ sudo apt-get update
$ sudo apt-get install -y snips-audio-server snips-watch

If the keyserver pgp.mit.edu is down try to use another one, like pgp.surfnet.nl.

Do not run sam setup audio because we have already created a fitting audio configuration and otherwise these settings in /etc/asound.conf would be overwritten.

3. Configure Snips Audio Server

Make sure that this setting is made on the satellite device.

The configuration of the Snips Audio Server is straightforward and only affects two lines in /etc/snips.toml. In the section [snips-common] you need to change the entry for mqtt = "localhost:1883" by setting the IP of your Snips main unit, e.g. mqtt = "192.168.1.126:1883". The second setting to be adjusted is in the [snips-audio-server] section. Adjust the entry bind so that you give your satellite a unique ID, e.g. bind = "kitchen@mqtt". This ID is used to determine the source or destination of payloads in the MQTT messages. If an ID is accidentally assigned more than once, this can lead to strange effects, since then several devices feel addressed by corresponding messages. Here is the complete file:

[snips-common]
# bus = "mqtt"
mqtt = "192.168.1.126:1883"
# audio = "localhost:26300"
# assistant = "/usr/share/snips/assistant"
# user_dir = "/var/lib/snips"

[snips-analytics]

[snips-asr]
# no_fst_map = true
# beam_size = 8
# model = "/usr/share/snips/asr"
# audio = ["+"]

[snips-asr-google]
# credentials = "/usr/share/snips/googlecredentials.json"
# audio = ["+"]

[snips-audio-server]
# frame = 256
bind = "kitchen@mqtt"
# mike = "Built-in Microphone"

[snips-dialogue]
# resources = "/usr/share/snips/dialogue"
# session_timeout = 15
# lambda_timeout = 5
# retry_count = 3
# sound_feedback_enabled_default = true

[snips-hotword]
# model = "/usr/share/snips/hotword"
# hotword_id = "default"
# sensitivity = "0.5"
# audio = ["+"]

[snips-nlu]

[snips-tts]
## Choose one tts provider (defaults to picotts)
# provider = "picotts"
# provider = "makerstts"
# provider = "customtts"
## customtts specific configuration (here configured to use picotts)
# customtts = { command = ["pico2wave", "-w", "%%OUTPUT_FILE%%", "-l", "%%LANG%%", "%%TEXT%%"] }

Restart the audio-server sudo service snips-audio-server restart. Now you can use the sam cli to check the status of the installation on the satellite... and this is what a trimmed-down installation of Snips for satellite devices looks like.

$ sam connect 192.168.1.104
? Enter username for the device: pi
? Enter password for the device: [hidden]
✔ Connected to 192.168.1.104
i A public key has been generated and copied to the device at 192.168.1.104:~/.ssh/authorized_keys
$ sam status

Connected to device 192.168.1.104

OS version ................... Raspbian GNU/Linux 9 (stretch)
Installed assistant .......... Not installed
Status ....................... Live (no assistant)

Service status:

snips-analytics .............. (not running)
snips-asr .................... (not running)
snips-audio-server ........... 0.56.4 (running)
snips-dialogue ............... (not running)
snips-hotword ................ (not running)
snips-nlu .................... (not running)
snips-skill-server ........... (not running)
snips-tts .................... (not running)

4. Configure Snips Hotword

Make sure that this setting is made on the Snips main unit.

Now we switch to the main unit and make another setting in the file /etc/snips.toml. First setting to be adjusted is in the [snips-audio-server] section. Adjust the entry bind so that you give your main unit a unique ID, e.g. bind = "main@mqtt". The other setting to be adjusted is in section [snips-hotword]. Change the entry audio = ["+"] to include the two IDs we are using audio=["main@mqtt", "kitchen@mqtt"]. Here is the complete file:

[snips-common]
# bus = "mqtt"
# mqtt = "localhost:1883"
# audio = "localhost:26300"
# assistant = "/usr/share/snips/assistant"
# user_dir = "/var/lib/snips"

[snips-analytics]

[snips-asr]
# no_fst_map = true
# beam_size = 8
# model = "/usr/share/snips/asr"
# audio = ["+"]

[snips-asr-google]
# credentials = "/usr/share/snips/googlecredentials.json"
# audio = ["+"]

[snips-audio-server]
# frame = 256
# bind = "0.0.0.0:26300"
bind = "main@mqtt"
# mike = "Built-in Microphone"

[snips-dialogue]
# resources = "/usr/share/snips/dialogue"
# session_timeout = 15
# lambda_timeout = 5
# retry_count = 3
# sound_feedback_enabled_default = true

[snips-hotword]
# model = "/usr/share/snips/hotword"
# hotword_id = "default"
# sensitivity = "0.5"
# audio = ["+"]
audio=["main@mqtt", "kitchen@mqtt"]

[snips-nlu]

[snips-tts]
## Choose one tts provider (defaults to picotts)
# provider = "picotts"
# provider = "makerstts"
# provider = "customtts"
## customtts specific configuration (here configured to use picotts)
# customtts = { command = ["pico2wave", "-w", "%%OUTPUT_FILE%%", "-l", "%%LANG%%", "%%TEXT%%"] }```

Restart the hotword service and audio-server:

$ sudo service snips-hotword restart
$ sudo service snips-audio-server restart

So let's take it for a spin, assuming you have an assistant and skill installed on the main unit.

$ sam connect 192.168.1.126
? Enter username for the device: pi
? Enter password for the device: [hidden]
✔ Connected to 192.168.1.126
$ sam watch
[07:42:36] Watching on localhost:1883 (MQTT)
[07:42:41] [Hotword] detected on site kitchen, for model default
[07:42:41] [Asr] was asked to stop listening on site kitchen
[07:42:42] [Hotword] was asked to toggle itself 'off' on site kitchen
[07:42:42] [Dialogue] session with id '0146c49a-d042-44ff-8332-a74ea983c796' was started on site kitchen
[07:42:42] [AudioServer] was asked to play a wav of 41.1 kB with id '88809600-f303-44eb-8521-a8d8342b875f' on site kitchen
[07:42:42] [AudioServer] finished playing wav with id '88809600-f303-44eb-8521-a8d8342b875f'
[07:42:42] [Asr] was asked to listen on site kitchen
[07:42:46] [Asr] captured text "welche kalenderwoche haben wir heute" in 3.0s
[07:42:46] [Asr] was asked to stop listening on site kitchen
[07:42:46] [AudioServer] was asked to play a wav of 93.1 kB with id 'db7870a8-c48d-47fc-a87b-ea185d1c6429' on site kitchen
[07:42:48] [AudioServer] finished playing wav with id 'db7870a8-c48d-47fc-a87b-ea185d1c6429'
[07:42:48] [Nlu] was asked to parse input welche kalenderwoche haben wir heute
[07:42:48] [Nlu] detected intent CrystalMethod:weekNumber with probability 0.961 for input "welche kalenderwoche haben wir heute"
              Slots ->
                 date -> 2018-06-28 00:00:00 +00:00
[07:42:48] [Dialogue] New intent detected CrystalMethod:weekNumber with probability 0.961
              Slots ->
                 date -> 2018-06-28 00:00:00 +00:00
[07:42:48] [Dialogue] was ask to end session with id 0146c49a-d042-44ff-8332-a74ea983c796 by saying 'Der 28.6.2018 ist in Kalenderwoche 26 .'
[07:42:48] [Tts] was asked to say "Der 28.6.2018 ist in Kalenderwoche 26 ."
[07:42:48] [AudioServer] was asked to play a wav of 188.2 kB with id 'f2a5e3c4-1c08-40ce-868c-f83747dd1110' on site kitchen
[07:42:55] [AudioServer] finished playing wav with id 'f2a5e3c4-1c08-40ce-868c-f83747dd1110'
[07:42:55] [Tts] finished speaking with id '8a3c5a4d-1b16-4f7f-bcdb-79b9d6afc038'
[07:42:55] [Dialogue] session with id '0146c49a-d042-44ff-8332-a74ea983c796' was ended on site kitchen. The session ended as expected
[07:42:55] [Asr] was asked to stop listening on site kitchen
[07:42:55] [Hotword] was asked to toggle itself 'on' on site kitchen

Now we are done with the basic installation of a Snips satellite. In a follow-up article I will show you how to optimize the Snips Hotword service. We will activate the LED and the button of the Google AIY Voice Kit and upgrade the satellite to a full-featured jukebox. Until then have fun and keep your voice in privacy.

Lars Martin

I have been developing software for 20 years - mostly on the basis of the JVM. In the recent past I've been doing a methamorphosis towards polyglot projects. In my spare time, I enjoy Smart Home and Home Automation.

Blog Comments powered by Disqus.