Skip to content

Commit 36e2740

Browse files
committed
Add devcontainer, github action firmware build
2 parents 4a8da9e + bb9a112 commit 36e2740

File tree

4 files changed

+215
-44
lines changed

4 files changed

+215
-44
lines changed

.devcontainer/Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Using Ubuntu image contents, see here for details: https://github.com/devcontainers/images/tree/main/src/base-ubuntu
2+
# Select Ubuntu version, options are "20.04" (Focal), "22.04" (Jammy) or "24.04" (noble)
3+
ARG VARIANT="24.04"
4+
5+
FROM mcr.microsoft.com/devcontainers/base:ubuntu-${VARIANT}
6+
7+
# Prevent interactive prompts during package installation
8+
ARG DEBIAN_FRONTEND=noninteractive
9+
10+
# Install required packages to build firmware and documentation
11+
RUN apt-get update \
12+
&& apt-get -y install --no-install-recommends \
13+
software-properties-common \
14+
build-essential \
15+
git \
16+
symlinks \
17+
expect \
18+
bash-completion \
19+
cmake \
20+
ninja-build \
21+
wget \
22+
&& apt-get clean \
23+
&& rm -rf /var/lib/apt/lists/*
24+
25+
# Download and install the Arm GNU toolchain
26+
ARG TOOLCHAIN_VER="12.2.rel1"
27+
ARG TOOLCHAIN_URL="https://developer.arm.com/-/media/Files/downloads/gnu/${TOOLCHAIN_VER}/binrel/arm-gnu-toolchain-${TOOLCHAIN_VER}-x86_64-arm-none-eabi.tar.xz"
28+
29+
RUN wget -q $TOOLCHAIN_URL -O /tmp/arm-gnu-toolchain.tar.xz \
30+
&& mkdir -p /opt/arm-gnu-toolchain \
31+
&& tar -xf /tmp/arm-gnu-toolchain.tar.xz -C /opt/arm-gnu-toolchain --strip-components=1 \
32+
&& rm /tmp/arm-gnu-toolchain.tar.xz \
33+
&& for tool in gcc gdb g++ size objcopy objdump nm c++filt; do \
34+
ln -s /opt/arm-gnu-toolchain/bin/arm-none-eabi-${tool} /usr/bin/arm-none-eabi-${TOOLCHAIN_VER}-${tool}; \
35+
done
36+
37+
# Add toolchain to PATH
38+
ENV PATH="/opt/arm-gnu-toolchain/bin:${PATH}"

.devcontainer/devcontainer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "S2T Env",
3+
"build": {
4+
// Path is relative to the devcontainer.json file.
5+
"dockerfile": "Dockerfile"
6+
},
7+
"runArgs": [
8+
"--privileged"
9+
],
10+
// Mount USB devices for flashing firmware
11+
"mounts": [
12+
"source=/dev,target=/dev,type=bind"
13+
],
14+
// Run commands after container is created
15+
"postCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
16+
"customizations": {
17+
"vscode": {
18+
"extensions": [
19+
"ms-vscode.cmake-tools"
20+
]
21+
}
22+
}
23+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: 'Build Firmware'
2+
3+
on:
4+
push:
5+
branches: [ master, feature/dev-container ]
6+
pull_request:
7+
branches: [ master, feature/dev-container ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
with:
18+
submodules: recursive
19+
20+
- name: Create access key header
21+
run: |
22+
cat > Core/Lib/picovoice/include/pv_access_key.h << 'EOF'
23+
#ifndef ACCESS_KEY_H
24+
#define ACCESS_KEY_H
25+
26+
// Picovoice Access Key
27+
#define PV_ACCESS_KEY "${{ secrets.PV_ACCESS_KEY }}"
28+
29+
#endif
30+
EOF
31+
32+
- name: Build Docker image
33+
run: |
34+
docker build -t s2t-builder:latest -f .devcontainer/Dockerfile .devcontainer/
35+
36+
- name: Build firmware in container
37+
run: |
38+
# Create container with source mounted
39+
docker run -d --name s2t-build \
40+
--user $(id -u):$(id -g) \
41+
-v ${{ github.workspace }}:/workspaces/Speech2Touch \
42+
-w /workspaces/Speech2Touch \
43+
s2t-builder:latest \
44+
tail -f /dev/null
45+
46+
# Configure CMake
47+
docker exec s2t-build /usr/bin/cmake \
48+
-DCMAKE_BUILD_TYPE=Release \
49+
-DCMAKE_TOOLCHAIN_FILE=/workspaces/Speech2Touch/cmake/gcc-arm-none-eabi.cmake \
50+
-S /workspaces/Speech2Touch \
51+
-B /workspaces/Speech2Touch/build/Release \
52+
-G Ninja
53+
54+
# Clean build
55+
docker exec s2t-build /usr/bin/cmake \
56+
--build /workspaces/Speech2Touch/build/Release --target clean --
57+
58+
# Build firmware
59+
docker exec s2t-build /usr/bin/cmake \
60+
--build /workspaces/Speech2Touch/build/Release --target all --
61+
62+
# List built files
63+
docker exec s2t-build ls -la /workspaces/Speech2Touch/build/Release/
64+
65+
- name: Copy artifacts from container
66+
run: |
67+
# Create build directory on host
68+
mkdir -p ${{ github.workspace }}/build/Release
69+
70+
# Copy artifacts from container to host
71+
docker cp s2t-build:/workspaces/Speech2Touch/build/Release/Speech2Touch.bin \
72+
${{ github.workspace }}/build/Release/Speech2Touch.bin
73+
docker cp s2t-build:/workspaces/Speech2Touch/build/Release/Speech2Touch.elf \
74+
${{ github.workspace }}/build/Release/Speech2Touch.elf
75+
docker cp s2t-build:/workspaces/Speech2Touch/build/Release/Speech2Touch.hex \
76+
${{ github.workspace }}/build/Release/Speech2Touch.hex
77+
docker cp s2t-build:/workspaces/Speech2Touch/build/Release/Speech2Touch.map \
78+
${{ github.workspace }}/build/Release/Speech2Touch.map
79+
80+
# Ensure correct ownership
81+
sudo chown -R $(id -u):$(id -g) ${{ github.workspace }}/build/Release/
82+
83+
# Stop and remove container
84+
docker stop s2t-build
85+
docker rm s2t-build
86+
87+
- name: Verify build artifacts
88+
run: |
89+
echo "Build artifacts on host:"
90+
ls -lah ${{ github.workspace }}/build/Release/
91+
92+
- name: Upload firmware artifact
93+
uses: actions/upload-artifact@v4
94+
with:
95+
name: speech2touch-firmware-${{ github.sha }}
96+
path: ${{ github.workspace }}/build/Release/Speech2Touch.bin
97+
if-no-files-found: error
98+
retention-days: 90
99+
100+
- name: Upload firmware artifact (latest)
101+
if: github.ref == 'refs/heads/master'
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: speech2touch-firmware-latest
105+
path: ${{ github.workspace }}/build/Release/Speech2Touch.bin
106+
if-no-files-found: error
107+
retention-days: 90

README.md

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Speech2Touch 🗣️👆
22

3-
[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/edholmes2232/Speech2Touch/actions)
3+
[![Build Firmware](https://github.com/edholmes2232/Speech2Touch/actions/workflows/build-firmware.yml/badge.svg)](https://github.com/edholmes2232/Speech2Touch/actions/workflows/build-firmware.yml)
44
[![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE.md)
55
[![Test Coverage](https://img.shields.io/badge/tests-pending-lightgrey)](https://github.com/edholmes2232/Speech2Touch/actions)
66

@@ -12,12 +12,17 @@ Based on STM32WB55, it leverages [Picovoice](https://picovoice.ai/) to process s
1212

1313
The INMP441 MEMS microphone is used for voice input.
1414

15+
**Pre-built firmware:** [Download latest](https://github.com/edholmes2232/Speech2Touch/actions/workflows/build-firmware.yml) (select latest successful run → Artifacts)
16+
17+
**Coverage:** This project has been written about on [Hackaday](https://hackaday.com/2025/09/24/coffee-by-command-the-speech2touch-voice-hack/) and [Hackster.io](https://www.hackster.io/news/espresso-yourself-with-your-voice-b83a2757f170).
18+
1519
---
1620

1721
## ☕️ Demo
1822

1923
https://github.com/user-attachments/assets/7025197e-daeb-4745-9b6d-d1ec124fa88a
2024

25+
---
2126

2227
## 🤖 Prototype Hardware
2328

@@ -27,6 +32,7 @@ https://github.com/user-attachments/assets/7025197e-daeb-4745-9b6d-d1ec124fa88a
2732

2833
The shape and orientation of the protoboard were dictated by the position of the USB ports of the Franke A600.
2934

35+
---
3036

3137
## 🏭 Hardware-In-Loop (HIL) Test
3238

@@ -40,47 +46,57 @@ https://github.com/user-attachments/assets/d8d1ce1c-74fb-45fa-a442-dd6b9ee583c0
4046
- STM32WB55 USB Dongle dev board
4147
- INMP441 microphone
4248
- Franke A600 (or compatible) touchscreen device
43-
- QT (for HIL testing)
44-
- VSCode (for debugging)
45-
- [STM32Cube for Visual Studio Code](https://www.st.com/content/st_com/en/stm32-mcu-developer-zone/software-development-tools/stm32cubevscode.html) extension
49+
- Qt (for HIL testing)
50+
- VSCode with [STM32Cube extension](https://www.st.com/content/st_com/en/stm32-mcu-developer-zone/software-development-tools/stm32cubevscode.html)
51+
- See `Dockerfile` for toolchain and package requirements
4652

4753
### 🚀 Container Build & Flash (Recommended)
48-
Coming soon...
54+
55+
1. Open in VSCode and reopen in dev container (`F1` → "Dev Containers: Reopen in Container")
56+
2. Configure and build:
57+
```bash
58+
cmake -DCMAKE_BUILD_TYPE=Release \
59+
-DCMAKE_TOOLCHAIN_FILE=/workspaces/Speech2Touch/cmake/gcc-arm-none-eabi.cmake \
60+
-S /workspaces/Speech2Touch -B /workspaces/Speech2Touch/build/Release -G Ninja
61+
cmake --build /workspaces/Speech2Touch/build/Release --target all
62+
```
63+
3. Flash `build/Release/Speech2Touch.bin` to your STM32WB55
4964

5065
### 🛠️ Manual Build & Flash
5166

52-
1. Clone this repository.
53-
2. Set up the project in VSCode using the STM32Cube extension.
54-
3. Build and flash the firmware from VSCode menus.
55-
4. Connect the device to the coffee machine via USB.
67+
1. Clone this repository
68+
2. Set up the project in VSCode using the STM32Cube extension
69+
3. Build and flash the firmware from VSCode
70+
4. Connect the device to the coffee machine via USB
5671

5772
### 🧪 HIL Testing
5873

59-
The Hardware-In-Loop test creates a QT GUI window which emulates the position of touch targets to match the Franke A600. It utilizes Linux text-to-speech utilities to trigger the device, and tests that the correct corresponding touch target is triggered.
74+
The Hardware-In-Loop test suite uses a Qt GUI to emulate the Franke A600 touchscreen layout. It leverages Linux text-to-speech utilities to trigger the device and validates that commands activate the correct touch targets.
6075

61-
1. Build the QT test suite:
62-
```
63-
$ cmake -DCMAKE_BUILD_TYPE=Test -S Speech2Touch -B Speech2Touch/build/Test -G Ninja
64-
$ cmake --build /home/ed/Projects/Speech2Touch/build/Test --target all --
65-
```
66-
2. Connect the embedded device with the latest firmware to the host PC USB port.
67-
3. Use `dmesg` to find the `/dev/input/eventX` USB input device path.
68-
4. Run automated test:
76+
1. Build the Qt test suite:
77+
```bash
78+
cmake -DCMAKE_BUILD_TYPE=Test -S Speech2Touch -B Speech2Touch/build/Test -G Ninja
79+
cmake --build Speech2Touch/build/Test --target all
6980
```
70-
$ ./build/Test/Test/hil/runner/test_full_loop --input /dev/input/event10
81+
2. Connect the embedded device with the latest firmware to the host PC USB port
82+
3. Use `dmesg` to identify the `/dev/input/eventX` USB input device path
83+
4. Run the automated test:
84+
```bash
85+
./build/Test/Test/hil/runner/test_full_loop --input /dev/input/event10
7186
```
7287

7388
---
7489

7590
## 🏗️ Architecture Overview
7691

7792
```
78-
[INMP441 microphone] → [Picovoice Speech Recognition] → [STM32WB55 MCU] → [Custom USB HID] → [Touchscreen Device]
93+
[INMP441 Microphone] → [Picovoice Speech Recognition] → [STM32WB55 MCU] → [Custom USB HID] → [Touchscreen Device]
7994
```
80-
- **Input:** Microphone captures user speech.
81-
- **Processing:** Picovoice library processes audio and extracts commands.
82-
- **Translation:** Commands are mapped to touchscreen coordinates.
83-
- **Output:** Custom USB HID packets simulate touch events on the target device.
95+
96+
- **Input:** INMP441 microphone captures audio
97+
- **Processing:** Picovoice library performs speech recognition and command extraction
98+
- **Translation:** Commands are mapped to touchscreen coordinates
99+
- **Output:** Custom USB HID packets simulate touch events
84100

85101
### 🧵 Threading
86102

@@ -100,26 +116,14 @@ sequenceDiagram
100116
end
101117
102118
activate Speech
103-
Speech->>Speech: Speech Regognition
104-
Speech->>Speech: Convert to target co-ords
119+
Speech->>Speech: Speech Recognition
120+
Speech->>Speech: Convert to Target Coords
105121
deactivate Speech
106122
107123
Speech->>Touch: Touch Coordinates
108124
Touch->>USB: USB HID Report
109-
110125
```
111126

112-
113-
114-
115-
---
116-
117-
## 🔬 HIL Testing Suite
118-
119-
- **QT-based GUI** replicates the Franke A600 touchscreen.
120-
- **Automated tests** ensure voice commands map to correct on-screen buttons.
121-
- **Continuous integration** ready.
122-
123127
---
124128

125129
## 🔮 Extending
@@ -139,14 +143,13 @@ The Picovoice precompiled binary at `Core/Lib/picovoice/libpicovoice.a` is pulle
139143
The configuration files in `Core/Lib/picovoice/include` are specifically set up for a Franke A600, including using "Franke" as the wake-word. New configuration files can be generated from the [Picovoice Console](https://console.picovoice.ai/).
140144

141145
---
142-
## ☑️ To Do
143-
- Create .devcontainer for firmware builds within a container.
144-
- Replace Dev Board + Protoboard with a PCB.
146+
147+
## ☑️ Roadmap
148+
149+
- Replace Dev Board + Protoboard with a PCB
145150
- Unit testing.
146-
- CI/CD with GitHub Actions for generating firmware, running unit tests.
147151
- Extract audio over RTT for tuning.
148-
- Decouple Franke A600 specific functionality for easier adapting of Speech2Touch to different applications.
149-
152+
- Decouple Franke A600-specific functionality for easier adapting of Speech2Touch to different applications.
150153

151154
---
152155

0 commit comments

Comments
 (0)