aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-05-25 18:38:03 +0200
committerLoek Le Blansch <loek@pipeframe.xyz>2024-05-25 18:38:03 +0200
commit36a23cfeb565446572c84d22a8be8e2e01c3c3e0 (patch)
treee5a88b98fd783861b8ea6146543415289f940732
parent4525f60f29359b7ba88e47880d79fb9869913656 (diff)
parent078038da762d7f64ae07cf416a2a08dddfc0c651 (diff)
Merge branch 'master' into wip/main-controller
-rw-r--r--.editorconfig12
-rw-r--r--puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino95
-rw-r--r--puzzle/neo/console-neopuzzle/neo.cpp100
-rw-r--r--puzzle/vault/arduino-vaultpuzzle/arduino-vaultpuzzle.ino150
-rw-r--r--puzzle/vault/console-vaultpuzzle/vault.cpp130
-rw-r--r--readme.md26
6 files changed, 513 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..cd37156
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+insert_final_newline = true
+
+[*.md]
+indent_style = space
+indent_size = 2
+
+
diff --git a/puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino b/puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino
new file mode 100644
index 0000000..b334677
--- /dev/null
+++ b/puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino
@@ -0,0 +1,95 @@
+#include <Wire.h>
+#include <Adafruit_NeoTrellis.h>
+
+#define MATRIX_SIZE 8
+#define LED_COLOR_ON 0xFFFFFF // Color of the LEDs in ON state
+#define LED_COLOR_OFF 0x000000 // Color of the LEDs in OFF state
+
+Adafruit_NeoTrellis t_array[MATRIX_SIZE / 4][MATRIX_SIZE / 4] = {
+ {Adafruit_NeoTrellis(0x2E), Adafruit_NeoTrellis(0x2F)},
+ {Adafruit_NeoTrellis(0x30), Adafruit_NeoTrellis(0x32)}
+};
+
+Adafruit_MultiTrellis trellis((Adafruit_NeoTrellis *)t_array, MATRIX_SIZE / 4, MATRIX_SIZE / 4);
+
+bool neoMatrix[MATRIX_SIZE][MATRIX_SIZE]; // To track state of each pixel
+
+enum NeoState {
+ NEO_UNINITIALIZED,
+ NEO_PLAYING,
+ NEO_SOLVED
+};
+
+NeoState neoState = NEO_UNINITIALIZED;
+
+void setup() {
+ Serial.begin(115200);
+ while (!Serial); // Wait for Serial to be ready
+
+ if (!trellis.begin()) {
+ Serial.println("Failed to initialize NeoTrellis");
+ while (1) delay(1);
+ }
+
+ // Initialize the matrix with a checkerboard pattern
+ bool toggle = false;
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ for (int j = 0; j < MATRIX_SIZE; j++) {
+ neoMatrix[i][j] = toggle;
+ toggle = !toggle;
+ trellis.setPixelColor(i * MATRIX_SIZE + j, neoMatrix[i][j] ? LED_COLOR_ON : LED_COLOR_OFF);
+ }
+ toggle = !toggle;
+ }
+ trellis.show();
+ neoState = NEO_PLAYING;
+
+ // Register the callback for each key
+ for (int i = 0; i < MATRIX_SIZE * MATRIX_SIZE; i++) {
+ trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING, true);
+ trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING, true);
+ trellis.registerCallback(i, buttonCallback);
+ }
+}
+
+void loop() {
+ trellis.read(); // Process button events
+ delay(20);
+}
+
+TrellisCallback buttonCallback(keyEvent evt) {
+ int x = evt.bit.NUM / MATRIX_SIZE;
+ int y = evt.bit.NUM % MATRIX_SIZE;
+
+ if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
+ toggleAdjacentLEDs(x, y);
+ trellis.show();
+ if (isNeoPuzzleSolved()) {
+ neoState = NEO_SOLVED;
+ Serial.println("The NeoTrellis puzzle is solved!");
+ }
+ }
+ return 0;
+}
+
+void toggleAdjacentLEDs(int x, int y) {
+ for (int dx = -1; dx <= 1; ++dx) {
+ for (int dy = -1; dy <= 1; ++dy) {
+ if (dx == 0 && dy == 0) continue; // Skip the center button itself
+ int nx = x + dx, ny = y + dy;
+ if (nx >= 0 && nx < MATRIX_SIZE && ny >= 0 && ny < MATRIX_SIZE) {
+ neoMatrix[nx][ny] = !neoMatrix[nx][ny];
+ trellis.setPixelColor(nx * MATRIX_SIZE + ny, neoMatrix[nx][ny] ? LED_COLOR_ON : LED_COLOR_OFF);
+ }
+ }
+ }
+}
+
+bool isNeoPuzzleSolved() {
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ for (int j = 0; j < MATRIX_SIZE; j++) {
+ if (neoMatrix[i][j]) return false; // If any LED is on, puzzle is not solved
+ }
+ }
+ return true;
+}
diff --git a/puzzle/neo/console-neopuzzle/neo.cpp b/puzzle/neo/console-neopuzzle/neo.cpp
new file mode 100644
index 0000000..56d90f7
--- /dev/null
+++ b/puzzle/neo/console-neopuzzle/neo.cpp
@@ -0,0 +1,100 @@
+#include <iostream>
+#include <array>
+
+#define MATRIX_SIZE 8
+
+enum NeoState {
+ NEO_UNINITIALIZED,
+ NEO_PLAYING,
+ NEO_SOLVED
+};
+
+// Simulate the 8x8 LED matrix with a 2D array
+std::array<std::array<bool, MATRIX_SIZE>, MATRIX_SIZE> neoMatrix;
+
+NeoState neoState = NEO_UNINITIALIZED;
+
+// Helper function to toggle LEDs if within bounds
+void toggleIfValid(int x, int y) {
+ if (x >= 0 && x < MATRIX_SIZE && y >= 0 && y < MATRIX_SIZE) {
+ neoMatrix[x][y] = !neoMatrix[x][y];
+ }
+}
+
+void initializeNeoMatrix() {
+ // The initial pattern from the Appendix A example (assuming red is 'true'/on and white is 'false'/off)
+ std::array<std::array<bool, MATRIX_SIZE>, MATRIX_SIZE> initialPattern = {{
+ {false, true, false, true, false, true, false, true},
+ {true, false, true, false, true, false, true, false},
+ {false, true, false, true, false, true, false, true},
+ {true, false, true, false, true, false, true, false},
+ {false, true, false, true, false, true, false, true},
+ {true, false, true, false, true, false, true, false},
+ {false, true, false, true, false, true, false, true},
+ {true, false, true, false, true, false, true, false}
+ }};
+
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ for (int j = 0; j < MATRIX_SIZE; j++) {
+ neoMatrix[i][j] = initialPattern[i][j];
+ }
+ }
+
+ neoState = NEO_PLAYING;
+}
+
+
+void printNeoMatrix() {
+ // Print the matrix state to the console
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ for (int j = 0; j < MATRIX_SIZE; j++) {
+ std::cout << (neoMatrix[i][j] ? 1 : 0) << " ";
+ }
+ std::cout << std::endl;
+ }
+}
+
+void toggleAdjacentLEDs(int x, int y) {
+ // Toggle the LED at (x, y) and adjacent LEDs
+ toggleIfValid(x, y); // Center
+ toggleIfValid(x - 1, y); // Up
+ toggleIfValid(x + 1, y); // Down
+ toggleIfValid(x, y - 1); // Left
+ toggleIfValid(x, y + 1); // Right
+}
+
+
+bool isNeoPuzzleSolved() {
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ for (int j = 0; j < MATRIX_SIZE; j++) {
+ if (neoMatrix[i][j]) return false; // If any LED is on, puzzle is not solved
+ }
+ }
+ return true;
+}
+
+/// Integration needed
+int main() {
+ initializeNeoMatrix();
+ printNeoMatrix();
+
+ while (neoState != NEO_SOLVED) {
+ int x, y;
+ std::cout << "Enter the coordinates of the button pressed (x y): ";
+ std::cin >> x >> y;
+
+ if (x >= 0 && x < MATRIX_SIZE && y >= 0 && y < MATRIX_SIZE) {
+ toggleAdjacentLEDs(x, y);
+ printNeoMatrix();
+
+ if (isNeoPuzzleSolved()) {
+ neoState = NEO_SOLVED;
+ std::cout << "The NeoTrellis puzzle is solved!\n";
+ }
+ } else {
+ std::cout << "Invalid coordinates. Please enter values between 0 and " << MATRIX_SIZE - 1 << ".\n";
+ }
+ }
+
+ return 0;
+}
diff --git a/puzzle/vault/arduino-vaultpuzzle/arduino-vaultpuzzle.ino b/puzzle/vault/arduino-vaultpuzzle/arduino-vaultpuzzle.ino
new file mode 100644
index 0000000..4dd8ac8
--- /dev/null
+++ b/puzzle/vault/arduino-vaultpuzzle/arduino-vaultpuzzle.ino
@@ -0,0 +1,150 @@
+#include <Wire.h>
+#include <TM1637Display.h>
+
+// Definitions for GPIO numbers, change these according to your hardware setup
+#define TOTAL_LEVELS 5
+#define TAG "VaultPuzzle"
+
+// Key Matrix Pin Configuration
+#define ROWS 4
+#define COLS 3
+
+// Module connection pins (Digital Pins for TM1637)
+#define CLK 2
+#define DIO 3
+
+// Pin to indicate puzzle solved state
+#define SOLVED_PIN 53
+
+// Initialize the TM1637 display
+TM1637Display display(CLK, DIO);
+
+//TODO Update these pin numbers based on your Arduino setup
+const int ROW_PINS[ROWS] = {7, 6, 5, 4};
+const int COL_PINS[COLS] = {10, 9, 8};
+
+typedef enum {
+ STATE_UNINITIALIZED = 0x00,
+ STATE_RESET = 0x01,
+ STATE_PLAYING = 0x02,
+ STATE_SOLVED = 0x03,
+ STATE_ERROR = 0x04
+} PuzzleState;
+
+const char* validButtons[TOTAL_LEVELS] = {"A2", "B1", "D3", "C2", "C1"};
+PuzzleState puzzleState = STATE_UNINITIALIZED;
+int currentLevel = 0;
+
+// Function prototypes
+void display_code(int level);
+void initialize_system();
+void check_button_press();
+void update_state_after_button_press(bool validPress);
+void play_error_sound();
+void blink_display();
+
+void setup() {
+ Serial.begin(115200); // Initialize default Serial for debug messages
+ pinMode(SOLVED_PIN, OUTPUT); // Initialize the solved indicator pin
+ digitalWrite(SOLVED_PIN, LOW); // Start with the solved pin LOW
+
+ display.setBrightness(0x0f); // Set the brightness of the TM1637 display
+ initialize_system();
+ Serial.println("GPIO and display initialized.");
+
+ // Test to light up all segments
+ uint8_t allOn[] = {0xFF, 0xFF, 0xFF, 0xFF}; // All segments on
+ display.setSegments(allOn);
+ delay(2000); // Keep it on for 2 seconds before proceeding
+
+ // Initialize the game
+ if (true) { // Simulating isVaultClosed
+ puzzleState = STATE_RESET;
+ currentLevel = 0;
+ display_code(currentLevel);
+ } else {
+ Serial.println("Vault door is open. Please close the door to start the puzzle.");
+ }
+}
+
+void initialize_system() {
+ // Configure the rows as input with pull-up
+ for (int i = 0; i < ROWS; i++) {
+ pinMode(ROW_PINS[i], INPUT_PULLUP);
+ }
+
+ // Configure the columns as output
+ for (int i = 0; i < COLS; i++) {
+ pinMode(COL_PINS[i], OUTPUT);
+ digitalWrite(COL_PINS[i], HIGH);
+ }
+}
+
+void loop() {
+ while (puzzleState != STATE_SOLVED) {
+ check_button_press();
+ delay(100); // Non-blocking delay
+ }
+ // When puzzle is solved, you might want to display a final message and set the solved pin high
+ if (puzzleState == STATE_SOLVED) {
+ digitalWrite(SOLVED_PIN, HIGH); // Set the solved pin high
+ display.showNumberDec(currentLevel, true); // Show final level or a special message
+ Serial.println("Final display shown. Puzzle complete.");
+ while (1) { delay(1000); } // Hold on the final display
+ }
+}
+
+void display_code(int level) {
+ Serial.print("Displaying code for level "); Serial.println(level);
+ // Display the level on the TM1637 4-digit 7-segment display
+ display.showNumberDec(level, true); // True to show leading zeros
+ Serial.print("Code for level "); Serial.print(level); Serial.println(" displayed successfully.");
+}
+
+void check_button_press() {
+ char keyPress[3] = {0};
+ for (int col = 0; col < COLS; col++) {
+ digitalWrite(COL_PINS[col], LOW); // Activate column
+ for (int row = 0; row < ROWS; row++) {
+ if (digitalRead(ROW_PINS[row]) == LOW) { // Detect if any row is activated
+ delay(50); // Debounce delay
+ if (digitalRead(ROW_PINS[row]) == LOW) { // Confirm the button is still pressed
+ keyPress[0] = 'A' + row;
+ keyPress[1] = '1' + col;
+ keyPress[2] = '\0';
+ Serial.print("Keypress detected: "); Serial.println(keyPress);
+ if (strcmp(keyPress, validButtons[currentLevel]) == 0) {
+ currentLevel++;
+ if (currentLevel >= TOTAL_LEVELS) {
+ puzzleState = STATE_SOLVED;
+ Serial.println("Puzzle solved!");
+ display.showNumberDec(currentLevel + 1, true); // Display the final level
+ digitalWrite(SOLVED_PIN, HIGH); // Set the solved pin high
+ } else {
+ puzzleState = STATE_PLAYING;
+ display_code(currentLevel);
+ }
+ } else {
+ play_error_sound();
+ blink_display();
+ puzzleState = STATE_ERROR;
+ currentLevel = 0;
+ display_code(currentLevel);
+ }
+ while (digitalRead(ROW_PINS[row]) == LOW) {} // Wait for release
+ }
+ }
+ }
+ digitalWrite(COL_PINS[col], HIGH); // Deactivate column
+ }
+}
+
+void play_error_sound() {
+ // Simulate error sound - connect a buzzer to play actual sound
+ Serial.println("Playing error sound.");
+}
+
+void blink_display() {
+ // Simulate blinking the display - use LEDs or other methods to show visual feedback
+ Serial.println("7-segment display is blinking to indicate an error.");
+}
diff --git a/puzzle/vault/console-vaultpuzzle/vault.cpp b/puzzle/vault/console-vaultpuzzle/vault.cpp
new file mode 100644
index 0000000..3566b3e
--- /dev/null
+++ b/puzzle/vault/console-vaultpuzzle/vault.cpp
@@ -0,0 +1,130 @@
+#include <iostream>
+#include <string>
+#include <array>
+
+// Definitions for puzzle requirements
+constexpr int TOTAL_LEVELS = 5;
+
+// Enumeration for the states of the puzzle
+enum PuzzleState {
+ STATE_UNINITIALIZED,
+ STATE_RESET,
+ STATE_PLAYING,
+ STATE_SOLVED,
+ STATE_ERROR
+};
+
+// This array maps each level to the correct button press
+const std::array<std::string, TOTAL_LEVELS> validButtons = {"A3", "F1", "U4", "C2", "L1"};
+
+PuzzleState puzzleState = STATE_UNINITIALIZED;
+int currentLevel = 0;
+
+// Function prototypes
+void displayCode(int level);
+void sendI2CUpdate(PuzzleState state);
+
+// Simulate sending an I2C update
+void sendI2CUpdate(PuzzleState state) {
+ std::cout << "Sending state " << state << " to main controller via I2C.\n";
+}
+
+// Simulate checking if the vault door is closed
+bool isVaultClosed() {
+ return true; // Return true if the door sensor indicates closed
+}
+
+// Function to display a code on the 7-segment display
+void displayCode(int level) {
+ std::cout << "Displaying code for level " << level << " on the 7-segment display.\n";
+}
+
+// Function to initialize the puzzle
+void initializePuzzle() {
+ if (isVaultClosed()) {
+ puzzleState = STATE_RESET;
+ currentLevel = 1; // Start at level 1
+ std::cout << "Puzzle initialized. Starting at level " << currentLevel << ".\n";
+ displayCode(currentLevel); // Show the first code
+ } else {
+ std::cout << "Vault door is open. Please close the door to start the puzzle.\n";
+ }
+}
+
+// Function to lock the vault
+void lockVault() {
+ std::cout << "Vault locked.\n";
+}
+
+// Function to unlock the vault
+void unlockVault() {
+ std::cout << "Vault unlocked!\n";
+}
+
+// Function to simulate the buzzer sound
+void playErrorSound() {
+ std::cout << "Playing error sound.\n";
+}
+
+// Function to simulate blinking the 7-segment display
+void blinkDisplay() {
+ std::cout << "7-segment display is blinking to indicate an error.\n";
+}
+
+// Validate the button press for the current level
+bool isValidButtonPress(const std::string& button, int level) {
+ return button == validButtons[level - 1];
+}
+
+// Function to update the state of the puzzle based on the current level
+void updateStateAfterButtonPress(bool validPress) {
+ if (validPress) {
+ if (currentLevel >= TOTAL_LEVELS) {
+ puzzleState = STATE_SOLVED;
+ unlockVault();
+ } else {
+ puzzleState = STATE_PLAYING;
+ displayCode(currentLevel);
+ }
+ } else {
+ puzzleState = STATE_ERROR;
+ playErrorSound();
+ blinkDisplay();
+ lockVault();
+ currentLevel = 1; // Reset to level 1
+ displayCode(currentLevel);
+ }
+ sendI2CUpdate(puzzleState); // Notify main controller of the state change
+}
+
+int main() {
+ initializePuzzle();
+
+ std::string buttonInput;
+
+ while (puzzleState != STATE_SOLVED) {
+ std::cout << "Enter the button pressed for level " << currentLevel << " (format Xn, e.g., A3): ";
+ std::getline(std::cin, buttonInput);
+
+ if (!buttonInput.empty() && isValidButtonPress(buttonInput, currentLevel)) {
+ currentLevel++;
+ if (currentLevel > TOTAL_LEVELS) {
+ puzzleState = STATE_SOLVED;
+ unlockVault();
+ std::cout << "The puzzle is solved and the vault is open!\n";
+ } else {
+ displayCode(currentLevel);
+ }
+ } else {
+ playErrorSound();
+ blinkDisplay();
+ lockVault();
+ puzzleState = STATE_RESET;
+ currentLevel = 1;
+ displayCode(currentLevel);
+ }
+ sendI2CUpdate(puzzleState);
+ }
+
+ return 0;
+}
diff --git a/readme.md b/readme.md
index 1ad72e9..7802f5c 100644
--- a/readme.md
+++ b/readme.md
@@ -2,6 +2,32 @@
Avans University of Applied Sciences project puzzle box.
+## tidyness
+
+Please keep this repository tidy by being aware of the following conventions!
+
+### folder structure
+
+|folder|contains|
+|-|-|
+|`/client`|Desktop PC application for controlling the puzzle box
+|`/docs`|Project documentation in AsciiDoc(tor) format
+|`/lib`|Libraries (tracked as [submodules](#submodules))
+|`/main`|Main controller (RPi pico) software
+|`/proto`|Puzzle bus TCP protocol functions (used by main and client)
+|`/puzzle/<name>`|Puzzle sources, each puzzle has its own subdirectory
+|`/shared`|Auxiliary shared code
+|`/test`|Unit test framework (currently unutilized)
+
+### code style
+
+An `.editorconfig` file is provided in this repository. Please install the
+[EditorConfig](https://editorconfig.org/) plugin for your text editor of choice
+to automatically use these.
+
+Currently, no linter/formatter is configured for maintaining consistent code
+style.
+
## submodules
This repository tracks (most) dependencies via git submodules.