aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/cmd.cpp2
-rw-r--r--client/cmd.h7
-rw-r--r--client/i2c.h2
-rw-r--r--client/parse.h4
-rw-r--r--client/rl.cpp2
-rw-r--r--client/sock.cpp2
-rw-r--r--client/sock.h2
-rw-r--r--lib/pbdrv/CMakeLists.txt6
-rw-r--r--lib/pbdrv/drv/arduino/include.cmake2
-rw-r--r--lib/pbdrv/drv/arduino/index.dox22
-rw-r--r--lib/pbdrv/drv/arduino/mod.cpp19
-rw-r--r--lib/pbdrv/drv/index.dox13
-rw-r--r--lib/pbdrv/drv/rp2040/include.cmake11
-rw-r--r--lib/pbdrv/drv/rp2040/index.dox31
-rw-r--r--lib/pbdrv/drv/rp2040/mod.c49
-rw-r--r--lib/pbdrv/drv/rp2040/pb-mod.h21
-rw-r--r--lib/pbdrv/ext/freertos/include.cmake4
-rw-r--r--lib/pbdrv/ext/freertos/index.dox2
-rw-r--r--lib/pbdrv/ext/freertos/pb-mod.c10
-rw-r--r--lib/pbdrv/index.dox13
-rw-r--r--lib/pbdrv/pb-mod.h8
-rw-r--r--lib/pbdrv/pb-route.c3
-rw-r--r--lib/pbdrv/pb-types.h6
-rw-r--r--main/CMakeLists.txt4
-rw-r--r--main/config.def.h47
-rw-r--r--main/i2c.c88
-rw-r--r--main/index.dox6
-rw-r--r--main/init.c14
-rw-r--r--main/mod.c3
-rw-r--r--main/pbdrv.c95
-rw-r--r--main/pbdrv.h64
-rw-r--r--main/readme.md12
-rw-r--r--puzzle/dummy/CMakeLists.txt7
-rw-r--r--puzzle/dummy/FreeRTOSConfig.h4
-rw-r--r--puzzle/dummy/makefile4
-rw-r--r--puzzle/vault/main.cpp152
-rw-r--r--puzzle/vault/makefile6
37 files changed, 403 insertions, 344 deletions
diff --git a/client/cmd.cpp b/client/cmd.cpp
index 10d53e3..062fefa 100644
--- a/client/cmd.cpp
+++ b/client/cmd.cpp
@@ -28,7 +28,7 @@ void cmd_test(char*) {
void cmd_help(char*) {
printf("List of available commands:\n");
for (size_t i = 0; i < cmds_length; i++) {
- struct cmd cmd = cmds[i];
+ cmd_t cmd = cmds[i];
printf(" %-*s", 10, cmd.name);
if (cmd.info != NULL)
printf(" %s", cmd.info);
diff --git a/client/cmd.h b/client/cmd.h
index a3cc44a..4f77d50 100644
--- a/client/cmd.h
+++ b/client/cmd.h
@@ -2,8 +2,13 @@
/**
* \ingroup pbc
- * \defgroup pbc_cmd cmd
+ * \defgroup pbc_cmd Commands
* \brief Commands within \ref pbc
+ *
+ * \note A manpage is available containing end-user usage instructions inside
+ * the \ref client folder in the source code repository. This page contains the
+ * internal code documentation for the commands defined in \c pbc.
+ *
* \{
*/
diff --git a/client/i2c.h b/client/i2c.h
index 87f33c9..d2cfa9a 100644
--- a/client/i2c.h
+++ b/client/i2c.h
@@ -5,7 +5,7 @@
/**
* \ingroup pbc
- * \defgroup pbc_i2c i2c
+ * \defgroup pbc_i2c I2C
* \brief I2C abstraction functions
* \{
*/
diff --git a/client/parse.h b/client/parse.h
index 8b7d235..1beb714 100644
--- a/client/parse.h
+++ b/client/parse.h
@@ -4,8 +4,8 @@
/**
* \ingroup pbc
- * \defgroup pbc_parse parse
- * \brief Debug send command parser utilities
+ * \defgroup pbc_parse Parse
+ * \brief Debug \c send command parser utilities
* \{
*/
diff --git a/client/rl.cpp b/client/rl.cpp
index f839d96..2e74e5f 100644
--- a/client/rl.cpp
+++ b/client/rl.cpp
@@ -57,7 +57,7 @@ static char* rl_completion_entries(const char *text, int state) {
if (state == 0) i = 0;
while (i < cmds_length) {
- struct cmd cmd = cmds[i];
+ cmd_t cmd = cmds[i];
i++;
if (strncmp(text, cmd.name, strlen(text)) == 0) {
return strdup(cmd.name);
diff --git a/client/sock.cpp b/client/sock.cpp
index 3490586..e33a3dc 100644
--- a/client/sock.cpp
+++ b/client/sock.cpp
@@ -101,7 +101,7 @@ void PBSocket::sock_task() {
if (ret > 0) continue;
// message read completely!
- i2c_recv(input.addr, input.data, input.length);
+ i2c_recv(input.data, input.length);
free(input.data);
}
diff --git a/client/sock.h b/client/sock.h
index c124e45..792123e 100644
--- a/client/sock.h
+++ b/client/sock.h
@@ -5,7 +5,7 @@
/**
* \ingroup pbc
- * \defgroup pbc_sock sock
+ * \defgroup pbc_sock Socket
* \brief TCP socket handling
* \{
*/
diff --git a/lib/pbdrv/CMakeLists.txt b/lib/pbdrv/CMakeLists.txt
index 998ed4d..903a3b4 100644
--- a/lib/pbdrv/CMakeLists.txt
+++ b/lib/pbdrv/CMakeLists.txt
@@ -23,7 +23,7 @@ add_library(pbdrv STATIC
pb-buf.c
)
target_include_directories(pbdrv SYSTEM INTERFACE .)
-target_link_libraries(pbdrv mpack)
+target_link_libraries(pbdrv PRIVATE mpack)
# puzzle bus *module* specific code
add_library(pbdrv-mod OBJECT
@@ -32,9 +32,9 @@ add_library(pbdrv-mod OBJECT
pb-route.c
)
target_include_directories(pbdrv-mod SYSTEM INTERFACE .)
-target_link_libraries(pbdrv-mod pbdrv)
+target_link_libraries(pbdrv-mod PUBLIC pbdrv)
# puzzle bus drivers
include(drv/arduino/include.cmake)
-# include(drv/rp2040/include.cmake) # please see /main/pbdrv.h
+include(drv/rp2040/include.cmake)
diff --git a/lib/pbdrv/drv/arduino/include.cmake b/lib/pbdrv/drv/arduino/include.cmake
index 1e2ff08..5ec1124 100644
--- a/lib/pbdrv/drv/arduino/include.cmake
+++ b/lib/pbdrv/drv/arduino/include.cmake
@@ -3,7 +3,7 @@ if(NOT DEFINED ARDUINO)
endif()
target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.cpp")
-target_link_arduino_libraries(pbdrv-mod core Wire)
+target_link_arduino_libraries(pbdrv-mod PRIVATE core Wire)
# freertos is used to defer the handling of i2c messages outside the receive
# interrupt service routine
diff --git a/lib/pbdrv/drv/arduino/index.dox b/lib/pbdrv/drv/arduino/index.dox
index 4c74222..03510dd 100644
--- a/lib/pbdrv/drv/arduino/index.dox
+++ b/lib/pbdrv/drv/arduino/index.dox
@@ -2,18 +2,24 @@
/**
\ingroup pb_drv
\defgroup pb_drv_arduino Arduino
-\brief Arduino (Arduino-CMake-Toolchain) driver
-
-This driver is automatically enabled if the variable \c ARDUINO is defined in
-your CMakeLists.txt (it is by default when using Arduino-CMake-Toolchain).
-
-\note This driver automatically includes the
-\ref pb_ext_freertos "FreeRTOS extension" for deferring calls to \c
-pb_i2c_recv() from the I2C ISR.
+\brief Arduino ATmega (w/ Arduino-CMake-Toolchain) driver
This driver is known to work with the following MCUs:
- ATmega328P (Arduino Uno)
- ATmega2560 (Arduino Mega)
+\par Usage
+- Link the \c pbdrv-mod library with your main executable
+
+\note This driver is automatically enabled if the variable \c ARDUINO is
+defined in your CMakeLists.txt (it is by default when using
+Arduino-CMake-Toolchain).
+
+\note This driver automatically includes the \ref pb_ext_freertos
+"FreeRTOS extension" for deferring calls to \c pb_i2c_recv() from the I2C ISR.
+
+A complete example for this driver is available in the \ref puzzle/dummy
+folder.
+
*/
diff --git a/lib/pbdrv/drv/arduino/mod.cpp b/lib/pbdrv/drv/arduino/mod.cpp
index 2eef8d5..c381077 100644
--- a/lib/pbdrv/drv/arduino/mod.cpp
+++ b/lib/pbdrv/drv/arduino/mod.cpp
@@ -1,7 +1,3 @@
-#ifndef ARDUINO
-#error This driver only works on the Arduino platform!
-#endif
-
#include <Arduino.h>
#include <Wire.h>
#include <avr/delay.h>
@@ -42,8 +38,17 @@ static void pb_setup() {
Wire.onReceive(recv_event);
}
-/// \ingroup pb_drv_arduino
+/**
+ * \ingroup pb_drv_arduino
+ * \warning This function includes a hard-coded 10ms delay before sending. This
+ * is to work around a weird issue where the Arduino pulls both SDA and SCL low
+ * while attempting to initiate an I2C transmission. We were able to verify
+ * that the Arduino correctly handles bus arbitration under a test scenario
+ * with 2 Uno's, but ran into issues while integrating the Arduino's with the
+ * RP2040.
+ */
__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) {
+ vTaskDelay(10 / portTICK_PERIOD_MS); // prevent bus collisions
Wire.beginTransmission((int) addr);
Wire.write(buf, sz);
Wire.endTransmission(true);
@@ -69,6 +74,10 @@ void loop_task() {
* \ingroup pb_drv_arduino
* \brief Application entrypoint
*
+ * This function overrides the default (weak) implementation of the \c main()
+ * function in the Arduino framework. No additional setup is required to use
+ * this driver.
+ *
* \note I should really be able to use Arduino's initVariant function for
* this, but I can't seem to get it to link properly using the CMake setup in
* this repository. Overriding the main() function seems to work, and the
diff --git a/lib/pbdrv/drv/index.dox b/lib/pbdrv/drv/index.dox
index 1fe09e2..89b9247 100644
--- a/lib/pbdrv/drv/index.dox
+++ b/lib/pbdrv/drv/index.dox
@@ -7,19 +7,6 @@
Like \ref pb_ext "extensions", drivers provide platform-specific
implementations for various functions used in \ref pbdrv-mod.
-Drivers are automatically included based on your build configuration, and you
-only need to ensure \c pbdrv-mod is linked with your final executable in order
-to use one of the available drivers:
-
-```cmake
-# include pbdrv
-add_subdirectory(lib/pbdrv)
-
-# link pbdrv-mod
-target_link_libraries(main pbdrv-mod)
-
-```
-
If there is no existing driver for your target, you may implement the following
in order to use \ref pbdrv-mod:
diff --git a/lib/pbdrv/drv/rp2040/include.cmake b/lib/pbdrv/drv/rp2040/include.cmake
new file mode 100644
index 0000000..6d25e96
--- /dev/null
+++ b/lib/pbdrv/drv/rp2040/include.cmake
@@ -0,0 +1,11 @@
+if(NOT PICO_PLATFORM STREQUAL "rp2040")
+ return()
+endif()
+
+target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.c")
+target_link_libraries(pbdrv-mod PRIVATE hardware_i2c_headers pico_i2c_slave_headers)
+target_include_directories(pbdrv-mod INTERFACE "${CMAKE_CURRENT_LIST_DIR}")
+
+# pico-stdlib is compatible with C stdlib
+include("${CMAKE_CURRENT_LIST_DIR}/../../ext/stdlib/include.cmake")
+
diff --git a/lib/pbdrv/drv/rp2040/index.dox b/lib/pbdrv/drv/rp2040/index.dox
new file mode 100644
index 0000000..eb01d15
--- /dev/null
+++ b/lib/pbdrv/drv/rp2040/index.dox
@@ -0,0 +1,31 @@
+// vim:ft=doxygen
+/**
+\ingroup pb_drv
+\defgroup pb_drv_rp2040 RP2040
+\brief Raspberry Pi Pico and Pico W driver
+
+This driver is known to work with the following MCUs:
+- Raspberry Pi Pico W
+
+\note While the RP2040's datasheet claims it supports multi-master
+configurations by implementing bus arbitration, it does not natively support
+a mode where it is configured as a (multi-)master with a slave address, such
+that it can be addressed by other multi-masters.
+
+\note This driver includes a workaround that uses both I2C peripherals on the
+RP2040. To use this driver, make sure you initialize both I2C peripherals, and
+connect their respective SDA and SCL pins to the same I2C bus.
+
+\par Usage
+- Link the \c pbdrv-mod library with your main executable
+- Call the \c pb_setup() function (from \c <pb-mod.h>) during setup
+
+\note This driver is automatically enabled if the variable \c PICO_PLATFORM is
+equal to \c "rp2040" in your CMakeLists.txt (it is by default when using the
+pico-sdk CMake library).
+
+\note This driver automatically includes the \ref pb_ext_stdlib
+"stdlib extension" as the pico-sdk implements the C standard library.
+
+*/
+
diff --git a/lib/pbdrv/drv/rp2040/mod.c b/lib/pbdrv/drv/rp2040/mod.c
new file mode 100644
index 0000000..1535da9
--- /dev/null
+++ b/lib/pbdrv/drv/rp2040/mod.c
@@ -0,0 +1,49 @@
+#include "pb.h"
+
+#include "pb.h"
+#include "pb-types.h"
+#include "pb-mod.h"
+#include "pb-send.h"
+#include "pb-buf.h"
+
+#include <hardware/i2c.h>
+#include <hardware/gpio.h>
+#include <pico/i2c_slave.h>
+
+#define PB_I2C_S i2c0
+#define PB_I2C_M i2c1
+
+#define BUF_SIZE 256
+
+uint8_t i2c_msg_buf[BUF_SIZE];
+size_t i2c_msg_buf_sz = 0;
+
+// This function is called from the I2C ISR
+static void recv_event(i2c_inst_t *i2c, i2c_slave_event_t event) {
+ switch (event) {
+ case I2C_SLAVE_RECEIVE: {
+ if (i2c_msg_buf_sz == BUF_SIZE) return;
+ i2c_msg_buf[i2c_msg_buf_sz++] = i2c_read_byte_raw(PB_I2C_S);
+ break;
+ }
+ case I2C_SLAVE_FINISH: {
+ pb_i2c_recv(i2c_msg_buf, i2c_msg_buf_sz);
+ i2c_msg_buf_sz = 0;
+ break;
+ }
+ default: break;
+ }
+}
+
+void pb_setup() {
+ i2c_init(PB_I2C_S, PB_CLOCK_SPEED_HZ);
+ i2c_init(PB_I2C_M, PB_CLOCK_SPEED_HZ);
+
+ i2c_slave_init(PB_I2C_S, PB_MOD_ADDR, &recv_event);
+}
+
+__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) {
+ // false to write stop condition to i2c bus
+ i2c_write_timeout_us(PB_I2C_M, addr, buf, sz, false, PB_TIMEOUT_US);
+}
+
diff --git a/lib/pbdrv/drv/rp2040/pb-mod.h b/lib/pbdrv/drv/rp2040/pb-mod.h
new file mode 100644
index 0000000..96edc70
--- /dev/null
+++ b/lib/pbdrv/drv/rp2040/pb-mod.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \ingroup pb_drv_rp2040
+ * \brief Puzzle bus driver setup
+ *
+ * This function must be called from a setup/init function to initialize the
+ * I2C peripherals used by this driver.
+ */
+void pb_setup();
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "../../pb-mod.h"
+
diff --git a/lib/pbdrv/ext/freertos/include.cmake b/lib/pbdrv/ext/freertos/include.cmake
index e7ab7fd..e205c8f 100644
--- a/lib/pbdrv/ext/freertos/include.cmake
+++ b/lib/pbdrv/ext/freertos/include.cmake
@@ -1,9 +1,7 @@
target_sources(pbdrv PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pb-mem.c")
-target_link_libraries(pbdrv
+target_link_libraries(pbdrv PUBLIC
freertos_kernel
freertos_kernel_include
freertos_config
)
-target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pb-mod.c")
-
diff --git a/lib/pbdrv/ext/freertos/index.dox b/lib/pbdrv/ext/freertos/index.dox
index dfa45ff..7d72918 100644
--- a/lib/pbdrv/ext/freertos/index.dox
+++ b/lib/pbdrv/ext/freertos/index.dox
@@ -2,5 +2,5 @@
/**
\ingroup pb_ext
\defgroup pb_ext_freertos FreeRTOS
-\brief FreeRTOS memory management and scheduler-based delay
+\brief FreeRTOS memory management
*/
diff --git a/lib/pbdrv/ext/freertos/pb-mod.c b/lib/pbdrv/ext/freertos/pb-mod.c
deleted file mode 100644
index 5c0aa36..0000000
--- a/lib/pbdrv/ext/freertos/pb-mod.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <FreeRTOS.h>
-#include <task.h>
-
-#include "../../pb-types.h"
-
-/// \ingroup pb_ext_freertos
-__weak void pb_mod_blocking_delay_ms(unsigned long ms) {
- vTaskDelay(ms / portTICK_PERIOD_MS);
-}
-
diff --git a/lib/pbdrv/index.dox b/lib/pbdrv/index.dox
index eb0fd63..8ddcb6a 100644
--- a/lib/pbdrv/index.dox
+++ b/lib/pbdrv/index.dox
@@ -11,8 +11,9 @@ themselves. For a complete puzzle module driver, please see \ref pbdrv-mod.
If you order to use \ref pbdrv, you need to include this folder in your
CMakeLists.txt file, include the \ref pb_ext "extension" for your target
-platform, and link the \c pbdrv library with your executable:
+platform, and link the \c pbdrv library with your executable.
+\par Example
```cmake
# include pbdrv
add_subdirectory(lib/pbdrv)
@@ -35,14 +36,10 @@ using an existing \ref pb_drv "driver", or (partially) implementing the driver
functions.
Like \ref pbdrv, \ref pbdrv-mod can be used by including this folder in your
-CMakeLists.txt file and linking the library with your executable. A notable
-difference with \ref pbdrv-mod is that you do not need to include an extension.
-\ref pb_ext "Extensions" are still used by \ref pbdrv-mod, but they are
-included automatically by the target platform's \ref pb_drv "driver". The
-appropriate \ref pb_drv "driver" to load is also automatically detected.
-
-Example:
+CMakeLists.txt file and linking the library with your executable. Please see
+the \ref pb_drv "drivers" page for target-specific usage instructions.
+\par Example
```cmake
# include pbdrv
add_subdirectory(lib/pbdrv)
diff --git a/lib/pbdrv/pb-mod.h b/lib/pbdrv/pb-mod.h
index 3869e55..91e1d1f 100644
--- a/lib/pbdrv/pb-mod.h
+++ b/lib/pbdrv/pb-mod.h
@@ -24,14 +24,6 @@ extern const char * PB_MOD_NAME;
*/
extern const i2c_addr_t PB_MOD_ADDR;
-/**
- * \brief Platform-specific blocking delay function
- *
- * FIXME: this entire function should be removed (see handover: RP2040 I2C
- * limitations)
- */
-void pb_mod_blocking_delay_ms(unsigned long ms);
-
/// \}
/**
diff --git a/lib/pbdrv/pb-route.c b/lib/pbdrv/pb-route.c
index f5c32d6..5a7bd67 100644
--- a/lib/pbdrv/pb-route.c
+++ b/lib/pbdrv/pb-route.c
@@ -93,9 +93,6 @@ __weak void pb_route_cmd_magic_req(pb_msg_t * msg) {
// // return early if magic doesn't match
if (pb_memcmp(cmd->magic, pb_cmd_magic_req, sizeof(pb_cmd_magic_req)) != 0) return;
- // FIXME: this should be removed (see handover: RP2040 I2C limitations)
- pb_mod_blocking_delay_ms(2000);
-
pb_buf_t buf = pb_send_magic_res();
pb_send_reply(msg, &buf);
pb_buf_free(&buf);
diff --git a/lib/pbdrv/pb-types.h b/lib/pbdrv/pb-types.h
index ef3df54..44b590b 100644
--- a/lib/pbdrv/pb-types.h
+++ b/lib/pbdrv/pb-types.h
@@ -131,6 +131,12 @@ typedef struct {
pb_global_state_t state; //!< global state
} pb_cmd_state_t;
+//! PB_MOD_STATE data
+typedef struct {
+ i2c_addr_t sender; //!< i2c address of sender
+ pb_global_state_t state; //!< global state
+} pb_puzzle_module_t;
+
//! \ref PB_CMD_MAGIC data
typedef struct {
char * magic; //!< magic value
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index d6cbf33..76b1244 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -10,7 +10,6 @@ include(lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_impor
add_subdirectory(lib/mpack)
add_subdirectory(lib/i2ctcp)
add_subdirectory(lib/pbdrv)
-include(lib/pbdrv/ext/stdlib/include.cmake) # pico-stdlib is compatible with C stdlib
project(puzzlebox_main C CXX ASM)
@@ -24,7 +23,6 @@ add_executable(main
mod.c
tasks.c
blink.c
- pbdrv.c
)
pico_enable_stdio_usb(main 1)
@@ -34,7 +32,7 @@ pico_add_extra_outputs(main)
# include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include)
target_include_directories(main PRIVATE .)
-target_link_libraries(main
+target_link_libraries(main PUBLIC
pico_cyw43_arch_lwip_sys_freertos
pico_stdlib
pico_i2c_slave
diff --git a/main/config.def.h b/main/config.def.h
index 0dae608..cb6e8b4 100644
--- a/main/config.def.h
+++ b/main/config.def.h
@@ -4,8 +4,33 @@
/**
* \ingroup main
- * \defgroup main_config config
+ * \defgroup main_config Config
* \brief Configuration options
+ *
+ * The main controller firmware is configured statically (i.e. through
+ * compile-time defined options). Because the configuration is likely to
+ * contain Wi-Fi credentials, this file is not tracked under version control.
+ *
+ * Before compiling the main controller fimrware, a file (`config.h`) must be
+ * created by the user with the following format:
+ *
+ * ```c
+ * #pragma once
+ *
+ * // define non-default options here
+ *
+ * #include "config.def.h"
+ * ```
+ *
+ * \note `config.def.h` contains preprocessor logic that tries to ensure a
+ * correct configuration. The default configuration has the following settings:
+ * - Wi-Fi is disabled (prints a warning during compilation because it was not
+ * explicitly disabled by the user)
+ * - The TCP server is disabled (due to Wi-Fi being disabled)
+ *
+ * \note The exact default values of each configuration option, and all
+ * available options are listed below.
+ *
* \{
*/
@@ -72,13 +97,21 @@
* \name I2C configuration
* \{
*/
-#ifndef CFG_SDA_PIN
-//! I2C SDA pin
-#define CFG_SDA_PIN 16
+#ifndef CFG_SDA0_PIN
+//! I2C 0 SDA pin
+#define CFG_SDA0_PIN 16
#endif
-#ifndef CFG_SCL_PIN
-//! I2C SCL pin
-#define CFG_SCL_PIN 17
+#ifndef CFG_SCL0_PIN
+//! I2C 0 SCL pin
+#define CFG_SCL0_PIN 17
+#endif
+#ifndef CFG_SDA1_PIN
+//! I2C 1 SDA pin
+#define CFG_SDA1_PIN 18
+#endif
+#ifndef CFG_SCL1_PIN
+//! I2C 1 SCL pin
+#define CFG_SCL1_PIN 19
#endif
/// \}
diff --git a/main/i2c.c b/main/i2c.c
index f366793..e1563a0 100644
--- a/main/i2c.c
+++ b/main/i2c.c
@@ -8,36 +8,82 @@
#include "i2c.h"
#include "pb-mod.h"
-#include "pbdrv.h"
#include "config.h"
#include "pb-buf.h"
#include "pb-send.h"
-i2c_addr_t modules[CFG_PB_MOD_MAX];
+static pb_global_state_t _global_state = PB_GS_IDLE;
+pb_puzzle_module_t modules[CFG_PB_MOD_MAX];
+// stolen from lib/pico-sdk/src/rp2_common/hardware_i2c/i2c.c
+#define i2c_reserved_addr(addr) (((addr) & 0x78) == 0 || ((addr) & 0x78) == 0x78)
size_t modules_size = 0;
-static void state_exchange() {
+static void bus_scan() {
+ pb_buf_t buf = pb_send_magic_req();
+
+ // check for all 7-bit addresses
+ uint16_t addr_max = 1 << 7;
+ for (uint16_t addr = 0x00; addr < addr_max; addr++) {
+ if (i2c_reserved_addr(addr)) continue;
+ if (addr == PB_MOD_ADDR) continue;
+
+ pb_i2c_send(addr, (uint8_t *) buf.data, buf.size);
+ }
+
+ pb_buf_free(&buf);
+}
+
+static void update_state() {
+ int idle = 0, playing = 0, solved = 0;
+
+ // count # of modules in each state
for (size_t i = 0; i < modules_size; i++) {
- pb_buf_t buf = pb_send_state_req();
-
- pb_buf_free(&buf);
+ pb_global_state_t state = modules[i].state;
+ if (state == PB_GS_IDLE) idle++;
+ else if (state == PB_GS_PLAYING) playing++;
+ else if (state == PB_GS_SOLVED) solved++;
+ }
+
+ if (idle == modules_size) { // if all modules are in PB_GS_IDLE
+ pb_hook_mod_state_write(PB_GS_IDLE);
+ } else if (solved == modules_size) { // if all modules are in PB_GS_SOLVED
+ pb_hook_mod_state_write(PB_GS_SOLVED);
+ } else {
+ pb_hook_mod_state_write(PB_GS_PLAYING);
}
+
+ // if a module is still playing, don't promote a next one to playing module
+ if (playing > 0) return;
+
+ for (size_t i = 0; i < modules_size; i++) {
+ // find first module that is idle
+ pb_global_state_t module_state = modules[i].state;
+ if (module_state != PB_GS_IDLE) continue;
+
+ pb_buf_t buff = pb_send_state_set(PB_GS_PLAYING);
+ pb_i2c_send(modules[i].sender, (uint8_t*)buff.data, buff.size);
+ pb_buf_free(&buff);
+ }
+}
+
+static void state_exchange() {
+ update_state();
+ pb_buf_t buf = pb_send_state_req();
+ for (size_t i = 0; i < modules_size; i++)
+ pb_i2c_send(modules[i].sender, (uint8_t *) buf.data, buf.size);
+ pb_buf_free(&buf);
}
void bus_task() {
// do a scan of the bus
bus_scan();
- // FIXME: this should be removed (see handover: RP2040 I2C limitations)
- // wait for 5 seconds until all handshake responses are received
- pb_mod_blocking_delay_ms(5e3);
-
while(1) {
// send my state to all puzzle modules
state_exchange();
// wait 1 second
- pb_mod_blocking_delay_ms(1e3);
+ vTaskDelay(1e3 / portTICK_PERIOD_MS);
}
}
@@ -54,7 +100,25 @@ void bus_task() {
*/
void pb_route_cmd_magic_res(pb_msg_t * msg) {
if (modules_size == CFG_PB_MOD_MAX) return;
- modules[modules_size++] = msg->sender;
+ pb_puzzle_module_t tmp_module = {msg->sender, PB_GS_NOINIT};
+ modules[modules_size++] = tmp_module;
printf("i2c: registered puzzle module w/ address 0x%02x\n", msg->sender);
}
+void pb_route_cmd_state_res(pb_msg_t * msg) {
+ pb_cmd_state_t * cmd = msg->cmd;
+ i2c_addr_t sender = msg->sender;
+
+ for (size_t i = 0; i < modules_size; i++) {
+ if (modules[i].sender != sender) continue;
+ modules[i].state = cmd->state;
+ }
+}
+
+pb_global_state_t pb_hook_mod_state_read() {
+ return _global_state;
+}
+
+void pb_hook_mod_state_write(pb_global_state_t state) {
+ _global_state = state;
+}
diff --git a/main/index.dox b/main/index.dox
index 0fee58a..54c5b15 100644
--- a/main/index.dox
+++ b/main/index.dox
@@ -1,13 +1,13 @@
// vim:ft=doxygen
/**
\ingroup main
-\defgroup main_tasks tasks
-\brief Tasks
+\defgroup main_tasks Tasks
+\brief FreeRTOS tasks
*/
/**
\ingroup main
-\defgroup main_pb_override overrides
+\defgroup main_pb_override Overrides
\brief Override functions from \ref pbdrv-mod
*/
diff --git a/main/init.c b/main/init.c
index 6d29d19..25fa5e3 100644
--- a/main/init.c
+++ b/main/init.c
@@ -7,7 +7,7 @@
#include "config.h"
#include "init.h"
#include "tasks.h"
-#include "pbdrv.h"
+#include "pb-mod.h"
static void init_stdio() {
stdio_init_all();
@@ -29,11 +29,15 @@ static void init_wifi() {
}
static void init_i2c() {
- gpio_set_function(CFG_SDA_PIN, GPIO_FUNC_I2C);
- gpio_set_function(CFG_SCL_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(CFG_SDA0_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(CFG_SCL0_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(CFG_SDA1_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(CFG_SCL1_PIN, GPIO_FUNC_I2C);
- gpio_pull_up(CFG_SDA_PIN);
- gpio_pull_up(CFG_SCL_PIN);
+ gpio_pull_up(CFG_SDA0_PIN);
+ gpio_pull_up(CFG_SCL0_PIN);
+ gpio_pull_up(CFG_SDA1_PIN);
+ gpio_pull_up(CFG_SCL1_PIN);
pb_setup();
}
diff --git a/main/mod.c b/main/mod.c
index 11a1bb7..8650861 100644
--- a/main/mod.c
+++ b/main/mod.c
@@ -1,3 +1,6 @@
+#include <FreeRTOS.h>
+#include <task.h>
+
#include "pb.h"
#include "pb-mod.h"
diff --git a/main/pbdrv.c b/main/pbdrv.c
deleted file mode 100644
index 0624897..0000000
--- a/main/pbdrv.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "pb.h"
-
-#include "pb.h"
-#include "pb-types.h"
-#include "pb-mod.h"
-#include "pb-send.h"
-
-#include <hardware/i2c.h>
-#include <hardware/gpio.h>
-#include <pico/i2c_slave.h>
-#include <pico/stdio.h>
-
-#include <FreeRTOS.h>
-#include <stdio.h>
-#include <timers.h>
-
-#define PB_I2C i2c0
-#define BUF_SIZE 256
-#define MSGS_MAX 4
-
-typedef struct {
- uint8_t data[BUF_SIZE];
- size_t size;
-} i2c_msg_buf_t;
-
-static i2c_msg_buf_t msgs[MSGS_MAX];
-static size_t msg_index = 0;
-
-static void async_pb_i2c_recv(void * _msg, uint32_t _) {
- i2c_msg_buf_t * msg = _msg;
- pb_i2c_recv(msg->data, msg->size);
-}
-
-static void msg_complete(i2c_msg_buf_t * msg) {
- // defer pb_i2c_recv call to FreeRTOS scheduler as pb_i2c_recv takes
- // too long to return from an ISR
- xTimerPendFunctionCallFromISR(async_pb_i2c_recv, msg, 0, NULL);
-
- // prepare next message for use
- msg_index = (msg_index + 1) % MSGS_MAX;
- msgs[msg_index].size = 0;
-}
-
-// This function is called from the I2C ISR
-static void recv_event(i2c_inst_t *i2c, i2c_slave_event_t event) {
- i2c_msg_buf_t * msg = &msgs[msg_index];
-
- switch (event) {
- case I2C_SLAVE_RECEIVE: {
- if (msg->size == BUF_SIZE) return;
- msg->data[msg->size++] = i2c_read_byte_raw(PB_I2C);
- break;
- }
- case I2C_SLAVE_FINISH: {
- msg_complete(msg);
- break;
- }
- default: break;
- }
-}
-
-void pb_setup() {
- i2c_init(PB_I2C, PB_CLOCK_SPEED_HZ);
- i2c_slave_init(PB_I2C, PB_MOD_ADDR, &recv_event);
-}
-
-__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) {
- i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR);
-
- // false to write stop condition to i2c bus
- i2c_write_timeout_us(PB_I2C, addr, buf, sz, false, PB_TIMEOUT_US);
-
- i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR);
-}
-
-void bus_scan() {
- i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR);
-
- pb_buf_t buf = pb_send_magic_req();
-
- // check for all 7-bit addresses
- uint16_t addr_max = 1 << 7;
- for (uint16_t addr = 0x00; addr < addr_max; addr++) {
- i2c_write_timeout_us(PB_I2C, addr, (uint8_t *) buf.data, buf.size, false, PB_TIMEOUT_US);
- }
-
- pb_buf_free(&buf);
-
- i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR);
-}
-
-void pb_mod_blocking_delay_ms(unsigned long ms) {
- vTaskDelay(ms / portTICK_PERIOD_MS);
-}
-
diff --git a/main/pbdrv.h b/main/pbdrv.h
deleted file mode 100644
index 9496aa9..0000000
--- a/main/pbdrv.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-#include "pb-types.h"
-
-/**
- * \ingroup pb_drv
- * \defgroup pb_drv_rp2040 RP2040
- * \brief Raspberry Pi Pico and Pico W driver
- *
- * \note This file is no longer inside `lib/pbdrv/drv/rp2040` as it is tightly
- * coupled to both the pico-sdk and FreeRTOS functions. I have tried to get
- * FreeRTOS to play nicely with the CMake subproject layout, but the pico-sdk
- * and the RP2040 port of FreeRTOS both rely on CMake's import() functionality,
- * which makes using FreeRTOS in a libary context extremely messy.
- *
- * \warning The workaround implemented in this driver was already kind of
- * messy, and **a different microcontroller should be used for the main
- * controller instead**. Please see the handover document for more details.
- *
- * \{
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//! Puzzle bus driver setup
-void pb_setup();
-
-/**
- * \note While the RP2040's datasheet claims it supports multi-master
- * configurations by implementing bus arbitration, it does not natively support
- * a mode where it is configured as a (multi-)master with a slave address, such
- * that it can be addressed by other multi-masters. This function includes a
- * hacky workaround that teporarily sets the RP2040 to I2C master mode to send
- * a message, and then restores it back to slave mode.
- *
- * \warning This approach results in some received frames being (partially)
- * dropped in the time period between the invocation of this function and the
- * bus becoming idle (and the message is sent).
- */
-void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz);
-
-/**
- * \brief Scan the bus for I2C slaves, and send handshake messages to ACK-ing
- * slaves.
- *
- * \note As a result of the RP2040 hardware limitations, this function is also
- * implemented in this file, even though it does not belong to the puzzle bus
- * driver.
- *
- * \warning In order to not miss any handshake responses, the bus should remain
- * busy during the entire scan. The \c nostop parameter of the \c
- * i2c_write_timeout_us() function from the pico-sdk does not seem to keep the
- * bus busy.
- */
-void bus_scan();
-
-/// \}
-
-#ifdef __cplusplus
-}
-#endif
-
diff --git a/main/readme.md b/main/readme.md
index 85a3fca..97150eb 100644
--- a/main/readme.md
+++ b/main/readme.md
@@ -8,17 +8,7 @@ This directory contains the software for the main controller of the Puzzle Box.
## building
1. make sure the submodules are initialized
-2. create a `config.h` file and define some options (see `config.def.h` for all
- options):
- ```c
- #pragma once
-
- #define CFG_NET_SSID "network name"
- #define CFG_NET_PASS "network password"
- #define CFG_NET_AUTH CYW43_AUTH_WPA2_AES_PSK
-
- #include "config.def.h"
- ```
+2. create a `config.h` file (see \ref main_config "config")
3. use CMake to build
## flashing
diff --git a/puzzle/dummy/CMakeLists.txt b/puzzle/dummy/CMakeLists.txt
index 63c4449..7edce2b 100644
--- a/puzzle/dummy/CMakeLists.txt
+++ b/puzzle/dummy/CMakeLists.txt
@@ -11,10 +11,10 @@ add_compile_definitions(DEBUG)
# arduino
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/lib/Arduino-CMake-Toolchain/Arduino-toolchain.cmake)
-# set(ARDUINO_BOARD "Arduino Uno [avr.uno]")
-set(ARDUINO_BOARD "Arduino Mega or Mega 2560 [avr.mega]")
+set(ARDUINO_BOARD "Arduino Uno [avr.uno]")
+# set(ARDUINO_BOARD "Arduino Mega or Mega 2560 [avr.mega]")
-# freertos
+# freertos (used for memory management only)
add_library(freertos_config INTERFACE)
target_include_directories(freertos_config SYSTEM INTERFACE .)
set(FREERTOS_PORT GCC_ATMEGA)
@@ -28,6 +28,7 @@ set(FREERTOS_HEAP 4)
project(pb_mod_dummy C CXX)
add_subdirectory(lib/pbdrv)
+include(lib/pbdrv/ext/freertos/include.cmake)
add_subdirectory(lib/FreeRTOS-Kernel)
add_executable(main
diff --git a/puzzle/dummy/FreeRTOSConfig.h b/puzzle/dummy/FreeRTOSConfig.h
index 1cfdd71..1d98f78 100644
--- a/puzzle/dummy/FreeRTOSConfig.h
+++ b/puzzle/dummy/FreeRTOSConfig.h
@@ -17,8 +17,8 @@
#define configSTACK_DEPTH_TYPE uint16_t
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
-// #define configTOTAL_HEAP_SIZE (1024)
-#define configTOTAL_HEAP_SIZE (7 * 1024)
+#define configTOTAL_HEAP_SIZE (1024)
+// #define configTOTAL_HEAP_SIZE (7 * 1024)
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
diff --git a/puzzle/dummy/makefile b/puzzle/dummy/makefile
index 26e9157..6b8341b 100644
--- a/puzzle/dummy/makefile
+++ b/puzzle/dummy/makefile
@@ -3,6 +3,6 @@ TARGET = $(BUILD_DIR)/main.elf
include ../../lazy.mk
export SERIAL_PORT ?= /dev/ttyACM0
-flash: upload-main;
-upload-main: $(TARGET)
+flash: upload;
+upload: $(TARGET)
diff --git a/puzzle/vault/main.cpp b/puzzle/vault/main.cpp
index 76e5f65..88d5b35 100644
--- a/puzzle/vault/main.cpp
+++ b/puzzle/vault/main.cpp
@@ -1,6 +1,7 @@
#include <Arduino.h>
-#include <Wire.h>
#include <TM1637Display.h>
+#include "lib/pbdrv/pb-types.h"
+#include "lib/pbdrv/pb-mod.h"
#define TOTAL_LEVELS 5
#define ROWS 4
@@ -17,36 +18,50 @@ const int ROW_PINS[ROWS] = {7, 6, 5, 4};
const int COL_PINS[COLS] = {10, 9, 8};
const char* validButtons[TOTAL_LEVELS] = {"A2", "B1", "D3", "C2", "C3"};
const char bombCode[] = "1234";
+const uint8_t SEGMENT_MAP[] = {
+ 0b00111111, // 0
+ 0b00000110, // 1
+ 0b01011011, // 2
+ 0b01001111, // 3
+ 0b01100110, // 4
+ 0b01101101, // 5
+ 0b01111101, // 6
+ 0b00000111, // 7
+ 0b01111111, // 8
+ 0b01101111, // 9
+ 0b01110111, // A
+ 0b01111100, // B
+ 0b00111001, // C
+ 0b01011110, // D
+ 0b01111001, // E
+ 0b01110001 // F
+ // Add other letters if needed
+};
+
+// This array of level codes matches the codes you might display per level.
+const char* levelCodes[TOTAL_LEVELS] = {"A1", "B2", "D1", "C3", "A2"};
+
+
+// Puzzle state
+pb_global_state_t puzzleState = PB_GS_NOINIT;
TM1637Display display(CLK, DIO);
-typedef enum {
- STATE_UNINITIALIZED = 0x00,
- STATE_RESET = 0x01,
- STATE_PLAYING = 0x02,
- STATE_SOLVED = 0x03,
- STATE_ERROR = 0x04
-} PuzzleState;
-
-PuzzleState puzzleState = STATE_UNINITIALIZED;
int currentLevel = 0;
-void requestEvent() {
- if (puzzleState == STATE_PLAYING) {
- uint8_t responseData[] = HANDSHAKE_SEND;
- Wire.write(responseData, sizeof(responseData));
- Serial.println("Handshake response sent.");
+void blink_display(int num) {
+ if (num == 1) {
+ // Display "1111" with leading zeros shown if necessary
+ display.showNumberDecEx(1111, 0b11111111, true);
+ } else if (num == 0) {
+ // Display "0000" with leading zeros shown if necessary
+ display.showNumberDecEx(0, 0b11111111, true);
}
+ delay(500);
+ display.clear();
+ delay(500);
}
-void blink_display(char num) {
- while(puzzleState == STATE_UNINITIALIZED || puzzleState == STATE_ERROR) {
- display.showNumberDecEx(0, 0b11111111, true); // Display "0000" with all digits on
- delay(500);
- display.clear();
- delay(500);
- }
-}
void display_final_code(const char* code) {
uint8_t segs[4] = {0, 0, 0, 0};
@@ -73,12 +88,10 @@ void check_button_press() {
if (strcmp(keyPress, validButtons[currentLevel]) == 0) {
currentLevel++;
if (currentLevel >= TOTAL_LEVELS) {
- puzzleState = STATE_SOLVED;
+ pb_hook_mod_state_write(PB_GS_SOLVED);
Serial.println("Puzzle solved!");
display.showNumberDec(currentLevel + 1, true);
digitalWrite(SOLVED_PIN, HIGH);
- } else {
- puzzleState = STATE_PLAYING;
}
} else {
currentLevel = 0;
@@ -102,64 +115,77 @@ void initialize_system() {
Serial.println("GPIO and display initialized.");
}
-void receiveEvent(int howMany) {
- if (howMany == 6) {
- uint8_t expectedBytes[] = HANDSHAKE_RECEIVED;
- uint8_t receivedBytes[6];
- bool match = true;
-
- for (int i = 0; i < 6; i++) {
- receivedBytes[i] = Wire.read();
- if (receivedBytes[i] != expectedBytes[i]) {
- match = false;
- break;
- }
- }
+void display_code_for_level(int level) {
+ char code[3] = {0}; // Temp storage for level code
+ strncpy(code, levelCodes[level], 2); // Copy the level-specific code
- if (match) {
- Serial.println("Correct handshake data received.");
- puzzleState = STATE_PLAYING;
- initialize_system();
+ uint8_t segs[4] = {0}; // Segments to send to the display
+
+ // Check if the first character is a letter and map it
+ if (isalpha(code[0])) {
+ if (code[0] >= 'A' && code[0] <= 'F') {
+ segs[0] = SEGMENT_MAP[code[0] - 'A' + 10]; // Maps A-F to their segment patterns
} else {
- Serial.println("Incorrect handshake data received.");
- puzzleState = STATE_ERROR;
+ // Handle unexpected characters or extend SEGMENT_MAP for more letters
+ segs[0] = 0; // Display nothing for undefined letters
}
} else {
- Serial.print("Received wrong number of bytes: ");
- Serial.println(howMany);
- puzzleState = STATE_ERROR;
+ // Assume it's a number and map directly
+ segs[0] = SEGMENT_MAP[code[0] - '0'];
}
+
+ // Check if the second character is a digit and map it
+ if (isdigit(code[1])) {
+ segs[1] = SEGMENT_MAP[code[1] - '0'];
+ } else {
+ // Handle unexpected characters
+ segs[1] = 0; // Display nothing for undefined digits
+ }
+
+ // Set only the first two segments, leave others blank
+ display.setSegments(segs, 2, 0); // Display on leftmost two digits
}
+pb_global_state_t pb_hook_mod_state_read() {
+ return puzzleState;
+}
+
+void pb_hook_mod_state_write(pb_global_state_t state) {
+ puzzleState = state;
+}
+
+void pb_hook_ev_main_state_update(pb_global_state_t state){
+ Serial.println("WE IN BOISS");
+}
+
void setup() {
Serial.begin(115200);
pinMode(SOLVED_PIN, OUTPUT);
digitalWrite(SOLVED_PIN, LOW);
- display.setBrightness(0x0f);
-
- Wire.begin(I2C_MODULE_ADDRESS);
- Wire.onRequest(requestEvent);
- Wire.onReceive(receiveEvent);
-
- // Initialize display with blinking zeros to indicate no connection or initialization state
- blink_display('0');
+ display.setBrightness(0x0f);
+ initialize_system();
}
void loop() {
switch(puzzleState) {
- case STATE_PLAYING:
+ case PB_GS_PLAYING:
+ display_code_for_level(currentLevel);
check_button_press();
delay(100);
break;
- case STATE_SOLVED:
+ case PB_GS_SOLVED:
+ Serial.println("STATE = PB_GS_SOLVED");
display_final_code(bombCode);
digitalWrite(SOLVED_PIN, HIGH);
- Serial.println("Final display shown. Puzzle complete.");
break;
- case STATE_ERROR:
- case STATE_UNINITIALIZED:
- blink_display('0');
+ case PB_GS_NOINIT:
+ Serial.println("STATE = PB_GS_NOINIT");
+ blink_display(0);
+ break;
+ case PB_GS_IDLE:
+ Serial.println("STATE = PB_GS_IDLE");
+ blink_display(1);
break;
}
-} \ No newline at end of file
+}
diff --git a/puzzle/vault/makefile b/puzzle/vault/makefile
index 26e9157..f33ca7e 100644
--- a/puzzle/vault/makefile
+++ b/puzzle/vault/makefile
@@ -2,7 +2,7 @@ TARGET = $(BUILD_DIR)/main.elf
include ../../lazy.mk
-export SERIAL_PORT ?= /dev/ttyACM0
-flash: upload-main;
-upload-main: $(TARGET)
+export SERIAL_PORT ?= /dev/ttyUSB0
+flash: upload;
+upload: $(TARGET)