aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main/puzzle/software/.devcontainer/Dockerfile47
-rw-r--r--main/puzzle/software/.devcontainer/devcontainer.json45
-rw-r--r--main/puzzle/software/.gitignore3
-rw-r--r--main/puzzle/software/.vscode/c_cpp_properties.json27
-rw-r--r--main/puzzle/software/.vscode/launch.json10
-rw-r--r--main/puzzle/software/.vscode/settings.json18
-rw-r--r--main/puzzle/software/.vscode/tasks.json259
-rw-r--r--main/puzzle/software/CMakeLists.txt8
-rw-r--r--main/puzzle/software/README.md32
-rw-r--r--main/puzzle/software/main/CMakeLists.txt2
-rw-r--r--main/puzzle/software/main/main.c160
11 files changed, 611 insertions, 0 deletions
diff --git a/main/puzzle/software/.devcontainer/Dockerfile b/main/puzzle/software/.devcontainer/Dockerfile
new file mode 100644
index 0000000..1fe78dc
--- /dev/null
+++ b/main/puzzle/software/.devcontainer/Dockerfile
@@ -0,0 +1,47 @@
+FROM espressif/idf
+
+ARG DEBIAN_FRONTEND=nointeractive
+ARG CONTAINER_USER=esp
+ARG USER_UID=1000
+ARG USER_GID=$USER_UID
+
+RUN apt-get update \
+ && apt install -y -q \
+ cmake \
+ git \
+ libglib2.0-0 \
+ libnuma1 \
+ libpixman-1-0 \
+ && rm -rf /var/lib/apt/lists/*
+
+# QEMU
+ENV QEMU_REL=esp_develop_8.2.0_20240122
+ENV QEMU_SHA256=e7c72ef5705ad1444d391711088c8717fc89f42e9bf6d1487f9c2a326b8cfa83
+ENV QEMU_DIST=qemu-xtensa-softmmu-${QEMU_REL}-x86_64-linux-gnu.tar.xz
+ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/esp-develop-8.2.0-20240122/${QEMU_DIST}
+
+ENV LC_ALL=C.UTF-8
+ENV LANG=C.UTF-8
+
+RUN wget --no-verbose ${QEMU_URL} \
+ && echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
+ && tar -xf $QEMU_DIST -C /opt \
+ && rm ${QEMU_DIST}
+
+ENV PATH=/opt/qemu/bin:${PATH}
+
+RUN groupadd --gid $USER_GID $CONTAINER_USER \
+ && adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
+ && usermod -a -G root $CONTAINER_USER && usermod -a -G dialout $CONTAINER_USER
+
+RUN chmod -R 775 /opt/esp/python_env/
+
+USER ${CONTAINER_USER}
+ENV USER=${CONTAINER_USER}
+WORKDIR /home/${CONTAINER_USER}
+
+RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
+
+ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
+
+CMD ["/bin/bash", "-c"] \ No newline at end of file
diff --git a/main/puzzle/software/.devcontainer/devcontainer.json b/main/puzzle/software/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..1d913ec
--- /dev/null
+++ b/main/puzzle/software/.devcontainer/devcontainer.json
@@ -0,0 +1,45 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
+// https://github.com/microsoft/vscode-dev-containers/tree/v0.183.0/containers/ubuntu
+{
+ "name": "ESP-IDF QEMU",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+ // Add the IDs of extensions you want installed when the container is created
+ "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
+ /* the path of workspace folder to be opened after container is running
+ */
+ "workspaceFolder": "${localWorkspaceFolder}",
+ "mounts": [
+ "source=extensionCache,target=/root/.vscode-server/extensions,type=volume"
+ ],
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.defaultProfile.linux": "bash",
+ "idf.espIdfPath": "/opt/esp/idf",
+ "idf.customExtraPaths": "",
+ "idf.pythonBinPath": "/opt/esp/python_env/idf5.3_py3.10_env/bin/python",
+ "idf.toolsPath": "/opt/esp",
+ "idf.gitPath": "/usr/bin/git"
+ },
+ "extensions": [
+ "espressif.esp-idf-extension"
+ ],
+ },
+ "codespaces": {
+ "settings": {
+ "terminal.integrated.defaultProfile.linux": "bash",
+ "idf.espIdfPath": "/opt/esp/idf",
+ "idf.customExtraPaths": "",
+ "idf.pythonBinPath": "/opt/esp/python_env/idf5.3_py3.10_env/bin/python",
+ "idf.toolsPath": "/opt/esp",
+ "idf.gitPath": "/usr/bin/git"
+ },
+ "extensions": [
+ "espressif.esp-idf-extension"
+ ],
+ }
+ },
+ "runArgs": ["--privileged"]
+} \ No newline at end of file
diff --git a/main/puzzle/software/.gitignore b/main/puzzle/software/.gitignore
new file mode 100644
index 0000000..51c9513
--- /dev/null
+++ b/main/puzzle/software/.gitignore
@@ -0,0 +1,3 @@
+build/
+sdkconfig
+sdkconfig.old \ No newline at end of file
diff --git a/main/puzzle/software/.vscode/c_cpp_properties.json b/main/puzzle/software/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..ee1cac1
--- /dev/null
+++ b/main/puzzle/software/.vscode/c_cpp_properties.json
@@ -0,0 +1,27 @@
+{
+ "configurations": [
+ {
+ "name": "ESP-IDF",
+ "compilerPath": "${config:idf.toolsPathWin}\\tools\\xtensa-esp-elf\\esp-13.2.0_20230928\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
+ "compileCommands": "${workspaceFolder}/build/compile_commands.json",
+ "includePath": [
+ "${config:idf.espIdfPath}/components/**",
+ "${config:idf.espIdfPathWin}/components/**",
+ "${config:idf.espAdfPath}/components/**",
+ "${config:idf.espAdfPathWin}/components/**",
+ "${workspaceFolder}/**"
+ ],
+ "browse": {
+ "path": [
+ "${config:idf.espIdfPath}/components",
+ "${config:idf.espIdfPathWin}/components",
+ "${config:idf.espAdfPath}/components/**",
+ "${config:idf.espAdfPathWin}/components/**",
+ "${workspaceFolder}"
+ ],
+ "limitSymbolsToIncludedHeaders": false
+ }
+ }
+ ],
+ "version": 4
+}
diff --git a/main/puzzle/software/.vscode/launch.json b/main/puzzle/software/.vscode/launch.json
new file mode 100644
index 0000000..6d2236f
--- /dev/null
+++ b/main/puzzle/software/.vscode/launch.json
@@ -0,0 +1,10 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "espidf",
+ "name": "Launch",
+ "request": "launch"
+ }
+ ]
+} \ No newline at end of file
diff --git a/main/puzzle/software/.vscode/settings.json b/main/puzzle/software/.vscode/settings.json
new file mode 100644
index 0000000..a3cde66
--- /dev/null
+++ b/main/puzzle/software/.vscode/settings.json
@@ -0,0 +1,18 @@
+{
+ "C_Cpp.intelliSenseEngine": "default",
+ "idf.adapterTargetName": "esp32",
+ "idf.customExtraPaths": "C:\\Users\\Gebruiker\\.espressif\\tools\\xtensa-esp-elf-gdb\\12.1_20231023\\xtensa-esp-elf-gdb\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\riscv32-esp-elf-gdb\\12.1_20231023\\riscv32-esp-elf-gdb\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\xtensa-esp-elf\\esp-13.2.0_20230928\\xtensa-esp-elf\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\riscv32-esp-elf\\esp-13.2.0_20230928\\riscv32-esp-elf\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\esp32ulp-elf\\2.35_20220830\\esp32ulp-elf\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\cmake\\3.24.0\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\openocd-esp32\\v0.12.0-esp32-20230921\\openocd-esp32\\bin;C:\\Users\\Gebruiker\\.espressif\\tools\\ninja\\1.11.1;C:\\Users\\Gebruiker\\.espressif\\tools\\idf-exe\\1.0.3;C:\\Users\\Gebruiker\\.espressif\\tools\\ccache\\4.8\\ccache-4.8-windows-x86_64;C:\\Users\\Gebruiker\\.espressif\\tools\\dfu-util\\0.11\\dfu-util-0.11-win64;C:\\Users\\Gebruiker\\.espressif\\tools\\esp-rom-elfs\\20230320",
+ "idf.customExtraVars": {
+ "OPENOCD_SCRIPTS": "C:\\Users\\Gebruiker\\.espressif\\tools\\openocd-esp32\\v0.12.0-esp32-20230921/openocd-esp32/share/openocd/scripts",
+ "IDF_CCACHE_ENABLE": "1",
+ "ESP_ROM_ELF_DIR": "C:\\Users\\Gebruiker\\.espressif\\tools\\esp-rom-elfs\\20230320/"
+ },
+ "idf.espIdfPathWin": "e:\\esp\\v5.2.1\\esp-idf",
+ "idf.openOcdConfigs": [
+ "interface/ftdi/esp32_devkitj_v1.cfg",
+ "target/esp32.cfg"
+ ],
+ "idf.portWin": "COM1",
+ "idf.pythonBinPathWin": "C:\\Users\\Gebruiker\\.espressif\\python_env\\idf5.2_py3.11_env\\Scripts\\python.exe",
+ "idf.toolsPathWin": "C:\\Users\\Gebruiker\\.espressif"
+}
diff --git a/main/puzzle/software/.vscode/tasks.json b/main/puzzle/software/.vscode/tasks.json
new file mode 100644
index 0000000..1dc7915
--- /dev/null
+++ b/main/puzzle/software/.vscode/tasks.json
@@ -0,0 +1,259 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build - Build project",
+ "type": "shell",
+ "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py build",
+ "windows": {
+ "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py build",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}"
+ }
+ }
+ },
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}"
+ }
+ },
+ "problemMatcher": [
+ {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "Set ESP-IDF Target",
+ "type": "shell",
+ "command": "${command:espIdf.setTarget}",
+ "problemMatcher": {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ },
+ {
+ "label": "Clean - Clean the project",
+ "type": "shell",
+ "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py fullclean",
+ "windows": {
+ "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py fullclean",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}"
+ }
+ }
+ },
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}"
+ }
+ },
+ "problemMatcher": [
+ {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ ]
+ },
+ {
+ "label": "Flash - Flash the device",
+ "type": "shell",
+ "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} -b ${config:idf.flashBaudRate} flash",
+ "windows": {
+ "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py flash -p ${config:idf.portWin} -b ${config:idf.flashBaudRate}",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}"
+ }
+ }
+ },
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}"
+ }
+ },
+ "problemMatcher": [
+ {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ ]
+ },
+ {
+ "label": "Monitor: Start the monitor",
+ "type": "shell",
+ "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} monitor",
+ "windows": {
+ "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py -p ${config:idf.portWin} monitor",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}"
+ }
+ }
+ },
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}"
+ }
+ },
+ "problemMatcher": [
+ {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ ],
+ "dependsOn": "Flash - Flash the device"
+ },
+ {
+ "label": "OpenOCD: Start openOCD",
+ "type": "shell",
+ "presentation": {
+ "echo": true,
+ "reveal": "never",
+ "focus": false,
+ "panel": "new"
+ },
+ "command": "openocd -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}",
+ "windows": {
+ "command": "openocd.exe -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}"
+ }
+ }
+ },
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}"
+ }
+ },
+ "problemMatcher": {
+ "owner": "cpp",
+ "fileLocation": [
+ "autoDetect",
+ "${workspaceFolder}"
+ ],
+ "pattern": {
+ "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ }
+ },
+ {
+ "label": "adapter",
+ "type": "shell",
+ "command": "${config:idf.pythonBinPath}",
+ "isBackground": true,
+ "options": {
+ "env": {
+ "PATH": "${env:PATH}:${config:idf.customExtraPaths}",
+ "PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter"
+ }
+ },
+ "problemMatcher": {
+ "background": {
+ "beginsPattern": "\bDEBUG_ADAPTER_STARTED\b",
+ "endsPattern": "DEBUG_ADAPTER_READY2CONNECT",
+ "activeOnStart": true
+ },
+ "pattern": {
+ "regexp": "(\\d+)-(\\d+)-(\\d+)\\s(\\d+):(\\d+):(\\d+),(\\d+)\\s-(.+)\\s(ERROR)",
+ "file": 8,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 9
+ }
+ },
+ "args": [
+ "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter_main.py",
+ "-e",
+ "${workspaceFolder}/build/${command:espIdf.getProjectName}.elf",
+ "-s",
+ "$OPENOCD_SCRIPTS",
+ "-dn",
+ "esp32",
+ "-om",
+ "connect_to_instance",
+ "-t",
+ "xtensa-esp32-elf-"
+
+ ],
+ "windows": {
+ "command": "${config:idf.pythonBinPathWin}",
+ "options": {
+ "env": {
+ "PATH": "${env:PATH};${config:idf.customExtraPaths}",
+ "PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter"
+ }
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/main/puzzle/software/CMakeLists.txt b/main/puzzle/software/CMakeLists.txt
new file mode 100644
index 0000000..e0bd7b6
--- /dev/null
+++ b/main/puzzle/software/CMakeLists.txt
@@ -0,0 +1,8 @@
+# For more information about build system see
+# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
+# The following five lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(software) \ No newline at end of file
diff --git a/main/puzzle/software/README.md b/main/puzzle/software/README.md
new file mode 100644
index 0000000..455eb90
--- /dev/null
+++ b/main/puzzle/software/README.md
@@ -0,0 +1,32 @@
+# _Sample project_
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+This is the simplest buildable example. The example is used by command `idf.py create-project`
+that copies the project to user specified path and set it's name. For more information follow the [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project)
+
+
+
+## How to use example
+We encourage the users to use the example as a template for the new projects.
+A recommended way is to follow the instructions on a [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project).
+
+## Example folder contents
+
+The project **sample_project** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main).
+
+ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt`
+files that provide set of directives and instructions describing the project's source files and targets
+(executable, library, or both).
+
+Below is short explanation of remaining files in the project folder.
+
+```
+├── CMakeLists.txt
+├── main
+│   ├── CMakeLists.txt
+│   └── main.c
+└── README.md This is the file you are currently reading
+```
+Additionally, the sample project contains Makefile and component.mk files, used for the legacy Make based build system.
+They are not used or needed when building with CMake and idf.py.
diff --git a/main/puzzle/software/main/CMakeLists.txt b/main/puzzle/software/main/CMakeLists.txt
new file mode 100644
index 0000000..e0287b7
--- /dev/null
+++ b/main/puzzle/software/main/CMakeLists.txt
@@ -0,0 +1,2 @@
+idf_component_register(SRCS "main.c"
+ INCLUDE_DIRS ".") \ No newline at end of file
diff --git a/main/puzzle/software/main/main.c b/main/puzzle/software/main/main.c
new file mode 100644
index 0000000..80aec8f
--- /dev/null
+++ b/main/puzzle/software/main/main.c
@@ -0,0 +1,160 @@
+#include <stdio.h>
+#include "driver/gpio.h"
+#include "esp_log.h"
+#include "driver/i2c.h"
+
+#define TAG "SoftwarePuzzle"
+#define I2C_MASTER_NUM I2C_NUM_0
+#define I2C_MASTER_SDA_IO GPIO_NUM_21 // update to actual gpio pin
+#define I2C_MASTER_SCL_IO GPIO_NUM_22 // update to actual gpio pin
+#define I2C_MASTER_FREQ_HZ 1000 // check actual value
+// #define I2C_SLAVE_ADDR 0x10
+
+// pins are sorted [up -> down] when looking at the puzzelbox;
+#define TOTAL_VALID 5
+#define INPUT_PINS {}
+#define OUTPUT_PINS {}
+
+typedef enum {
+ UNINITIALIZED = 0U,
+ IDLE,
+ PLAYING,
+ SOLVED,
+ ERROR
+} PuzzleState;
+
+PuzzleState state = UNINITIALIZED;
+const char* validCombinations[TOTAL_VALID] = {"11", "22", "33", "44", "55"};
+
+// master or slave? (assumed master)
+void I2CInit() {
+ i2c_config_t config;
+ config.mode = I2C_MODE_SLAVE;
+ config.sda_io_num = I2C_SLAVE_SDA_IO;
+ config.sda_pullup_en = GPIO_PULLUP_ENABLE;
+ config.scl_io_num = I2C_SLAVE_SCL_IO;
+ config.scl_pullup_en = GPIO_PULLUP_ENABLE;
+ config.master.clk_speed = I2C_MASTER_FREQ_HZ;
+ i2c_param_config(I2C_MASTER_NUM, &config);
+ i2c_driver_install(I2C_MASTER_NUM, config.mode, 0, 0, 0);
+}
+
+void SendI2CUpdate(PuzzleState state) {
+ uint8_t data;
+ switch (state) {
+ case UNINITIALIZED: data = 0x00; break;
+ case IDLE: data = 0x01; break;
+ case PLAYING: data = 0x02; break;
+ case SOLVED: data = 0x03; break;
+ case ERROR: data = 0x04; break;
+ default: data = 0xFF; // Unknown state
+ }
+ ESP_LOGI(TAG, "Sending state %d to main controller via I2C.", state);
+
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, (I2C_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, true);
+ i2c_master_write_byte(cmd, data, true);
+ i2c_master_stop(cmd);
+ esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+
+ if (ret == ESP_OK) {
+ ESP_LOGI(TAG, "State update sent successfully.");
+ } else {
+ ESP_LOGE(TAG, "Failed to send state update via I2C.");
+ }
+}
+
+void SystemInit() {
+ gpio_config_t config;
+
+ // Configure the input row with pull-up
+ config.intr_type = GPIO_INTR_DISABLE;
+ config.mode = GPIO_MODE_INPUT;
+ config.pin_bit_mask = (1ULL << INPUT_PINS[0]) | (1ULL << INPUT_PINS[1]) | (1ULL << INPUT_PINS[2]) | (1ULL << INPUT_PINS[3]);
+ config.pull_down_en = 0;
+ config.pull_up_en = 1;
+ gpio_config(&config);
+
+ // configure the output row
+ config.mode = GPIO_MODE_OUTPUT;
+ config.pin_bit_mask = (1ULL << OUTPUT_PINS[0]) | (1ULL << OUTPUT_PINS[1]) | (1ULL << OUTPUT_PINS[2]);
+ config.pull_down_en = 0;
+ config.pull_up_en = 0;
+ gpio_config(&config);
+
+ SendI2CUpdate(IDLE);
+ state = IDLE;
+}
+
+bool ValidateCombination(const int in, const int out) {
+ for ( int ans = 0; ans < TOTAL_VALID; ans++) {
+ if ( validCombinations[ans][0] == in ) {
+ if ( validCombinations[ans][1] == out )
+ return true;
+ }
+ }
+ return false;
+}
+
+void Update() {
+ bool validCombination = true;
+
+ for ( int outputPin = 0; outputPin < VALID_TOTAL; outputPin++ ){
+ // if a wrong in/out combination was given stop checking
+ if (validCombination == false) {
+ return;
+ }
+
+ // set output pin LOW
+ gpio_set_level(OUTPUT_PINS[outputPin], 0);
+ for ( int inputPin = 0; inputPin < VALID_TOTAL; intputPin++ ){
+ // check which input is set to LOW
+ if (gpio_get_level(INPUT_PINS[inputPin]) == 0) {
+ // remove message when going into production(?)
+ ESP_LOGI(TAG, "Detected connection between output pin: '%s' and input pin: '%s'", outputPin, inputPin);
+ // check if found connection is valid
+ validCombination = ValidateCombination(inputPin, outputPin);
+ }
+ }
+ // set output pin back to HIGH
+ gpio_set_level(OUTPUT_PINS[outputPin], 1);
+ }
+
+ if ( validCombination == true ) {
+ ESP_LOGI(TAG, "Solved!");
+ SendI2CUpdate(SOLVED);
+ state = SOLVED;
+ }
+}
+
+void app_main()
+{
+ I2CInit();
+ SystemInit();
+ ESP_LOGI("App", "GPIO and I2C initialized.");
+
+ uint8_t data[2];
+ while (state != SOLVED) {
+ if( state == ERROR ) {
+ ESP_LOGE(TAG, "Error: Entered unknown state!");
+ break;
+ }
+
+ if( state != ERROR && state != UNINITIALIZED ) {
+ // get puzzle status from main controller
+ // read 2 bytes from slave address 0x50, register 0x00
+ i2c_master_read(0x50, 0x00, data, 2);
+
+ // handle data
+ }
+
+ if( state == PLAYING ) {
+ Update();
+ }
+
+ // delay to make sure it doesn't check every few us
+ vTaskDelay(pdMS_TO_TICKS(100));
+ }
+} \ No newline at end of file