Skip to content
Merged
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
14 changes: 7 additions & 7 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
working-directory: src/

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand All @@ -28,11 +28,11 @@ jobs:
run: dotnet test --no-build --verbosity normal
- name: Publish
run: dotnet publish -c Release --no-build --verbosity normal
# Uploade the published artifacts

# Upload the published artifacts
- name: Upload Publish Artifact
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v4
with:
name: SpyderTallyApp
path: src/SpyderTallyControllerWebApp/bin/Release/net7.0/publish/
path: src/SpyderTallyControllerWebApp/bin/Release/net10.0/publish/
if-no-files-found: error
81 changes: 81 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rid: [linux-arm64, linux-arm]
defaults:
run:
working-directory: src/

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Restore dependencies
run: dotnet restore -r ${{ matrix.rid }}

- name: Publish self-contained single-file
run: |
dotnet publish SpyderTallyControllerWebApp/SpyderTallyControllerWebApp.csproj \
-c Release \
-r ${{ matrix.rid }} \
--self-contained \
-p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-o ../publish/${{ matrix.rid }}

- name: Package release tarball
working-directory: ${{ github.workspace }}
run: |
STAGE_DIR="spyder-tally-${{ matrix.rid }}"
mkdir -p "$STAGE_DIR/bin"

# Copy published binary
cp publish/${{ matrix.rid }}/SpyderTallyControllerWebApp "$STAGE_DIR/bin/"

# Copy config and support files
cp docs/sd_card/install.sh "$STAGE_DIR/"
cp docs/sd_card/nginx.conf "$STAGE_DIR/"
cp docs/sd_card/SpyderTallies.service "$STAGE_DIR/"
cp docs/sd_card/appConfig.json "$STAGE_DIR/"
cp docs/sd_card/deviceConfig.json "$STAGE_DIR/"

chmod +x "$STAGE_DIR/install.sh"
tar czf "spyder-tally-${{ matrix.rid }}.tar.gz" "$STAGE_DIR"

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: spyder-tally-${{ matrix.rid }}
path: spyder-tally-${{ matrix.rid }}.tar.gz
if-no-files-found: error

release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download all artifacts
uses: actions/download-artifact@v4

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
spyder-tally-linux-arm64/spyder-tally-linux-arm64.tar.gz
spyder-tally-linux-arm/spyder-tally-linux-arm.tar.gz
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*.userprefs

.vscode/
.claude/

# Build results
[Dd]ebug/
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Hi there! This repository contains source code and information for building a T


## Key Features and Overview
* Works with Spyder 200 / 300 / X20 / X80 hardware
* Works with Spyder 200 / 300 / X20 / X80 / Spyder-S hardware
* Works with every major release version of Spyder software
* Supports different servers and rules per individual tally
* Built-in web server for remote configuration and monitoring
Expand All @@ -27,12 +27,12 @@ The video below walks through the device hardware and software, providing an ove

Building your own Device
=================
All the instructions needed to get a device built and programmed are contained as a blog post on my Knighware.net website, and you can view that post [here](https://www.knighware.net/2021/10/03/building-a-spyder-tally-controller/). The post walks through the hardware needed to build your own device, the wiring for that hardware, and even configuring an SD card image containing the configured tally application.
All the instructions needed to get a device built and programmed are contained as a blog post on my Knighware.net website, and you can view that post [here](https://www.knightware.net/?p=4086). The post walks through the hardware needed to build your own device, the wiring for that hardware, and even configuring an SD card image containing the configured tally application.


Some Useful Links
=================
* [Building a Spyder Tally Controller](https://www.knighware.net/2021/10/03/building-a-spyder-tally-controller/) - Blog post on knightware.net containing instructions for building your own device
* [Building a Spyder Tally Controller](https://www.knightware.net/?p=4086) - Blog post on knightware.net containing instructions for building your own device
* [YouTube - Building a Spyder Tally Controller](https://youtu.be/8xSgcWn2_8I) - (Updated) Focuses on the updated hardware and software
* [YouTube Video - Building a Spyder Tally Controller](https://youtu.be/mBM5LXhSECg) - (Original) Covers older version of Tally controller, but contains a lot of theory and background on Spyder tech that is still very relevant
* [Spyder Client Library](https://www.nuget.org/packages/SpyderClientLibrary/) - this is the library powering communication with Spyder; useful for all kinds of projects and software applications
* [Spyder Client Library](https://www.nuget.org/packages/SpyderClientLibrary/) - this is the library powering communication with Spyder; useful for all kinds of projects and software applications
19 changes: 5 additions & 14 deletions docs/sd_card/SpyderTallies.service
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,19 @@ Description=Spyder Tally application by Derek Smithson (Knightware)
After=network.target

[Service]
# systemd will run this executable to start the service
# if /usr/bin/dotnet doesn't work, use `which dotnet` to find correct dotnet executable path
WorkingDirectory=/home/dsmithson/app
ExecStart=/home/dsmithson/.dotnet/dotnet /home/dsmithson/app/SpyderTallyControllerWebApp.dll
WorkingDirectory=/opt/spyder-tally
ExecStart=/opt/spyder-tally/SpyderTallyControllerWebApp

# to query logs using journalctl, set a logical name here
SyslogIdentifier=SpyderTallies

# Use your username to keep things simple.
# If you pick a different user, make sure dotnet and all permissions are set correctly to run the app
# To update permissions, use 'chown yourusername -R /srv/HelloWorld' to take ownership of the folder and files,
# Use 'chmod +x /srv/HelloWorld/HelloWorld' to allow execution of the executable file
User=dsmithson
# The install script will replace __USER__ with the installing user
User=__USER__

# ensure the service restarts after crashing
Restart=always
# amount of time to wait before restarting the service
# amount of time to wait before restarting the service
RestartSec=5

# This environment variable is necessary when dotnet isn't loaded for the specified user.
# To figure out this value, run 'env | grep DOTNET_ROOT' when dotnet has been loaded into your shell.
Environment=DOTNET_ROOT=/home/dsmithson/.dotnet

[Install]
WantedBy=multi-user.target
137 changes: 137 additions & 0 deletions docs/sd_card/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/bin/bash
set -euo pipefail

# Spyder Tally Controller - Install Script
# This script installs the Spyder Tally Controller application on a Raspberry Pi.
# It auto-detects architecture (arm64 vs arm32) and installs the correct binary.
#
# Usage:
# tar xzf spyder-tally-linux-arm64.tar.gz # (or linux-arm)
# cd spyder-tally-linux-arm64/ # (or linux-arm)
# sudo ./install.sh

INSTALL_DIR="/opt/spyder-tally"
SERVICE_NAME="SpyderTallies"

# --- Preflight checks ---

if [ "$(id -u)" -ne 0 ]; then
echo "Error: This script must be run as root (use sudo ./install.sh)"
exit 1
fi

# Determine the real user who invoked sudo
INSTALL_USER="${SUDO_USER:-$(whoami)}"
if [ "$INSTALL_USER" = "root" ]; then
echo "Error: Please run this script with sudo from a regular user account, not as root directly."
exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# --- Architecture detection ---

ARCH="$(uname -m)"
case "$ARCH" in
aarch64)
EXPECTED_RID="linux-arm64"
;;
armv7l|armv6l)
EXPECTED_RID="linux-arm"
;;
*)
echo "Error: Unsupported architecture '$ARCH'. Expected aarch64 (arm64) or armv7l/armv6l (arm32)."
exit 1
;;
esac

# Check that the binary in this package matches the detected architecture
if [ ! -f "$SCRIPT_DIR/bin/SpyderTallyControllerWebApp" ]; then
echo "Error: Binary not found at $SCRIPT_DIR/bin/SpyderTallyControllerWebApp"
echo ""
echo "Detected architecture: $ARCH ($EXPECTED_RID)"
echo "Make sure you downloaded the correct package for your Pi:"
echo " - Raspberry Pi 4/5 (64-bit OS): spyder-tally-linux-arm64.tar.gz"
echo " - Raspberry Pi 3 or older (32-bit OS): spyder-tally-linux-arm.tar.gz"
exit 1
fi

echo "=== Spyder Tally Controller Installer ==="
echo "Detected architecture: $ARCH ($EXPECTED_RID)"
echo "Installing as user: $INSTALL_USER"
echo "Install directory: $INSTALL_DIR"
echo ""

# --- Stop existing service if running ---

if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
echo "Stopping existing $SERVICE_NAME service..."
systemctl stop "$SERVICE_NAME"
fi

# --- Install nginx ---

echo "Installing nginx..."
apt-get update -qq
apt-get install -y -qq nginx > /dev/null

# --- Enable I2C ---

echo "Enabling I2C interface..."
if command -v raspi-config &> /dev/null; then
raspi-config nonint do_i2c 0
else
echo " Warning: raspi-config not found - skipping I2C setup."
echo " You may need to enable I2C manually if not already enabled."
fi

# --- Install application ---

echo "Installing application to $INSTALL_DIR..."
mkdir -p "$INSTALL_DIR"

# Copy binary
cp "$SCRIPT_DIR/bin/SpyderTallyControllerWebApp" "$INSTALL_DIR/"
chmod +x "$INSTALL_DIR/SpyderTallyControllerWebApp"

# Copy default config files (only if they don't already exist, to preserve user settings on upgrade)
for config_file in appConfig.json deviceConfig.json; do
if [ ! -f "$INSTALL_DIR/$config_file" ]; then
cp "$SCRIPT_DIR/$config_file" "$INSTALL_DIR/"
echo " Created default $config_file"
else
echo " Keeping existing $config_file (not overwriting)"
fi
done

# Set ownership so the service user can write config files
chown -R "$INSTALL_USER":"$INSTALL_USER" "$INSTALL_DIR"

# --- Install nginx config ---

echo "Installing nginx configuration..."
cp "$SCRIPT_DIR/nginx.conf" /etc/nginx/nginx.conf
nginx -t -q
systemctl restart nginx
systemctl enable nginx

# --- Install systemd service ---

echo "Installing systemd service..."
sed "s/__USER__/$INSTALL_USER/g" "$SCRIPT_DIR/SpyderTallies.service" > /etc/systemd/system/SpyderTallies.service
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl start "$SERVICE_NAME"

# --- Done ---

echo ""
echo "=== Installation complete! ==="
echo ""
echo "The Spyder Tally Controller is now running."
echo " Web UI: http://$(hostname -I | awk '{print $1}')"
echo ""
echo "Useful commands:"
echo " sudo systemctl status $SERVICE_NAME # Check service status"
echo " sudo systemctl restart $SERVICE_NAME # Restart the service"
echo " sudo journalctl -u $SERVICE_NAME -f # Tail the log"
39 changes: 18 additions & 21 deletions docs/sd_card/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,25 @@ http {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

# Logging Settings
# access_log /var/log/nginx/access.log;
# error_log /var/log/nginx/error.log;

# Gzip Settings
gzip on;

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

server {
listen 80 default_server;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}
# WebSocket support (required for SignalR)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 80 default_server;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}

4 changes: 2 additions & 2 deletions src/SpyderTallyControllerLinux.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
# Visual Studio Version 18
VisualStudioVersion = 18.2.11408.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpyderTallyControllerWebApp", "SpyderTallyControllerWebApp\SpyderTallyControllerWebApp.csproj", "{1E5F6F1B-9473-4A89-A69D-5014519E8002}"
EndProject
Expand Down
Loading