···11+# CRUSH.md - Daedalus Project Guidelines
22+33+## Build Commands
44+- Setup environment: `direnv allow`
55+- Build firmware: `cd firmware && make`
66+- Flash to RP2350: `cd firmware && make flash`
77+- Disassemble: `cd firmware && make disasm`
88+- Clean build: `cd firmware && make clean`
99+- Format code: `clang-format -i firmware/src/*.cpp firmware/src/*.h`
1010+- Lint code: `cppcheck --enable=all firmware/src/`
1111+1212+## Test Commands
1313+- Run all tests: `ctest -S build`
1414+- Run single test: `ctest -R <test_name> -V`
1515+1616+## Code Style Guidelines
1717+1818+### General
1919+- Use 2-space indentation
2020+- Max line length: 100 characters
2121+- UTF-8 encoding for all files
2222+- Follow Google C++ Style Guide with modifications in .clang-format
2323+2424+### Naming Conventions
2525+- Classes: PascalCase (e.g., `LedController`)
2626+- Functions: camelCase (e.g., `initializeHardware()`)
2727+- Variables: camelCase (e.g., `ledPin`)
2828+- Constants: UPPER_SNAKE_CASE (e.g., `MAX_BUFFER_SIZE`)
2929+- Files: snake_case.ext (e.g., `led_controller.cpp`)
3030+3131+### Error Handling
3232+- Use return codes for error reporting
3333+- Check return values from SDK functions
3434+- Use assertions for invariants
3535+- Avoid dynamic memory allocation when possible
3636+3737+### Nix Development
3838+- Pico SDK is provided via nixpkgs with withSubmodules override
3939+- All tools are managed through Nix
4040+- Use `nix flake update` to update dependencies
4141+4242+## Code Style Guidelines
4343+4444+### General
4545+- Use 2-space indentation
4646+- Max line length: 100 characters
4747+- UTF-8 encoding for all files
4848+4949+### Naming Conventions
5050+- Functions/methods: camelCase
5151+- Variables: camelCase
5252+- Constants: UPPER_SNAKE_CASE
5353+- Classes: PascalCase
5454+- Files: kebab-case.ext
5555+5656+### Error Handling
5757+- Use explicit error handling (no silent failures)
5858+- Log errors with appropriate context
5959+- Return meaningful error messages
6060+6161+### Comments
6262+- Use JSDoc-style comments for functions
6363+- Keep comments current with code changes
6464+- Explain "why" not "what" in comments
6565+6666+### Git Practices
6767+- Commit messages: Start with verb in present tense
6868+- Keep commits focused on single changes
6969+- Reference issues in commit messages when applicable
+36
firmware/CMakeLists.txt
···11+cmake_minimum_required(VERSION 3.25)
22+33+# Need to include pico-sdk cmake support. Must happen before project.
44+include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
55+66+project(daedalus_firmware
77+ LANGUAGES C CXX ASM
88+ VERSION 1.0
99+)
1010+1111+set(CMAKE_C_STANDARD 11)
1212+set(CMAKE_CXX_STANDARD 17)
1313+set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Get compilation DB
1414+1515+# initialize the Raspberry Pi Pico SDK
1616+pico_sdk_init()
1717+1818+# Tell CMake where to find the executable source file
1919+add_executable(${PROJECT_NAME}
2020+ src/main.cpp
2121+)
2222+2323+# Create map/bin/hex/uf2 files
2424+pico_add_extra_outputs(${PROJECT_NAME})
2525+2626+# Link to pico_stdlib (gpio, time, etc. functions)
2727+target_link_libraries(${PROJECT_NAME}
2828+ pico_stdlib
2929+ hardware_gpio
3030+ hardware_uart
3131+ pico_multicore
3232+)
3333+3434+# Choice of available stdio outputs
3535+pico_enable_stdio_usb(${PROJECT_NAME} 1) # ~13880 extra bytes
3636+pico_enable_stdio_uart(${PROJECT_NAME} 0) # ~1176 extra bytes
+30
firmware/Makefile
···11+# Little driver makefile to make the complicated bootloader less of a hassle.
22+33+PROJECT=daedalus_firmware
44+55+TOOLCHAIN_PREFIX=arm-none-eabi-
66+77+# Check if FORCE_FLASH is set
88+PICOTOOL_OPTS = $(if $(FORCE_FLASH),-f,)
99+1010+build/$(PROJECT).uf2:
1111+1212+flash : build/$(PROJECT).uf2
1313+ picotool load $(PICOTOOL_OPTS) $<
1414+ picotool reboot
1515+1616+build/$(PROJECT).uf2 build/$(PROJECT).elf: build FORCE
1717+ $(MAKE) -C build
1818+ $(TOOLCHAIN_PREFIX)size build/$(PROJECT).elf
1919+ $(TOOLCHAIN_PREFIX)nm --print-size --size-sort --radix=d build/$(PROJECT).elf | awk '{printf("%5d %s\n", $$2, $$4);}' | sort -nr | head -20
2020+2121+disasm: build/$(PROJECT).elf
2222+ $(TOOLCHAIN_PREFIX)objdump -C -S build/$(PROJECT).elf
2323+2424+build:
2525+ cmake -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DPICO_BOARD=pico2
2626+2727+clean:
2828+ rm -rf build
2929+3030+FORCE:
+33
firmware/README.md
···11+# Daedalus Firmware
22+33+This directory contains the firmware for the RP2350 board.
44+55+## Building
66+77+1. Make sure you have the Pico SDK set up (automatically handled by the Nix flake)
88+2. Configure the build:
99+ ```
1010+ cmake -B build -S .
1111+ ```
1212+3. Build the firmware:
1313+ ```
1414+ cmake --build build
1515+ ```
1616+4. The output files will be in the `build` directory:
1717+ - `daedalus_firmware.uf2`: UF2 file for drag-and-drop programming
1818+ - `daedalus_firmware.elf`: ELF file for debugging
1919+ - `daedalus_firmware.hex`: HEX file for programming with external tools
2020+2121+## Flashing
2222+2323+Connect the RP2350 board in bootloader mode (hold BOOTSEL while connecting USB) and copy the UF2 file:
2424+2525+```
2626+cp build/daedalus_firmware.uf2 /path/to/rp2350/
2727+```
2828+2929+Or use picotool:
3030+3131+```
3232+picotool load -x build/daedalus_firmware.uf2
3333+```
+30
firmware/STYLE.md
···11+# C++ style guide for Daedalus firmware
22+33+## Formatting
44+- Use clang-format with the provided .clang-format file
55+- Run before committing: `clang-format -i firmware/src/*.cpp firmware/src/*.h`
66+77+## Naming
88+- Classes: PascalCase (e.g., `LedController`)
99+- Functions: camelCase (e.g., `initializeHardware()`)
1010+- Variables: camelCase (e.g., `ledPin`)
1111+- Constants: UPPER_SNAKE_CASE (e.g., `MAX_BUFFER_SIZE`)
1212+- Macros: UPPER_SNAKE_CASE (e.g., `DEBUG_ENABLE`)
1313+- Files: snake_case (e.g., `led_controller.cpp`)
1414+1515+## Headers
1616+- Use `#pragma once` for header guards
1717+- Order includes: standard library, Pico SDK, project headers
1818+1919+## Comments
2020+- Use Doxygen-style comments for functions and classes
2121+- Comment complex algorithms and non-obvious code
2222+2323+## Error handling
2424+- Use return codes for error reporting
2525+- Check return values from SDK functions
2626+- Use assertions for invariants
2727+2828+## Memory management
2929+- Avoid dynamic memory allocation when possible
3030+- If needed, use RAII principles with smart pointers
+65
firmware/pico_sdk_import.cmake
···11+# This file is copied from the Pico SDK
22+# It allows the CMake project to find the Pico SDK
33+44+# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
55+66+# This can be dropped into an external project to help locate the Pico SDK
77+# It should be included prior to project()
88+99+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
1010+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
1111+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
1212+endif ()
1313+1414+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
1515+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
1616+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
1717+endif ()
1818+1919+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
2020+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
2121+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
2222+endif ()
2323+2424+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
2525+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
2626+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
2727+2828+if (NOT PICO_SDK_PATH)
2929+ if (PICO_SDK_FETCH_FROM_GIT)
3030+ include(FetchContent)
3131+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
3232+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
3333+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
3434+ endif ()
3535+ FetchContent_Declare(
3636+ pico_sdk
3737+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
3838+ GIT_TAG master
3939+ )
4040+ if (NOT pico_sdk)
4141+ message("Downloading Raspberry Pi Pico SDK")
4242+ FetchContent_Populate(pico_sdk)
4343+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
4444+ endif ()
4545+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
4646+ else ()
4747+ message(FATAL_ERROR
4848+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
4949+ )
5050+ endif ()
5151+endif ()
5252+5353+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
5454+if (NOT EXISTS ${PICO_SDK_PATH})
5555+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
5656+endif ()
5757+5858+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
5959+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
6060+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
6161+endif ()
6262+6363+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
6464+6565+include(${PICO_SDK_INIT_CMAKE_FILE})
+122
firmware/src/main.cpp
···11+#include <stdlib.h>
22+33+#include <cstring>
44+55+#include "pico/multicore.h"
66+#include "pico/stdlib.h"
77+88+// Define GPIO pins for rows and columns
99+const uint ROW_PINS[4] = {12, 13, 14, 15};
1010+const uint COL_PINS[6] = {17, 18, 19, 20, 21, 22};
1111+1212+#define ROWS 4
1313+#define COLS 6
1414+1515+// LED state matrix
1616+uint8_t led_states[ROWS][COLS] = {0};
1717+1818+void setup_pins() {
1919+ // Set up row pins
2020+ for (int i = 0; i < ROWS; i++) {
2121+ gpio_init(ROW_PINS[i]);
2222+ gpio_set_dir(ROW_PINS[i], GPIO_OUT);
2323+ gpio_put(ROW_PINS[i], 0); // Start with LEDs off
2424+ }
2525+2626+ // Set up column pins
2727+ for (int j = 0; j < COLS; j++) {
2828+ gpio_init(COL_PINS[j]);
2929+ gpio_set_dir(COL_PINS[j], GPIO_OUT);
3030+ gpio_put(COL_PINS[j], 1); // Start with LEDs off
3131+ }
3232+}
3333+3434+// Function to update the display at variable frequencies
3535+void update_led_display(uint32_t refresh_rate_hz) {
3636+ static uint32_t last_update = 0;
3737+ uint32_t current_time = to_ms_since_boot(get_absolute_time());
3838+ uint32_t update_interval = 1000 / refresh_rate_hz;
3939+4040+ // Only update at the specified frequency
4141+ if (current_time - last_update < update_interval) {
4242+ return;
4343+ }
4444+4545+ last_update = current_time;
4646+4747+ // Display the current state
4848+ for (int row = 0; row < ROWS; row++) {
4949+ // Set row active
5050+ gpio_put(ROW_PINS[row], 1);
5151+5252+ // Set column states for this row
5353+ for (int col = 0; col < COLS; col++) {
5454+ // LED on = column LOW, LED off = column HIGH
5555+ gpio_put(COL_PINS[col], led_states[row][col] ? 0 : 1);
5656+ }
5757+5858+ // Small delay for this row to be visible
5959+ sleep_us(100);
6060+6161+ // Deactivate row
6262+ gpio_put(ROW_PINS[row], 0);
6363+ }
6464+}
6565+6666+// Function to set a specific LED state
6767+void set_led(int row, int col, bool state) {
6868+ if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
6969+ led_states[row][col] = state ? 1 : 0;
7070+ }
7171+}
7272+7373+// Function to set the entire matrix state
7474+void set_matrix_state(const uint8_t new_state[ROWS][COLS]) {
7575+ memcpy(led_states, new_state, sizeof(led_states));
7676+}
7777+7878+void random_state(float density) {
7979+ // clamp
8080+ if (density > 1.0f)
8181+ density = 1.0f;
8282+ if (density < 0.0f)
8383+ density = 0.0f;
8484+8585+ for (int row = 0; row < ROWS; row++) {
8686+ for (int col = 0; col < COLS; col++) {
8787+ float r = (float)rand() / (float)RAND_MAX;
8888+ led_states[row][col] = (r < density) ? 1 : 0;
8989+ }
9090+ }
9191+}
9292+9393+const uint8_t ALL_ON[ROWS][COLS] = {{1, 1, 1, 1, 1, 1},
9494+ {1, 1, 1, 1, 1, 1},
9595+ {1, 1, 1, 1, 1, 1},
9696+ {1, 1, 1, 1, 1, 1}};
9797+9898+const uint8_t ALL_OFF[ROWS][COLS] = {{0, 0, 0, 0, 0, 0},
9999+ {0, 0, 0, 0, 0, 0},
100100+ {0, 0, 0, 0, 0, 0},
101101+ {0, 0, 0, 0, 0, 0}};
102102+103103+void core1_main() {
104104+ while (true) {
105105+ update_led_display(1000);
106106+ }
107107+}
108108+109109+int main() {
110110+ stdio_init_all();
111111+ setup_pins();
112112+113113+ multicore_launch_core1(core1_main);
114114+115115+ while (true) {
116116+ random_state(0.5); // Turn random LEDs ON
117117+ // set_matrix_state(ALL_ON);
118118+ sleep_ms(200);
119119+ // set_matrix_state(ALL_OFF);
120120+ // sleep_ms(500);
121121+ }
122122+}