---
active: true
tags:
  - project
  - eg
---
 ## Goal

Create escape room control software.  Each Raspberry Pi (RPi) controls some number of wired props in the room. The room and props are referred to as the game.  The game is made up of a number of game elements which can be puzzles, lights, sound, etc..  Each element can utilize one or more props, wired and/or non-wired.
## System Diagram

![[EG-Controllers.png]]

## Tasks

- [x] Add horse nicker as a sound effect ➕ 2026-05-11 ✅ 2026-05-11
- [x] Set horse to only trigger in auto pilot if ship and axe puzzle are solved, otherwise nicker and reset after 10 seconds ➕ 2026-05-10 ✅ 2026-05-12
- [x] Set helmets to only trigger in auto pilot if knocker is solved, otherwise grunt and reset after 10 seconds ➕ 2026-05-10 ✅ 2026-05-12
- [x] Display the version number in the DMG title (use `version.json5`) ➕ 2026-04-21 ✅ 2026-05-14
- [x] Collect and display the prop controller version numbers and display in the user interface ➕ 2026-05-11 ✅ 2026-05-14
- [x] Change the drum sequence and the number of lights that illuminate ➕ 2026-05-15 ✅ 2026-05-20
- [x] Add the lock combinations to the user interface ➕ 2026-04-23 ✅ 2026-05-20
- [x] Put all locks on one tab ➕ 2026-05-22 ✅ 2026-05-30
- [ ] Add drum puzzle sequence to the UI ➕ 2026-05-22 
- [ ] Add axe puzzle order to the UI ➕ 2026-05-22 
- [ ] Add the table puzzle order to the UI ➕ 2026-05-22 
- [ ] Move all lock combinations onto one tab ➕ 2026-05-21 
- [ ] Add Postgres user for the control software ➕ 2026-05-22 
- [ ] Add tables for the Memir phrases ➕ 2026-05-22 
- [ ] Collect and display the game controller version number in the user interface ➕ 2026-05-14 
- [ ] Change the knocker sequence to match the tapping ➕ 2026-05-15 
## Archived Tasks

- [x] Change each prop controller to send the time to the next ping in seconds along with it's name ➕ 2026-04-21 ✅ 2026-05-09
- [x] Ensure prop controllers send a ping when they start ➕ 2026-04-22 ✅ 2026-05-09
- [x] Add `.pkg` generation to the `odin_ui/Justfile` with version number in file name ➕ 2026-03-20 ✅ 2026-05-09
- [x] Rename `sys_ping_interval_secs` in the server to be `initial_sys_poll_time_secs` ➕ 2026-04-21 ✅ 2026-05-09
- [x] Keep track of all next contact Instant ➕ 2026-04-21 ✅ 2026-05-10
- [x] Switch iOS build of Odin to Escape Gallery profile ➕ 2026-04-16 ✅ 2026-05-10
- [x] Add support for pre-defined messages for Memir in the user interface ➕ 2026-03-05 ✅ 2026-05-06
- [x] Create a separate stopwatch task with it's own commands; start, stop, reset ➕ 2026-02-06 ✅ 2026-02-11
- [x] Incorporate new stopwatch into auto pilot and UI ➕ 2026-02-09 ✅ 2026-03-17
- [x] Fix timer to be visible everywhere ➕ 2026-02-07 ✅ 2026-03-17
- [x] Add faster feedback when press speak button ➕ 2026-02-07 ✅ 2026-03-17
- [x] Move Memir up in the UI ➕ 2026-02-07 ✅ 2026-04-16
- [x] Update the [[Escape Gallery Control Software]] with Geno structures ➕ 2026-03-23 ✅ 2026-04-16
- [x] Swap in Geno for all messages and use MessagePack in Dart ➕ 2026-03-03 ✅ 2026-04-16
- [x] Test out using MessagePack in Dart with Geno and Rust ➕ 2026-03-03 ✅ 2026-04-16
- [x] Add `release` to the `Justfile` to build macOS package and iOS app ➕ 2026-04-16 ✅ 2026-04-21
- [x] Fix running the app in release mode on MacOS ➕ 2026-03-08 ✅ 2026-04-21
- [x] Create a tool to generate the `update` methods that copy from game command to game state where the name matches ➕ 2026-04-22 ✅ 2026-04-23
- [x] Sync up all the game command message field with corresponding state message fields ➕ 2026-04-22 ✅ 2026-04-23
- [x] Finish the `odin_game_test` service that just responds to commands by changing state ➕ 2026-04-21 ✅ 2026-04-23
- [x] Add a script to start the NATS service and the test game controller ➕ 2026-04-23 ✅ 2026-04-23
- [x] Move all `getFullState` calls into the services after connection ➕ 2026-04-23 ✅ 2026-04-26
- [x] All UI latch toggles are broken ➕ 2026-04-23 ✅ 2026-04-26
- [x] Change UI to toggle buttons to solve/unsolve puzzles ➕ 2026-04-23 ✅ 2026-04-26
- [x] Fix the bug with leaving the bellows in the fishes mouth ➕ 2026-03-05 ✅ 2026-04-26
- [x] Add the two iOS devices to the provisioning profile for Escape Gallery ➕ 2026-04-16 ✅ 2026-04-27
- [x] Create a tool to generate the `update` from prop state to prop state ➕ 2026-04-22 ✅ 2026-05-01
- [x] Fix iOS app to reconnect to the NATS service after resume ➕ 2026-04-22 ✅ 2026-05-04
- [x] Add `ClueServer`  to `odin-ctrl` ➕ 2026-02-04 ✅ 2026-02-04
- [x] Move Piper TTS code over to `odin-av2` and cut volume ➕ 2026-02-04 ✅ 2026-02-04
- [x] Add Memir text to speech UI ➕ 2026-01-16 ✅ 2026-02-04
- [x] Debug Memir end-to-end ➕ 2026-02-04 ✅ 2026-02-06
- [x] Ensure time with seconds shows when in finished state ➕ 2026-01-28 ✅ 2026-02-06
- [x] Increase power supply timer for `odin-drum` and screw down the lid ➕ 2026-01-26 ✅ 2026-02-06
- [x] Remove and drill out the holes for the knocker ➕ 2026-02-04 ✅ 2026-02-06
- [x] Ensure `odin-ctrl` restarts always (fix `.conf` file) ➕ 2026-01-28 ✅ 2026-02-06
- [x] Write test program for `odin-av2` to test TTS ➕ 2026-01-14 ✅ 2026-02-04
- [x] Fix `odin-av2` build problem ➕ 2026-01-18 ✅ 2026-01-25
- [x] Design and print caps for the knocker switches ➕ 2026-01-18 ✅ 2026-01-25
- [x] Fix the electronics in the horses head ➕ 2026-01-11 ✅ 2026-01-25
- [x] Add continuous re-start logic to all servers ➕ 2026-01-22 ✅ 2026-01-25
- [x] Fix the knocker detection algorithm ➕ 2026-01-14 ✅ 2026-01-26
- [x] Merge new knocker code into `odin-drum` ➕ 2026-01-26 ✅ 2026-01-27
- [x] Add the code to gradually turn off the sconces ➕ 2026-01-19 ✅ 2026-01-28
- [x] Wolf snarl starts to quickly after last animal head solved ➕ 2026-01-19 ✅ 2026-01-19
- [x] End sequence starts to quickly after hammer or dragon is solved ➕ 2026-01-19 ✅ 2026-01-19
- [x] Tree of life lights turn off when the helm lights are turned on or off ➕ 2026-01-19 ✅ 2026-01-19
- [x] Turn on the other light when going through the fireplace ➕ 2026-01-19 ✅ 2026-01-19
- [x] Fireplace latch extra toggles need to be faster ➕ 2026-01-19 ✅ 2026-01-19
- [x] Reduce sound effect volume ➕ 2026-01-19 ✅ 2026-01-19
- [x] Switch eyes on the berserker heads in the prop controller ➕ 2026-01-18 ✅ 2026-01-19
- [x] Change all prop controller `main.rs` and `config_file.rs` to require a config file ➕ 2026-01-18 ✅ 2026-01-19
- [x] Reduce number of times required to turn crank and make configurable ➕ 2026-01-19 ✅ 2026-01-19
- [x] Put `degrees_to_duty_cycle` in `odin_prop_util` ➕ 2026-01-19 ✅ 2026-01-19
- [x] Dragons triggered with only one half of the spear ➕ 2026-01-19 ✅ 2026-01-19
- [x] Turn off barrel puzzle after 5 minutes ➕ 2026-01-19 ✅ 2026-01-19
- [x] Make Memir's eye pulse when on ➕ 2026-01-19 ✅ 2026-01-19
- [x] Make sconces flicker ➕ 2025-08-13 ✅ 2026-01-19
- [x] Make chandelier flicker ➕ 2025-12-23 ✅ 2026-01-19
- [x] Make fireplace flicker ➕ 2025-12-23 ✅ 2026-01-19
- [x] `Off` condition must turn off cupboards lights, dragon eyes, helm eyes, hammer box light ➕ 2026-01-19 ✅ 2026-01-19
- [x] Check the `Running` room condition sets all the game states ➕ 2026-01-19 ✅ 2026-01-19
- [x] Implement the `Ready` room condition ➕ 2026-01-17 ✅ 2026-01-19
- [x] Implement the `Resetting` room condition ➕ 2026-01-17 ✅ 2026-01-19
- [x] Implement the `Finished` room condition ➕ 2026-01-17 ✅ 2026-01-19
- [x] Turn on warriors eyes when helm puzzle solved ➕ 2026-01-19 ✅ 2026-01-19
- [x] Check tree lights turn on when the fireplace opens ➕ 2026-01-19 ✅ 2026-01-19
- [x] Add sound effects into the auto-pilot; eagle, horse, bear, fish, hammer, dragons, ➕ 2026-01-18 ✅ 2026-01-19
- [x] Grand finale lightning sequence with flashing lights ➕ 2026-01-18 ✅ 2026-01-19
- [x] Add a timer to the auto-pilot tab ➕ 2026-01-19 ✅ 2026-01-19
- [x] Hook up auto-pilot logic to update the time and sconces based on the time elapsed ➕ 2026-01-19 ✅ 2026-01-19
- [x] Have fire place latch cycle 3 times in the prop controller ➕ 2026-01-19 ✅ 2026-01-19
- [x] Replace the barrel audio ➕ 2026-01-13 ✅ 2026-01-14
- [x] Test out the microphones and make sure we can hear everything ➕ 2026-01-13 ✅ 2026-01-14
- [x] Refactor to remove the timer in the controller in prep for moving timing to the auto-pilot ➕ 2026-01-14 ✅ 2026-01-14
- [x] Add margin to the LabelIconButtons ➕ 2026-01-16 ✅ 2026-01-16
- [x] Make a LabelButton widget with a fix width ➕ 2026-01-14 ✅ 2026-01-16
- [x] Switch to using `publish_json` and `publish_rmp` in `odin_ctrl` ➕ 2026-01-15 ✅ 2026-01-16
- [x] Get [piper-rs](https://github.com/thewh1teagle/piper-rs) building in container ➕ 2026-01-14 ✅ 2026-01-16
- [x] Go through all `cmd_loop`'s and move `Some(cmd) =>` logic inside the `match` ➕ 2026-01-04 ✅ 2026-01-16
- [x] Rename dragon to mantle puzzle ➕ 2026-01-11 \7        u ✅ 2026-01-16
- [x] Create new helm electronics for testing with simulator ➕ 2026-01-10 ✅ 2026-01-17
- [x] Repair electronics for the helms in `odin-tree` ➕ 2026-01-11 ✅ 2026-01-17
- [x] Implement end game and room off commands for auto-pilot ➕ 2026-01-16 ✅ 2026-01-17
- [x] Remove "Auto Pilot" toggle and add buttons for "Room Ready", "Start Game", "End Game", and "Room Off" ➕ 2026-01-14 ✅ 2026-01-17
- [x] Process, add to `assets` and upload all sound effects to `odin-av1` ➕ 2026-01-15 ✅ 2026-01-17
- [x] Write test program to set background audio volume ➕ 2026-01-15 ✅ 2026-01-18
- [x] Play sound effects and duck background audio ➕ 2026-01-11 ✅ 2026-01-18
- [x] Change color of buttons when value like radio button (add a flag to `LabelIconButton`) ➕ 2026-01-11 ✅ 2026-01-18
- [x] Hook up Ui for sound effects ➕ 2026-01-15 ✅ 2026-01-18
- [x] Implement the `Off` room condition ➕ 2026-01-17 ✅ 2026-01-18
- [x] Install new software on iPad ➕ 2026-01-18 ✅ 2026-01-19
- [x] Commit all `odin_test` to Gitea ➕ 2026-01-11 ✅ 2026-01-11
- [x] Create test program for the drum sequence ➕ 2026-01-11 ✅ 2026-01-11
- [x] Finish the coding for the drum sequence ➕ 2026-01-04 ✅ 2026-01-11
- [x] Fix sequence and write the tree on/off prop code ➕ 2025-12-29 ✅ 2026-01-11
- [x] Install new piezo drum vibration sensors ➕ 2025-08-17 ✅ 2026-01-11
- [x] Write code for `odin-drum` to detect drumming pattern ➕ 2025-12-23 ✅ 2026-01-11
- [x] Hook up knocker to auto-pilot ➕ 2025-12-26 ✅ 2026-01-11
- [x] Hook up drums to auto-pilot ➕ 2025-12-26 ✅ 2026-01-11
- [x] Hook up hammer hand to auto-pilot ➕ 2026-01-11 ✅ 2026-01-11
- [x] Hook up dragons to auto-pilot ➕ 2026-01-11 ✅ 2026-01-11
- [x] Add the timer to the auto-pilot ➕ 2025-12-23 ✅ 2026-01-13
- [x] Add UI to start background music ➕ 2025-12-23 ✅ 2026-01-11
- [x] Add UI for drums ➕ 2025-12-23 ✅ 2026-01-11
- [x] Design the case for the combined power and buck board ➕ 2025-12-23 ✅ 2026-01-11
- [x] Print cases and assemble and program the first one taking notes ➕ 2025-12-23 ✅ 2026-01-11
- [x] Add code to read the Berserker heads in `odin_tree` ➕ 2025-11-06 ✅ 2026-01-11
- [x] Add UI for Berserker heads ➕ 2025-11-12 ✅ 2026-01-11
- [x] Make the barrel puzzle audio play forever ➕ 2026-01-04 ✅ 2026-01-10
- [x] Add a switch in UI to turn on tree lights ➕ 2025-11-12 ✅ 2026-01-10
- [x] Write the helms prop code ➕ 2025-12-29 ✅ 2026-01-10
- [x] Finish the dragon prop code (reset is not working) ➕ 2026-01-03 ✅ 2026-01-10
- [x] Finish the coding for the detection of the knocker sequence ➕ 2026-01-04 ✅ 2026-01-10
- [x] Add `av1` and `av2` into System tab ➕ 2025-12-13 ✅ 2025-12-13
- [x] Create prop control software for `odin-av1` ➕ 2025-12-13 ✅ 2025-12-13
- [x] Create prop control software for `odin-av2` ➕ 2025-12-13 ✅ 2025-12-13
- [x] RFID reader for spear do not register RFID tag reliably.  Wall is likely too thick, there are metal nails or screws in the wood, RFID tags buried too deep ➕ 2025-11-08 ✅ 2025-11-13
- [x] Dagger no longer registers reliably.  RFID in wrong position or too deep.  Also, knife can be reversed so not sure which side RFID tag is on ➕ 2025-11-08 ✅ 2025-11-13
- [x] Horses right eye no longer registers light/dark - need to dismantle and debug ➕ 2025-11-08 ✅ 2025-11-13
- [x] Divide up the controllers equally between the power supplies and see if that fixes the power on problem ➕ 2025-11-08 ✅ 2025-11-13
- [x] Add horse head eye sensor code code in `odin_middle` ➕ 2025-11-04 ✅ 2025-11-06
- [x] Add short door latch opener code to `odin_left` ➕ 2025-11-05 ✅ 2025-11-06
- [x] Add long door latch opener code to `odin_left` ➕ 2025-11-05 ✅ 2025-11-06
- [x] Create `deploy-all` script ➕ 2025-11-05 ✅ 2025-11-06
- [x] Add reset UI ➕ 2025-11-08 ✅ 2025-11-08
- [x] Get fish RFID reader working with reset on `odin_left` ➕ 2025-11-04 ✅ 2025-11-08
- [x] Get bear RFID reader working with reset `odin_left` ➕ 2025-11-04 ✅ 2025-11-08
- [x] Get bird RFID reader working `odin_middle` ➕ 2025-11-04 ✅ 2025-11-08
- [x] Get horse eye light sensors working `odin_middle` ➕ 2025-11-04 ✅ 2025-11-08
- [x] Change `PROP_SYSTEM_STATE_SUBJECT` to `prop.system.state` and re-deploy all prop controllers ➕ 2025-11-04 ✅ 2025-11-08
- [x] Fix the broken sensors on the barrel puzzle ➕ 2025-11-04 ✅ 2025-11-08
- [x] Add UI for the knocker puzzle; knocker, long door ➕ 2025-11-08 ✅ 2025-11-08
- [x] Get knocker door opener working ➕ 2025-11-08 ✅ 2025-11-08
- [x] Add UI for drum puzzle; tree, sensors, short door ➕ 2025-11-08 ✅ 2025-11-08
- [x] Get the drum door opener working ➕ 2025-11-08 ✅ 2025-11-08
- [x] Add UI for the helm puzzle; helmets, hammer chest ➕ 2025-11-08 ✅ 2025-11-08
- [x] Get helm puzzle chest opening ➕ 2025-11-08 ✅ 2025-11-08
- [x] Add UI for `berserker_puzzle.` ➕ 2025-11-06 ✅ 2025-11-09
- [x] Rename `odin_prop` to `odin_prop_msg` ➕ 2025-11-08 ✅ 2025-11-09
- [x] Create a new `odin_prop_util` for `helpers.rs` contents and remove all helpers.rs files ➕ 2025-11-08 ✅ 2025-11-09
- [x] Tidy up dragon puzzle UI ➕ 2025-11-11 ✅ 2025-11-11
- [x] Get spear hooked up to UI ➕ 2025-11-11 ✅ 2025-11-11
- [x] Create a generic to `fn` for receiving `Cmd` messages and do a `debug!` in prop controllers like in `odin_ctrl` ➕ 2025-11-08 ✅ 2025-11-12
- [x] Generate a Dart file for `SvgPicture` in a service and register ➕ 2025-11-05 ✅ 2025-11-12
- [x] Get hammer UI working; hand ➕ 2025-11-08 ✅ 2025-11-12
- [x] Get table puzzle UI coded ➕ 2025-10-18 ✅ 2025-10-31
- [x] Get ship puzzle UI coded ➕ 2025-10-26 ✅ 2025-10-31
- [x] Change timer UI to be a slider ➕ 2025-10-25 ✅ 2025-10-31
- [x] Add a [Slider](https://api.flutter.dev/flutter/material/Slider-class.html) with discrete values and allow setting ➕ 2025-10-26 ✅ 2025-10-31
- [x] Get fireplace puzzle UI coded ➕ 2025-10-26 ✅ 2025-10-31
- [x] Get barrel puzzle UI coded ➕ 2025-10-31 ✅ 2025-10-31
- [x] Configure laptop so I can build and install control software on iPad ➕ 2025-10-31 ✅ 2025-11-01
- [x] Figure out how to set macOS menu and window to **Odin** ➕ 2025-10-31 ✅ 2025-11-01
- [x] Hook lighting up end-to-end ➕ 2025-10-31 ✅ 2025-11-01
- [x] Create a `label_switch` widget with a ✅ 2025-11-01
- [x] Use `label_switch` everywhere for consistent sizing of label plus switch ➕ 2025-11-01 ✅ 2025-11-01
- [x] Debug the axe puzzle ➕ 2025-11-01 ✅ 2025-11-01
- [x] Create a `PositionedAxe` widget with a `bool?` for the state ➕ 2025-11-01 ✅ 2025-11-01
- [x] Refactor `odin-ctrl` to pass discrete `bool?` back to UI for axes from controller ➕ 2025-11-01 ✅ 2025-11-02
- [x] Split lighting into throne and feast rooms ➕ 2025-10-31 ✅ 2025-11-02
- [x] Refactor controller to use `include!` to avoid extra module ➕ 2025-11-02 ✅ 2025-11-02
- [x] Hook table puzzle up end-to-end ➕ 2025-11-01 ✅ 2025-11-02
- [x] Hook ship puzzle up end-to-end ➕ 2025-10-31 ✅ 2025-11-02
- [x] Make `NavigationRail` scrollable ➕ 2025-11-04 ✅ 2025-11-04
- [x] Hook up the barrel puzzle end-to-end ➕ 2025-11-02 ✅ 2025-11-04
- [x] Put the `Stack` elements in a container that is sized from the biggest SVG ➕ 2025-11-04 ✅ 2025-11-04
- [x] Remove the numbers from the `axeFrame` ➕ 2025-11-04 ✅ 2025-11-04
- [x] Fix the system status code in the controller. ➕ 2025-11-04 ✅ 2025-11-04
- [x] Add fireplace latch pin code ➕ 2025-11-04 ✅ 2025-11-04
- [x] Get bird RFID reader working ➕ 2025-11-04 ✅ 2025-11-05
- [x] Add chest latch opener code ➕ 2025-11-05 ✅ 2025-11-05
- [x] Break the messages to/from the prop controllers into a separate library ➕ 2025-09-28 ✅ 2025-09-28
- [x] Switch to using `MessagePacker` cr for message transmission ➕ 2025-09-28 ✅ 2025-09-28
- [x] Just add the message format header ➕ 2025-09-28 ✅ 2025-09-28
- [x] Test the new `odin_axe` code ➕ 2025-09-28 ✅ 2025-09-28
- [x] Merge changes back into `rust_prop_quickstart` ➕ 2025-09-28 ✅ 2025-09-28
- [x] Add `odin_prop` to `odin_ctrl` ➕ 2025-09-28 ✅ 2025-10-03
- [x] Create `AxePuzzle` object and hold onto the handle in the `Server` object ➕ 2025-09-28 ✅ 2025-10-03
- [x] Create a new new Flutter app for control ➕ 2025-04-21 ✅ 2025-10-03
- [x] Add `dummy` state into `odin_prop` to support `rust_prop_quickstart` ➕ 2025-09-28 ✅ 2025-10-03
- [x] Add command to force full state to be sent to controller ➕ 2025-10-03 ✅ 2025-10-03
- [x] Send full state command when controller starts ➕ 2025-10-03 ✅ 2025-10-03
- [x] Request full state when the UI starts up or when the puzzle button is clicked on left ➕ 2025-10-03 ✅ 2025-10-04
- [x] Whiteboard the control software design ➕ 2024-11-26 ✅ 2025-04-25
- [x] Finish Sketch from a plan view of the room ➕ 2025-02-16 ✅ 2025-08-13
- [x] Configure static IP for `eg-1` on `rm-1.escapegallery.net` ✅ 2025-09-08
- [x] Configure DHCP & DNS via `dnsmasq` on `eg-1` ➕ 2025-08-06 ✅ 2025-09-08
- [x] Configure wired connection for `odin-axe` ➕ 2025-08-13 ✅ 2025-09-08
- [x] Configure `/etc/hosts` on `eg-1` to add `eg-1.rm-1.escapegallery.net` ➕ 2025-09-08 ✅ 2025-09-09
- [x] Ensure that we can `ssh` over the `10.10.0.0/24` network ➕ 2025-09-08 ✅ 2025-09-09
- [x] Configure `nftables` for the `rm-1` network to NAT packets ➕ 2025-09-08 ✅ 2025-09-09
- [x] Temporarily take down `odin-axe` WiFi and check that updates work ➕ 2025-09-09 ✅ 2025-09-09
- [x] Set timeouts for DHCP on `odin-axe` and reboot ➕ 2025-09-09 ✅ 2025-09-09
- [x] Disable the WiFi permanently on RPi for `odin-axe` and test reboot ➕ 2025-09-08 ✅ 2025-09-09
- [x] Add `env_logger` to `odin_axe` and remove existing logger ➕ 2025-09-11 ✅ 2025-09-11
- [x] Add a command line to indicated the name of the NATS server ➕ 2025-09-11 ✅ 2025-09-11
- [x] Add Ctrl+C handling ➕ 2025-09-11 ✅ 2025-09-12
- [x] Add a `select` loop that listens on a `flume` channel for messages from the monitor thread ➕ 2025-09-11 ✅ 2025-09-12
- [x] Add the thread to monitor the state of the readers ➕ 2025-09-11 ✅ 2025-09-12
- [x] Add `flume` and send a message when axe state changes to main loop ➕ 2025-09-11 ✅ 2025-09-12
- [x] Remove the checks for correctness  just record the UID from the RFID chips ➕ 2025-09-11 ✅ 2025-09-12
- [x] Add `serde` and `serde_json` to the project ➕ 2025-09-11 ✅ 2025-09-12
- [x] Kill the GPIO thread when Ctrl+C is hit ➕ 2025-09-12 ✅ 2025-09-12
- [x] Add a `HashMap<String, Value>` to track prop state ➕ 2025-09-11 ✅ 2025-09-12
- [x] If the value has change, update the `HashMap` ➕ 2025-09-12 ✅ 2025-09-12
- [x] Publish nats message when state changes ➕ 2025-09-12 ✅ 2025-09-12
- [x] Run setup scripts on `odin-throne` and `odin-feast` ➕ 2025-09-13 ✅ 2025-09-13
- [x] Reverse `odin-throne` and `odin-feast` host names ➕ 2025-09-13 ✅ 2025-09-13
- [x] Add `odin` account to `/etc/nats-server.conf` on `eg-1` ➕ 2025-09-13 ✅ 2025-09-13
- [x] Install and configure [`lld` linker and test](https://users.rust-lang.org/t/linking-taking-an-inordinately-long-time/39253/4) ➕ 2025-09-13 ✅ 2025-09-14
- [x] Fix `odin-drum` ethernet connection ➕ 2025-09-15 ✅ 2025-09-19
- [x] Set up quick-start to cross compile on mac and then copy to specific machine using `Justfile` ➕ 2025-09-13 ✅ 2025-09-19
- [x] Put new SD card in `odin-ship` ➕ 2025-09-15 ✅ 2025-09-25
- [x] Configure SD card in `odin-ship` ➕ 2025-09-25 ✅ 2025-09-25
- [x] Take `odin-ship` off of WiFi ➕ 2025-09-25 ✅ 2025-09-26
- [x] Create a service file template for prop controllers ➕ 2025-09-13 ✅ 2025-09-26
- [x] Make `cmd` messages set the `prop` state and publish ➕ 2025-09-13 ✅ 2025-09-26
- [x] Update the `HashMap` when the state of the axes changes ➕ 2025-09-11 ✅ 2025-09-26
- [x] Create script to turrn off WiFi for a prop controller ➕ 2025-09-08 ✅ 2025-09-26
- [x] Refactor code to put stuff into `self` methods ➕ 2025-09-26 ✅ 2025-09-26
- [x] Ensure prop servers output version number when starting ➕ 2025-09-25 ✅ 2025-09-26
- [x] Increment patch number when doing deployments ➕ 2025-09-25 ✅ 2025-09-26
- [x] Write out name of controller from the `clap` information as first log message ➕ 2025-09-13 ✅ 2025-09-26
- [x] Add code to reset the latch after configurable amount of time (1 sec default) ➕ 2025-09-26 ✅ 2025-09-26
- [x] Debug why the `single_instance` code is not working! ➕ 2025-09-25 ✅ 2025-09-26
- [x] Merge common changes into `rust_prop_quickstart` ➕ 2025-09-26 ✅ 2025-09-26
- [x] Create the `odin-ctrl` project ➕ 2025-09-14 ✅ 2025-09-27
- [x] Install `odin-ctrl` on `eg-1` and setup to build it there ➕ 2025-09-14 ✅ 2025-09-27
- [x] `odin-axe` ✅ 2025-10-05
- [x] Fix `dnsmasq` issue on `eg-1` ➕ 2025-10-05 ✅ 2025-10-05
- [x] Install [Caddy]() on `eg-1` ➕ 2025-10-05 ✅ 2025-10-05
- [x] Create feast room lighting page ➕ 2025-10-05 ✅ 2025-10-23
- [x] Take `odin-av` off of WiFi ➕ 2025-09-15 ✅ 2025-10-23
- [x] Test that `rust-analyzer` in VS Code works with just the `settings.json` value set ➕ 2025-10-23 ✅ 2025-10-23
- [x] Remove all `config.toml` files ➕ 2025-10-23 ✅ 2025-10-23
- [x] Set `RUSTFLAGS` environment variable for builds in the Justfile and test ➕ 2025-10-23 ✅ 2025-10-23
- [x] Fix prop control install scripts ➕ 2025-10-23 ✅ 2025-10-23
- [x] Move `odin-ctrl` install and deploy scripts to `Justfile` ➕ 2025-10-23 ✅ 2025-10-23
- [x] Ensure all prop controllers send actual state after initialization ➕ 2025-10-19 ✅ 2025-10-23
- [x] Add icons for macOS and web ➕ 2025-10-19 ✅ 2025-10-24
- [x] Make sure each dashboard tab requests state when it loads ➕ 2025-10-23 ✅ 2025-10-24
- [x] Get `odin-axe` controller ping working end-to-end ➕ 2025-10-18 ✅ 2025-10-25
- [x] Send the system state on a timer from `odin-ctrl` instead of as it comes in ➕ 2025-10-25 ✅ 2025-10-25
- [x] Make prop controller status update time a config setting ➕ 2025-10-25 ✅ 2025-10-25
- [x] Add prop controller status for all other prop controllers ➕ 2025-10-24 ✅ 2025-10-26
- [x] Ensure `odin-ctrl` requests full state from all prop controllers 3 seconds after it starts ➕ 2025-10-18 ✅ 2025-10-26
- [x] Label the unused switch that now lights the table ➕ 2025-10-26 ✅ 2025-10-26
- [x] Put a `GridView` in the lighting tab ➕ 2025-10-26 ✅ 2025-10-29
- [x] Remove the arrays from the Lighting controller messages and use named fields ➕ 2025-10-29 ✅ 2025-10-30
- [x] Remove the chandelier solenoid out of the lighting tab ➕ 2025-10-20 ✅ 2025-10-30
- [x] Add unique nav bar icons for all the puzzles ➕ 2025-11-02 ✅ 2025-11-13
- [x] Create `AsyncAwaiter` widget centered [`flutter_spinkit`](https://pub.dev/packages/flutter_spinkit) spinner and message ➕ 2025-10-30 ✅ 2025-11-13
- [x] Position the drums ➕ 2025-11-13 ✅ 2025-11-13
- [x] Add code to drive tree lights in `odin_tree` ➕ 2025-11-06 ✅ 2025-11-13
- [x] Move code into the `odin_prop_util` from `odin_prop_msg` ➕ 2025-12-09 ✅ 2025-12-09
- [x] Fix the `Justfile` to label builds correctly ➕ 2025-12-09 ✅ 2025-12-09
- [x] Configure scripting on `odin-ctrl` ➕ 2025-12-09 ✅ 2025-12-09
- [x] Add code to receive commands from script and forward ➕ 2025-12-09 ✅ 2025-12-13
- [x] Update prop and game controllers ➕ 2025-12-13 ✅ 2025-12-13
- [x] Copy auto pilot code over from the simulator ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot UI in the app ➕ 2025-12-13 ✅ 2025-12-23
- [x] Get auto pilot triggering the axe puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the table puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the ship puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the bird puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the horse puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the bear puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the fish puzzle ➕ 2025-12-14 ✅ 2025-12-23
- [x] Get auto pilot triggering the fireplace ➕ 2025-12-14 ✅ 2025-12-23
- [x] Test EG room cameras ➕ 2025-07-06 ✅ 2025-12-23
- [x] Test EG speakers  with `audio-forwarder` ➕ 2025-09-15 ✅ 2025-12-23
- [x] Test Memir speakers with `audio-forwarder` ➕ 2025-10-23 ✅ 2025-12-23
- [x] Test EG microphone with `audio-forwarder` ➕ 2025-09-15 ✅ 2025-12-23
- [x] Get crank count code working on `odin_right` ➕ 2025-11-05 ✅ 2025-12-23
- [x] Add the crank count to the fireplace UI ➕ 2025-10-31 ✅ 2025-12-23
- [x] Add `room_on` state to the `game.controller.state` message and set when all prop controllers are alive ➕ 2025-10-20 ✅ 2025-12-23
- [x] Update UI based on `room_on` state ➕ 2025-10-20 ✅ 2025-12-23
- [x] Re-install the knocker in the wall ➕ 2025-12-26 ✅ 2025-12-26
## Notes

Debug msgpack encoded messages on NATS using:

```sh
nats sub --translate 'mpk --to-json' prop.<prop-host>.state
```

You can also create a debug build of the the prop controller that uses [`to_vec_named`](https://docs.rs/rmp-serde/1.3.0/rmp_serde/encode/fn.to_vec_named.html) to add field names to the structure.
## References

![[odin_left_middle_right.png.jpeg]]
- [NATS](https://github.com/nats-io/nats.ws) 
- [[Prop and Game Controller Raspberry Pi Configuration]]
- [Use Rhai for Scripting in Rust](https://crates.io/crates/rhai)
- [`msgpack-cli`](https://crates.io/crates/msgpack-cli)
- [Flutter Drawer](https://docs.flutter.dev/cookbook/design/drawer) 
- [flutter_svg](https://pub.dev/packages/flutter_svg)


## System Ping 

- The room is expected to be started in the following order - game controller then prop controllers and user interface.
- When a prop controller starts it will connect to the NATS server, and once successful will immediately send a system state message, updating the game controllers alive/dead state for that prop controller.
- The prop controller system state ping interval defaults to 60 seconds.
- The system service on the game controller will send a request for full state to all prop controllers when it starts.  This is to handle situations where the room is running and the game controller needs to be restarted.
- The system service keeps a list of the last contact time for each of prop controllers.  This time is updated each time a prop controller sends a system state message.
- Every 60 seconds (by default) the system service goes through the contact times and checks to see if the last contact time from any prop controller is greater than twice that, i.e. 120 seconds.  If so it marks the prop controller as dead.
- The system server will only send out changes to the alive/dead status of systems if anything changes to avoid unnecessary network traffic. 
