diff options
-rw-r--r-- | Doxyfile | 1 | ||||
-rw-r--r-- | docs/design.adoc | 79 | ||||
-rw-r--r-- | docs/doxygen-layout.xml | 62 | ||||
-rw-r--r-- | docs/handover.adoc | 33 | ||||
-rw-r--r-- | docs/research.adoc | 118 | ||||
-rw-r--r-- | docs/share/refs.bib | 27 | ||||
-rw-r--r-- | lib/pbdrv/pb-route.h | 4 | ||||
-rw-r--r-- | main/i2c.c | 11 | ||||
-rw-r--r-- | main/i2c.h | 1 | ||||
-rw-r--r-- | main/index.dox | 7 | ||||
-rw-r--r-- | puzzle/dummy/CMakeLists.txt | 7 | ||||
-rw-r--r-- | puzzle/dummy/makefile | 12 | ||||
-rw-r--r-- | puzzle/neo/CMakeLists.txt | 44 | ||||
-rw-r--r-- | puzzle/neo/FreeRTOSConfig.h | 52 | ||||
-rw-r--r-- | puzzle/neo/console-neopuzzle/neo.cpp | 100 | ||||
-rw-r--r-- | puzzle/neo/index.dox | 8 | ||||
l--------- | puzzle/neo/lib | 1 | ||||
-rw-r--r-- | puzzle/neo/main.cpp (renamed from puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino) | 79 | ||||
-rw-r--r-- | puzzle/neo/makefile | 8 | ||||
-rw-r--r-- | puzzle/neo/mod.c | 6 | ||||
-rw-r--r-- | puzzle/readme.md | 15 | ||||
-rw-r--r-- | puzzle/vault/CMakeLists.txt | 12 | ||||
-rw-r--r-- | puzzle/vault/index.dox | 5 | ||||
-rw-r--r-- | puzzle/vault/makefile | 12 | ||||
-rw-r--r-- | puzzle/vault/mod.c | 4 | ||||
-rw-r--r-- | readme.md | 79 |
26 files changed, 499 insertions, 288 deletions
@@ -2,6 +2,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "puzzlebox" OUTPUT_DIRECTORY = doxygen +LAYOUT_FILE = docs/doxygen-layout.xml INPUT += readme.md INPUT += lib/mpack diff --git a/docs/design.adoc b/docs/design.adoc index 9e48fd4..6df83b6 100644 --- a/docs/design.adoc +++ b/docs/design.adoc @@ -13,7 +13,7 @@ structure, and has three levels of design 'depth': Only design details deemed relevant by the document authors are documented here. Low-level implementation details such as API interfaces, code paths and -workarounds are documented inside the source code repository. +workarounds are documented with Doxygen cite:[pbdox]. [[sec:lv1]] == Top-Level @@ -34,8 +34,9 @@ main controller and multiple puzzle modules. Other notable details include: time of writing (2024-03-11), and this project only describes the interface between the puzzle box and the bomb. * The puzzle box is capable of bidirectional communication over Wi-Fi. This - connection is used to configure the puzzle box before gameplay or modify its - state during gameplay. + connection is used to configure the puzzle box before gameplay or modify its + state during gameplay (<<reqs.adoc#req:edge-manual-reset>>, + <<reqs.adoc#req:edge-skip-puzzle>>). [[fig:system-top]] .Context block diagram @@ -92,8 +93,11 @@ The criteria for a puzzle module controller are: The research document cite:[research] compares various microcontrollers matching these criteria. As a result of this research, the Microchip PIC16F15276 was selected as the recommended microcontroller for future puzzle -modules. The current development hardware utilizes an ESP32-PICO-D4 module, so -the puzzle module software is written with portability in mind. +modules. + +NOTE: The current development hardware still utilizes an ESP32-PICO-D4 module, +but due to a misunderstanding cite:[handover], Arduino boards were used to +implement the puzzle modules. [[fig:puzzle-module-top]] .Generic puzzle module top-level block diagram @@ -149,10 +153,9 @@ Pi Pico W as main controller during development. NOTE: This was written while we did not know the puzzle bus specifically requires slave-addressible I^2^C multi-master controllers to function properly. -While the research concludes the RP2040 is a suitable microcontroller for the -main controller, it is not. The RP2040 was still used, but has required -implementing workarounds. Please see the handover report for more details on -how this impacted the project cite:[handover]. +The RP2040 was still used, but has required implementing workarounds. Please +see the handover report for more details on how this impacted the project +cite:[handover]. [[fig:main-controller-top]] .Main controller top-level block diagram @@ -187,8 +190,10 @@ multi-master I^2^C bus, and all puzzle modules are specified to be I^2^C multi-master controllers that are slave-addressible. The multi-master part is required to prevent I^2^C transmissions from being corrupted in the event of a bus collision, and the slave-addressible part is required to both send and -receive messages on the same controller. This is the only hardware-level -specification made this year. +receive messages on the same controller. This can also be achieved by using 2 +I^2^C peripherals on the same bus simultaniously, which is what the RP2040 +currently uses. This has required changes to the wiring, and is the only +hardware-level specification made this year. More details on the messages sent over the puzzle bus are described in <<sec:lv3-pb-messages>>. @@ -253,7 +258,7 @@ development of future puzzle modules (<<reqs.adoc#req:main-static>>). === Puzzle module framework This subsection defines aspects of the 'puzzle framework': the interface that -allows puzzle modules to integrate with the puzzle bus main controller. All +allows puzzle modules to integrate with the puzzle bus and main controller. All communication described within this subsection refers to 'internal' communication between the main controller and puzzle modules on the puzzle bus. @@ -295,7 +300,7 @@ is shown in <<tab:pb-msg-fmt>>. The messages are (de)serialized using mpack. This choice was made after considering various alternative options for sending structured messages cite:[research]. Note that all messages are sent as I^2^C writes. Due to this, the I^2^C address of a message sender is included in the -header. +header to facilitate network-like features over I^2^C. [[tab:pb-msg-fmt]] .Puzzle bus message format @@ -332,7 +337,8 @@ is solved. | ``PROP`` | The PROP command type is used for exchanging arbitrary data between puzzle modules and/or the puzzle box client (pbc) over the <<sec:main-bridge,TCP -bridge>>. These properties are not used by the puzzle framework. +bridge>>. These properties are not used by the puzzle framework, and serve as +an extensible interface for puzzle module developers to use. |=== <<tab:pb-msg-actions>> lists the different command actions. @@ -362,7 +368,7 @@ main controller firmware (<<reqs.adoc#req:main-static>>). The specific format of the 'cmd' field from <<tab:pb-msg-fmt>> is different for each command type, and is for this reason only documented in-code using -Doxygen. +Doxygen cite:[pbdox]. [[sec:framework-state]] ==== State @@ -401,16 +407,15 @@ expansion without modification of the main controller software The RPI Pico SDK prohibits the use of I^2^C addresses reserved by the I^2^C specification. This means different addresses from previous years are used. -These addresses are indexed in the code under a shared header (see -``lib/pbdrv/pb.h``). - -The same I^2^C address may be used by two different puzzle modules, but this -will make it impossible for them to be used simultaniously. +These addresses are indexed in the code in a header exposed by the puzzle bus +driver cite:[pbdox]. The I^2^C addresses are also used to determine the puzzle sequence (i.e. the order in which puzzle modules are set to the 'playing' state). The sequence is determined by the main controller on startup, and consists of the connected puzzle modules' addresses in descending order (i.e. highest address first). +Note that the same I^2^C address may be used by two different puzzle modules, +but this will make it impossible for them to be used simultaniously. === Main Controller @@ -423,14 +428,15 @@ startup. Puzzle modules start in the 'uninitialized' state (see <<fig:puzzle-module-common-state>>), during which they do nothing. Puzzle modules in this state are still able to reply to requests, including MAGIC REQ commands. When the main controller receives a MAGIC RES command, the I^2^C -address of the sender is added to an internal list for puzzle modules. +address of the sender is added to an internal list of I^2^C devices that are +considered puzzle modules. After the initial handshake request 'wave' (bus scan), all puzzle modules are repeatedly asked for their global state using a STATE REQ command. This request also includes the global state of the requesting puzzle module, which is always the main controller (under normal circumstances). Upon receiving the first STATE REQ command, a puzzle module knows it has been registered successfully by -the main controller, and may transition into the 'idle' state. +the main controller, and transitions into the 'idle' state. [[fig:sequence-puzzle-module-init]] .Puzzle module initialization sequence diagram @@ -479,10 +485,7 @@ Due to the separation of the puzzle bus driver code into a standalone library for reading/writing puzzle bus commands, and a puzzle module-specific code, the puzzle box client is able to read/write raw I^2^C commands directly. A separate library was made for serializing I^2^C messages so they can be sent over the -TCP connection. - -Detailed specifications on the TCP socket server are in -<<sec:lv3-remote-control>>. +TCP connection. This library is documented in detail using Doxygen cite:[pbdox]. ==== Operating system @@ -494,13 +497,6 @@ choice was between FreeRTOS and Zephyr. FreeRTOS was chosen because it is the simplest solution, and because the features Zephyr offers over FreeRTOS are already present in the Raspberry Pi Pico SDK. -NOTE: Due to the issues with the RP2040 that were later discovered -cite:[handover], delays are used within the puzzle bus driver's message -handling logic. This means that due to the use of the RP2040, *all puzzle -modules* are required to use a task scheduler or similar mechanism for -deferring calls to the puzzle bus driver from the I^2^C interrupt service -routine (ISR). - === NeoTrellis puzzle This subsection defines aspects of the 'NeoTrellis puzzle' module and gives a @@ -515,8 +511,8 @@ button. The way to solve this puzzle is by dimming every Neopixel in the 8x8 matrix. This is done by clicking on a button, which switches the state of the Neopixel underneath the pixel and the Neopixels in each cardinal direction from the pressed button. This means that if a Neopixel was on and the button was -pressed it will turn off and vice-versa. A visual example can be found in -Appendix B. +pressed it will turn off and vice-versa. +// A visual example can be found in Appendix B. ==== Puzzle inputs & outputs @@ -544,7 +540,8 @@ have their own logical circuit engraved in the box, and the 6 input ports on the right side of the puzzle have a letter (A through F) engraved in the box. The way to solve the puzzle is by connecting the banana plug cable from an input port on the left side of the puzzle to the corresponding input port on -the right side of the puzzle. An example of this can be found in Appendix C. +the right side of the puzzle. +// An example of this can be found in Appendix C. When the puzzle starts, the participants of the game will have 6 code-fragments written on paper, corresponding to the logical circuits on the puzzle box. The @@ -605,14 +602,4 @@ clicked the vault resets and they need to start over from the beginning. .Vault puzzle in-out image::img/vault-io.png[] -== Components -[[sec:lv3-remote-control]] -=== Remote control - -[appendix] -== NeoTrellis puzzle example - -[appendix] -== Software puzzle example - include::share/footer.adoc[] diff --git a/docs/doxygen-layout.xml b/docs/doxygen-layout.xml new file mode 100644 index 0000000..9ac2aaa --- /dev/null +++ b/docs/doxygen-layout.xml @@ -0,0 +1,62 @@ +<!-- + This file is largely based off /templates/general/layout_default.xml from + <https://github.com/doxygen/doxygen>. The official documentation for this + XML file is at <https://www.doxygen.nl/manual/customize.html>. +--> +<doxygenlayout version="1.0"> + <navindex> + <tab type="mainpage" visible="yes" title="Index"/> + <tab type="topics" visible="yes" title="Components"/> + </navindex> + <group> + <briefdescription visible="yes"/> + <detaileddescription visible="yes"/> + <groupgraph visible="yes"/> + <memberdecl> + <nestedgroups visible="yes" title="Components"/> + <modules visible="yes"/> + <dirs visible="yes"/> + <files visible="yes"/> + <namespaces visible="yes"/> + <concepts visible="yes"/> + <classes visible="yes"/> + <defines/> + <typedefs/> + <sequences/> + <dictionaries/> + <enums/> + <enumvalues/> + <functions/> + <variables/> + <signals/> + <publicslots/> + <protectedslots/> + <privateslots/> + <events/> + <properties/> + <friends/> + <membergroups visible="yes"/> + </memberdecl> + <memberdef> + <pagedocs/> + <inlineclasses/> + <defines/> + <typedefs/> + <sequences/> + <dictionaries/> + <enums/> + <enumvalues/> + <functions/> + <variables/> + <signals/> + <publicslots/> + <protectedslots/> + <privateslots/> + <events/> + <properties/> + <friends/> + </memberdef> + <authorsection visible="yes"/> + </group> +</doxygenlayout> + diff --git a/docs/handover.adoc b/docs/handover.adoc index 77d5da0..efeec00 100644 --- a/docs/handover.adoc +++ b/docs/handover.adoc @@ -84,6 +84,8 @@ The current project state is as follows: ** two puzzle modules ('Vault' and 'NeoTrellis') integrated using the puzzle bus driver +Functionality: + * The main controller (a RPI Pico W) can interact with the different puzzle modules using a central shared I^2^C bus (referred to as the 'puzzle bus') * The main controller is able to find new puzzle modules on startup, and does @@ -92,6 +94,11 @@ The current project state is as follows: main controller through a TCP connection and allows control over various aspects of the puzzle box using simple commands. +Documentation: + +* These project documents +* Detailed usage and API documentation for all software modules cite:[pbdox] + == Incidents During this year's run of the project, we encountered several difficuties we @@ -142,10 +149,11 @@ Please note the following differences between I^2^C devices: regular I^2^C slave devices. - I^2^C multi-master controllers that are slave-addressable in master mode are the only kind of I^2^C controller suitable for use in puzzle modules. + Microcontrollers with 2 I^2^C peripherals on the same bus (one in master + mode, one in slave mode) can also be used to achieve the same effect. The RP2040 supports multi-master, but is not addressable as a slave in master -mode. Due to time constraints, this was mitigated using a workaround (see -<<fixme:rp2040-i2c>>). +mode. This was mitigated using a workaround (see <<fixme:rp2040-i2c>>). === Development hardware availability @@ -161,6 +169,12 @@ multiple people to develop using the same setup. Note that the RPI Pico is a special case, as it requires another Pico for debugging, effectively requiring double the amount of hardware to support developers. +Due to a misunderstanding, we also thought our development boards went missing +somewhere during week 13. Double-checking if project materials were actually +stolen, or making clear where the materials are stored by sending an image of +its location could have easily avoided this from happening; make sure to do +either. + === Auxiliary workarounds and technical limitations This section details workarounds that were implemented instead of being fixed @@ -170,10 +184,9 @@ mentioned in this section. [[fixme:rp2040-i2c,RP2040 I^2^C limitations]] <<fixme:rp2040-i2c>>:: -- All puzzle module drivers have a hard-coded 2 second delay between receiving - the MAGIC handshake request and the MAGIC handshake response handler. This - was done to ensure responses are not ignored by the RP2040 (main controller) - while it is still in I^2^C master mode. +- The RP2040 is not slave-addressable while in master mode. A workaround that + uses both I^2^C peripherals simultaniously was written to work around this + issue. Memory management on Arduino:: The Arduino's built-in memory management functions do not seem to work @@ -208,7 +221,7 @@ are critical for project success: - The RPI Pico (and Pico W)'s I^2^C peripheral supports multi-master, but does not support being addressed as a slave while in master mode. This is required for puzzle bus integration, and was mitigated using a workaround (see - <<fixme:rp2040-i2c>>). A replacement controller should be used instead. + <<fixme:rp2040-i2c>>). === Other suggestions @@ -216,13 +229,13 @@ These points are suggestions for future project groups. While we do not think these are critical to project success, we still think they are important to mention. -- Implement the hardware design from the year 22-23. +- The hardware design from the year 22-23 should be implemented. - The original game rules are described in a separate document from the year 20-21. - The RPI Pico W has programmable I/O modules. Due to time constraints, we did not research if these modules can be used to create a custom I^2^C peripheral (and driver) that allows multi-master communication while still being - addressable as a slave. If this is possible, the RPI Pico W can still be used - as main controller without the use of workarounds. + addressable as a slave. If this is possible, the RPI Pico W could be used + without the use of workarounds. include::share/footer.adoc[] diff --git a/docs/research.adoc b/docs/research.adoc index acab4ab..6f9b494 100644 --- a/docs/research.adoc +++ b/docs/research.adoc @@ -145,10 +145,9 @@ The Raspberry Pi Pico W board is utilized during development. NOTE: This was written while we did not know the puzzle bus specifically requires slave-addressible I^2^C multi-master controllers to function properly. -While the research concludes the RP2040 is a suitable microcontroller for the -main controller, it is not. The RP2040 was still used, but has required -implementing workarounds. Please see the handover report for more details on -how this impacted the project cite:[handover]. +The RP2040 was still used, but has required implementing workarounds. Please +see the handover report for more details on how this impacted the project +cite:[handover]. [[tab:main-mcu]] .Main controller MCU candidates @@ -411,6 +410,117 @@ testing. Including mocking tests, a large amount of assertions, multiple test with different input support, and lastly being supported in the newest non-experimental version of {cpp}. +== I^2^C (Thomas) + +=== Research question + +How can we use I^2^C for the puzzle module detection and communication? + +=== Puzzle Module and Main Controller Communication + +Research from project group 21/22 shows that the I^2^C protocol is the best +option for communication between the puzzle modules and the main controller. +This research section extends the previous section about which MCU is suitable +for the puzzle bus, as we have found vital I^2^C limitations with the +controller we had chosen. See the handover document for the found limitations. + +// TODO: REFERENCES +// TODO: Find synonym for 'vital' + +==== MCUs Supporting Master Addressable as Slave + +===== Atmega328p + +The Atmega328p has multi-master support, where the MCU is addressable as a +slave while being in master mode. This has been confirmed using the Arduino +wire library on both the Arduino Mega and the Arduino Uno. + +===== PIC16F15276 & ESP32 + +Both the PIC16F15276 and the ESP32 MCUs show possibilities to be addressable as a slave while being in master mode. However, at the moment of writing this +has yet to be tested. + +// TODO: Add the following reference: +// PIC16F15276 - 25.2.4.3 +// https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/PIC16F15256-74-75-76-Microcontroller-Data-Sheet-40002305.pdf + +// TODO: Add the following reference: +// https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/peripherals/i2c.html +// https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#i2c +// https://www.bitsandparts.nl/documentation/482/ESP32_Specifications_EN_v1.pdf + +==== Alternatives + +===== PIC16F15276 Registers + +In the case of the PIC16F15276 not support master addressable as slave the +following approach would most likely work. As the PIC16F15276 uses specific +registers for its master receive functions, namely the RCEN register, it can +be manually set to receive data from the I^2^C bus. However, this also has +yet to be tested. + +// TODO: Add the following reference: +// PIC16F15276 - 25.2.4.3 +// https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/PIC16F15256-74-75-76-Microcontroller-Data-Sheet-40002305.pdf + +===== Multiple I^2^C Peripherals + +==== ESP32 & RP2040 + +The ESP32 and the RP2040 both have multiple peripherals for I^2^C +communication, while also supporting simultaneous configuration. This allows +both two I^2^C peripherals to be active, one being configured as a master and +the other being configured as a slave. This enables the controller to send and +receive data to the I^2^C bus without much difficulty. This does introduce +increased code complexity but is a valid option if it is succesful in testing. + +// TODO: Add the following reference: +// https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/peripherals/i2c.html +// https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#i2c +// https://www.bitsandparts.nl/documentation/482/ESP32_Specifications_EN_v1.pdf + +=== Puzzle Module Detection + +Puzzle module detection is vital to the puzzelbox, as this allows changing the +puzzles without much software or hardware configuration needed. An option will +be given for the choice of main controller (RP2040); namely to scan the full +I^2^C bus for responsive slaves. The RPI Pico SDK has an API for I^2^C which +also supports functions create a bus scanning function. An example of this +bus scan function, according to the API examples, can be found in the pseudo +code below. + +[source, c] +---- +#include <stdio.h> +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +void bus_scan() { + int ret; + uint8_t rxdata; + + for (int addr = 0; addr < (1 << 7); ++addr) { + ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false); + printf(ret < 0 ? "." : "@"); + printf(addr % 16 == 15 ? "\n" : " "); + } + printf("Done.\n"); +} +---- + +The bus scan function tries to read data from all possible I^2^C addresses, +and prints a table which shows what the addresses are from found I^2^C +slaves. This is possible due to the i2c_read_blocking function, which returns +the length of the read data if the slave address is in use (in this case 1) or +a number below 0 if the slave address is not in use. The puzzelbox, however, +has the 'Neotrellis' puzzle which also uses I^2^C to function. The bus scan +function would also see the 'Neotrellis' rgb matrix as a puzzle module (slave) +using this implementation. This can easily be fixed using a handshake between +puzzle modules and the main controller, as the 'Neotrellis' rgb matrix cannot +answer this handshake and is therefor not recognized as a puzzle module. + +// TODO: references (API) & code block naming? + == Original Puzzle Box Functionality Research (Thomas) === Research question diff --git a/docs/share/refs.bib b/docs/share/refs.bib index 1c37465..299d621 100644 --- a/docs/share/refs.bib +++ b/docs/share/refs.bib @@ -200,3 +200,30 @@ publisher = {Avans University of Applied Sciences}, year = {2022}, } + +@online{Joh21, + author = {Johnston, P.}, + title = {Embedded systems testing resources}, + url = {https://embeddedartistry.com/blog/2018/10/18/embedded-systems-testing-resources/}, + month = jun, + msbib-day = {10}, + msbib-accessed = {2024-02-25}, + year = {2021}, +} + +@online{RPI23, + title = {Hardware APIs - Hardware I2C}, + url = {https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_i2c}, + month = jun, + msbib-day = {14}, + year = {2023}, + msbib-accessed = {2024-05-11}, +} + +@techreport{pbdox, + title = {puzzlebox Doxygen documentation}, + author = {Blansch, Loek Le and Hammer, Elwin and Faase, Lars and in 't Anker, Thomas}, + url = {https://media.pipeframe.xyz/puzzlebox/23-24/doxygen}, + year = {2024}, +} + diff --git a/lib/pbdrv/pb-route.h b/lib/pbdrv/pb-route.h index b80e4ec..2a28c0b 100644 --- a/lib/pbdrv/pb-route.h +++ b/lib/pbdrv/pb-route.h @@ -98,6 +98,7 @@ void pb_route_cmd_state_req(pb_msg_t * msg); * controller handles this type of command. */ void pb_route_cmd_state_res(pb_msg_t * msg); +// TODO: add link to pb_route_cmd_state_res handler in main/i2c.c /** * \brief Handle a \ref PB_CMD_STATE "STATE" message with action \ref * PB_ACTION_SET "SET" @@ -122,7 +123,8 @@ void pb_route_cmd_magic_req(pb_msg_t * msg); * PB_ACTION_RES "RES" * * The default implementation of this function is empty, as only the main - * controller handles this type of command. + * controller handles this type of command. (\ref main_route_cmd_magic_res + * "link") */ void pb_route_cmd_magic_res(pb_msg_t * msg); @@ -41,6 +41,17 @@ void bus_task() { } } +/** + * \ingroup main_pb_override + * \anchor main_route_cmd_magic_res + * + * This function registers the I2C address of the puzzle module that replied to + * the \c MAGIC \c REQ command into a list of "known puzzle modules", which are + * then periodically updated during gameplay. + * + * \note Up to \ref CFG_PB_MOD_MAX puzzle modules can be registered + * simultaniously. + */ void pb_route_cmd_magic_res(pb_msg_t * msg) { if (modules_size == CFG_PB_MOD_MAX) return; modules[modules_size++] = msg->sender; @@ -15,3 +15,4 @@ void bus_task(); /// \} + diff --git a/main/index.dox b/main/index.dox index aa2d07a..0fee58a 100644 --- a/main/index.dox +++ b/main/index.dox @@ -4,3 +4,10 @@ \defgroup main_tasks tasks \brief Tasks */ + +/** +\ingroup main +\defgroup main_pb_override overrides +\brief Override functions from \ref pbdrv-mod +*/ + diff --git a/puzzle/dummy/CMakeLists.txt b/puzzle/dummy/CMakeLists.txt index 6acc4c8..63c4449 100644 --- a/puzzle/dummy/CMakeLists.txt +++ b/puzzle/dummy/CMakeLists.txt @@ -17,8 +17,7 @@ set(ARDUINO_BOARD "Arduino Mega or Mega 2560 [avr.mega]") # freertos add_library(freertos_config INTERFACE) target_include_directories(freertos_config SYSTEM INTERFACE .) -# set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno -set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno +set(FREERTOS_PORT GCC_ATMEGA) set(FREERTOS_HEAP 4) # used for testing @@ -36,10 +35,10 @@ add_executable(main mod.c ) -target_link_libraries(main +target_link_libraries(main PUBLIC pbdrv-mod ) -target_link_arduino_libraries(main +target_link_arduino_libraries(main PUBLIC core Wire ) diff --git a/puzzle/dummy/makefile b/puzzle/dummy/makefile index 509d8e3..26e9157 100644 --- a/puzzle/dummy/makefile +++ b/puzzle/dummy/makefile @@ -6,15 +6,3 @@ export SERIAL_PORT ?= /dev/ttyACM0 flash: upload-main; upload-main: $(TARGET) -test: test_a test_b; - -test_a: - $(MAKE) -C . clean - $(MAKE) -E CMFLAGS+=-D\ CMAKE_CXX_FLAGS=-DTEST_A -C . - $(MAKE) -E SERIAL_PORT=/dev/ttyACM0 -C . flash - -test_b: - $(MAKE) -C . clean - $(MAKE) -E CMFLAGS+=-D\ CMAKE_CXX_FLAGS=-DTEST_B -C . - $(MAKE) -E SERIAL_PORT=/dev/ttyACM1 -C . flash - diff --git a/puzzle/neo/CMakeLists.txt b/puzzle/neo/CMakeLists.txt new file mode 100644 index 0000000..6c45f13 --- /dev/null +++ b/puzzle/neo/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.29) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +# enable debug features +set(CMAKE_BUILD_TYPE Debug) +add_compile_definitions(DEBUG) + +# arduino +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/lib/Arduino-CMake-Toolchain/Arduino-toolchain.cmake) +set(ARDUINO_BOARD "Arduino Mega or Mega 2560 [avr.mega]") + +# freertos +add_library(freertos_config INTERFACE) +target_include_directories(freertos_config SYSTEM INTERFACE .) +set(FREERTOS_PORT GCC_ATMEGA) +set(FREERTOS_HEAP 4) + +project(pb_mod_neo C CXX) + +add_subdirectory(lib/pbdrv) +add_subdirectory(lib/FreeRTOS-Kernel) + +add_executable(main + main.cpp + mod.c + ) + +target_link_libraries(main PUBLIC + pbdrv-mod + ) +target_link_arduino_libraries(main PUBLIC + core + Wire + Adafruit_seesaw + ) + +# fugly workaround +target_link_arduino_libraries(_arduino_lib_Adafruit_seesaw_Library PUBLIC "Adafruit BusIO") + +target_enable_arduino_upload(main) + diff --git a/puzzle/neo/FreeRTOSConfig.h b/puzzle/neo/FreeRTOSConfig.h new file mode 100644 index 0000000..c0acc49 --- /dev/null +++ b/puzzle/neo/FreeRTOSConfig.h @@ -0,0 +1,52 @@ +#pragma once + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ((configSTACK_DEPTH_TYPE) 192) +#define configUSE_16_BIT_TICKS 1 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TIME_SLICING 1 +#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 (5 * 1024) +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 0 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 92 + +#include <assert.h> +#define configASSERT(x) assert(x) + +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + diff --git a/puzzle/neo/console-neopuzzle/neo.cpp b/puzzle/neo/console-neopuzzle/neo.cpp deleted file mode 100644 index 56d90f7..0000000 --- a/puzzle/neo/console-neopuzzle/neo.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#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/neo/index.dox b/puzzle/neo/index.dox index 14cefdc..87822e0 100644 --- a/puzzle/neo/index.dox +++ b/puzzle/neo/index.dox @@ -3,4 +3,12 @@ \ingroup puz \defgroup puz_neo Neo \brief NeoTrellis puzzle module + +\par Setup +- Use the Arduino IDE library manager to install the "Adafruit seesaw Library" + library and its dependencies + +\warning There is another library named "Adafruit NeoTrellis M4 Library", this +is not the right library. + */ diff --git a/puzzle/neo/lib b/puzzle/neo/lib new file mode 120000 index 0000000..58677dd --- /dev/null +++ b/puzzle/neo/lib @@ -0,0 +1 @@ +../../lib
\ No newline at end of file diff --git a/puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino b/puzzle/neo/main.cpp index b334677..13a6859 100644 --- a/puzzle/neo/arduino-neopuzzle/arduino-neopuzzle.ino +++ b/puzzle/neo/main.cpp @@ -1,3 +1,4 @@ +#include <Arduino.h> #include <Wire.h> #include <Adafruit_NeoTrellis.h> @@ -22,6 +23,45 @@ enum NeoState { NeoState neoState = NEO_UNINITIALIZED; +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; +} + +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 setup() { Serial.begin(115200); while (!Serial); // Wait for Serial to be ready @@ -55,41 +95,4 @@ void setup() { 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; -} +}
\ No newline at end of file diff --git a/puzzle/neo/makefile b/puzzle/neo/makefile new file mode 100644 index 0000000..26e9157 --- /dev/null +++ b/puzzle/neo/makefile @@ -0,0 +1,8 @@ +TARGET = $(BUILD_DIR)/main.elf + +include ../../lazy.mk + +export SERIAL_PORT ?= /dev/ttyACM0 +flash: upload-main; +upload-main: $(TARGET) + diff --git a/puzzle/neo/mod.c b/puzzle/neo/mod.c new file mode 100644 index 0000000..7157d22 --- /dev/null +++ b/puzzle/neo/mod.c @@ -0,0 +1,6 @@ +#include "pb.h" +#include "pb-mod.h" + +const char * PB_MOD_NAME = "neotrellis"; +const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_NEOTRELLIS; + diff --git a/puzzle/readme.md b/puzzle/readme.md index 959c506..1b572ba 100644 --- a/puzzle/readme.md +++ b/puzzle/readme.md @@ -1,20 +1,15 @@ \defgroup puz puzzle \brief Puzzle modules -# puzzles - -This folder contains the source code for all puzzle modules. - ## Arduino-based puzzle modules > [!NOTE] > Because of the poorly designed hardware (21-22) used during development > (23-24), all puzzle modules ended up being developed using Arduino boards. -All libraries in this repository use CMake for building (for consistency), -which also means the Arduino based puzzle modules use CMake. The CMakeLists.txt -of some puzzles uses the [Arduino-CMake-Toolchain][arduino-cmake]. To build any -of these subfolders, make sure you have done the following: +The Arduino based puzzle modules also use CMake with the +[Arduino-CMake-Toolchain][arduino-cmake]. To build any of these puzzles, make +sure you have done the following: - Install the official Arduino IDE - Open "Tools" > "Board" > "Board manager" @@ -22,6 +17,8 @@ of these subfolders, make sure you have done the following: [arduino-cmake]: https://github.com/a9183756-gh/Arduino-CMake-Toolchain +<!-- + ## ESP-based puzzle modules ### ESP-IDF SDK Setup instructions @@ -34,3 +31,5 @@ of these subfolders, make sure you have done the following: - [For windows](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/windows-setup.html#get-started-windows-first-steps) - [For Linux](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/linux-macos-setup.html#get-started-linux-macos-first-steps) +--> + diff --git a/puzzle/vault/CMakeLists.txt b/puzzle/vault/CMakeLists.txt index b0f05f2..bc13c8c 100644 --- a/puzzle/vault/CMakeLists.txt +++ b/puzzle/vault/CMakeLists.txt @@ -7,26 +7,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # enable debug features set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) -# add_compile_options(-O0) # no optimizations # 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]") # freertos add_library(freertos_config INTERFACE) target_include_directories(freertos_config SYSTEM INTERFACE .) -# set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno -set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno +set(FREERTOS_PORT GCC_ATMEGA) set(FREERTOS_HEAP 4) -# used for testing -# set(ARDUINO_BOARD "Raspberry Pi Pico W [rp2040.rpipicow]") -# add_compile_definitions(USE_TINYUSB) -# include_directories(/home/loek/.arduino15/packages/rp2040/hardware/rp2040/3.9.2/libraries/Adafruit_TinyUSB_Arduino/src/arduino) - -project(pb_mod_dummy C CXX) +project(pb_mod_vault C CXX) add_subdirectory(lib/pbdrv) add_subdirectory(lib/FreeRTOS-Kernel) diff --git a/puzzle/vault/index.dox b/puzzle/vault/index.dox index b14e616..15eae1f 100644 --- a/puzzle/vault/index.dox +++ b/puzzle/vault/index.dox @@ -3,4 +3,9 @@ \ingroup puz \defgroup puz_vault Vault \brief Vault puzzle module + +\par Setup +- Use the Arduino IDE library manager to install the "TM1637" library (by + Avishay Orpaz) and its dependencies + */ diff --git a/puzzle/vault/makefile b/puzzle/vault/makefile index 509d8e3..26e9157 100644 --- a/puzzle/vault/makefile +++ b/puzzle/vault/makefile @@ -6,15 +6,3 @@ export SERIAL_PORT ?= /dev/ttyACM0 flash: upload-main; upload-main: $(TARGET) -test: test_a test_b; - -test_a: - $(MAKE) -C . clean - $(MAKE) -E CMFLAGS+=-D\ CMAKE_CXX_FLAGS=-DTEST_A -C . - $(MAKE) -E SERIAL_PORT=/dev/ttyACM0 -C . flash - -test_b: - $(MAKE) -C . clean - $(MAKE) -E CMFLAGS+=-D\ CMAKE_CXX_FLAGS=-DTEST_B -C . - $(MAKE) -E SERIAL_PORT=/dev/ttyACM1 -C . flash - diff --git a/puzzle/vault/mod.c b/puzzle/vault/mod.c index 058a585..bae8a3d 100644 --- a/puzzle/vault/mod.c +++ b/puzzle/vault/mod.c @@ -1,6 +1,6 @@ #include "pb.h" #include "pb-mod.h" -const char * PB_MOD_NAME = "dummy"; -const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_DUMMY; +const char * PB_MOD_NAME = "vault"; +const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_VAULT; @@ -5,63 +5,60 @@ implemented during the 2023-2024 run of the Puzzlebox project. This year's run of the project consists of only software students, and was developed using the hardware from the 21-22 run of the project. -Improved hardware was designed but not realised during the 22-23 run of the -project. This hardware is recommended for future groups participating in the -project. The software in this repository should be easily portable to various -other microcontrollers, and a recommendation is made in the [design -document](docs/design.adoc). +The software in this repository should be easily portable to various other +microcontrollers, and a recommendation is made in the [design +document](docs/design.adoc). Please see the [handover +document](docs/handover.adoc) first for more details. + +> [!NOTE] +> This project was mostly developed on Linux. All subfolders/modules/libraries +> use CMake, and *should* build cross-platform when using CMake+Ninja. This has +> not yet been verified. +> +> Please note that most subfolders use symbolic links to the [lib](lib/) +> folder, which may not work correctly if you clone the repository under a +> filesystem or operating system that does not support these. ## Documentation -This project is documented using Doxygen. To generate HTML docs, run +If you are viewing this page from Doxygen, please take a look at the +[components](topics.html) tab for a comprehensive list of components within +this project. + +Project documentation is available in the [docs](docs/) folder, and all code is +documented using Doxygen. To generate HTML docs, run ``` $ make doxygen ``` -and open <doxygen/html/index.html> in a browser. - -If you are viewing this page from Doxygen, please take a look at -[topics](/topics.html) for a comprehensive list of components within this -project. +and open [the generated HTML files](doxygen/html/index.html) in a browser. A +rendered copy of this documentation is also hosted unofficially at +[pipeframe](https://media.pipeframe.xyz/puzzlebox/23-24/doxygen). ## Tidyness -Please keep this repository tidy by being aware of the following conventions! - -### Folder structure - -``` -/client desktop PC application for controlling the puzzle box -/docs project documentation in AsciiDoc(tor) format -/lib custom libraries and submodules -├───/i2ctcp I2C over TCP protocol functions (used by main and client) -├───/mpack MsgPack CMake configuration and extension -└───/pbdrv puzzle bus driver (module driver + (de)serializing functions) -/main main controller (RPi pico) software -/puzzle/<name> puzzle sources, each puzzle has its own subdirectory -/shared (unused) shared code -/test unit tests -``` - -### 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. +Please keep this repository tidy by being aware of the following conventions: -There are also `.clang-tidy` and `.clang-format` files, which can be enforced -by running +- 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. +- There are also `.clang-tidy` and `.clang-format` files, which can be enforced + by running -``` -$ make format -``` + ``` + $ make format + ``` -## Submodules +## Libraries -This repository tracks (most) dependencies via git submodules. +Libraries are stored in the [lib](lib/) folder, and this folder is symlinked to +from each subfolder in the project for convenience (allows CMake to include +out-of-tree modules). The lib folder contains a mix of direct Git submodules +and custom libraries specific to this project. (Most) external dependencies are +tracked as git submodules, exceptions are in the [puzzle](puzzle/) folder. -If something is complaining about missing files +TL;DR: If something is complaining about missing files ``` git submodule update --init --recursive --depth 1 |