From 9425826203e1c954491a6a29ae84daf1a5edda2c Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 20 Jun 2024 17:53:58 +0200 Subject: more documentation (WIP design.adoc) --- docs/design.adoc | 96 ++++---- docs/figs.drawio | 396 ++++++++------------------------ docs/img/.gitignore | 1 + docs/img/main-controller-state.svg | 2 +- docs/img/main-controller-top.svg | 2 +- docs/img/power-supply-top.svg | 2 +- docs/img/puzzle-module-common-state.svg | 2 +- docs/img/puzzle-module-top.svg | 2 +- docs/img/software-components.puml | 37 +++ docs/img/style.ipuml | 3 + docs/img/system-bus.svg | 2 +- docs/img/system-top.svg | 2 +- docs/makefile | 5 + lib/pbdrv/pb-types.h | 43 +++- 14 files changed, 232 insertions(+), 363 deletions(-) create mode 100644 docs/img/.gitignore create mode 100644 docs/img/software-components.puml create mode 100644 docs/img/style.ipuml diff --git a/docs/design.adoc b/docs/design.adoc index 5ebbb15..e54c970 100644 --- a/docs/design.adoc +++ b/docs/design.adoc @@ -117,7 +117,7 @@ the following: ** Aggregating game state for all installed puzzle modules. ** Reading and writing game state for all installed puzzle modules. ** Broadcasting updates internally. -* Serve TCP socket connections for— +* Serve a TCP socket for— ** Sending state updates ** Manually updating game state ** Integration with the bomb @@ -146,6 +146,7 @@ The criteria for the main controller are: The requirements document compares various microcontrollers that fit these criteria. After this comparison, the decision was made to utilize the Raspberry Pi Pico W as main controller during development. +// TODO: this was a bad choice [[fig:main-controller-top]] .Main controller top-level block diagram @@ -165,9 +166,22 @@ peripherals is handled through a central I^2^C bus referred to as the 'puzzle bus'. This design was again carried over from the hardware design from the 21-22 group cite:[2122_design]. -The only notable difference made this year was the removal of the -"HarwareInterrupt" line1{empty}footnote:[This is not a typo], which was -connected but not utilized cite:[research]. +The previously specified "HarwareInterrupt" line1{empty}footnote:[This is not a +typo] has been removed this year, as it was connected but not utilized +cite:[research]. + +To optimize for flexibility and extensibility, the puzzle bus should ideally +function as a network (similar to the CAN bus), to allow puzzle modules to send +responses asynchronously. I^2^C was initially chosen for the puzzle bus due to +its widespread availability on microcontrollers and in peripherals, but does +not provide any network-like capabilities. + +To archive network-like capabilities, the puzzle bus is specified to be a +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. Address definitions and protocol specifications are further detailed in <>. @@ -202,15 +216,15 @@ image::img/system-bus.svg[] [[sec:lv2]] == Modules -This section elaborates on the top-level specifications from <> with -additional hardware specifications and software design decisions. +This section elaborates on the top-level (hardware) specifications from +<> with software design decisions. === Puzzle Module Framework This subsection defines aspects of the 'puzzle framework' and the interface that allows puzzle modules to integrate with this framework. All communication described within this subsection refers to 'internal' communication between the -main controller and puzzle module. +main controller and puzzle modules on the puzzle bus. The puzzle framework is the foundation of the puzzle box software, and is designed to facilitate the following: @@ -226,56 +240,52 @@ designed to facilitate the following: ==== State All puzzle modules implement the same state machine shown in -<>. Note that the arrows indicate state -transitions that a puzzle module may take on its own. The main controller also -allows the game operator to manually set the current state as one of the states -on the right half of <>, which can be used to -skip a puzzle if a player is stuck (<>) or -reset a game if it is malfunctioning (<>). - -Puzzle modules start in the 'uninitialized' state, where they repeatedly send -messages to the main controller (see <>). The state transition -from 'uninitialized' to 'reset' is forced by the main controller upon -initialization. States on the right half of <> -are used during gameplay. +<>. Note that solid arrows indicate state +transitions that a puzzle module may take on its own, while dashed arrows +indicate state transitions forced by the main controller. The main controller +also allows the game operator to manually set the current state as one of the +states on the right half of <>, which can be +used to skip a puzzle if a player is stuck (<>) +or reset a game if it is malfunctioning (<>). + +Puzzle modules start in the 'uninitialized' state, where they wait until the +main controller sends a SET STATE command. Receiving this command indicates to +the puzzle module that it was successfully registered by the main controller, +and that it may transition from the 'uninitialized' state to the 'reset' state. [[fig:puzzle-module-common-state]] .Global puzzle module state machine image::img/puzzle-module-common-state.svg[] The state machine described in <> is referred -to as the global state. Puzzle modules may also declare and define custom state -variables, which is referred to as auxiliary state. These auxiliary state -variables contain game-specific variables; e.g. the binary state of each button -on the Neotrellis puzzle module, or the last passcode entered on the vault -puzzle module. +to as the global state. Puzzle modules may also declare and define custom +variables, which are referred to as properties. These properties may contain +game-specific variables; e.g. the binary state of each button on the Neotrellis +puzzle module, or the last passcode entered on the vault puzzle module. -Separating the auxiliary state from the generic state allows the main -controller to handle the auxiliary state as an arbitrary blob, which allows for -future expansion without modification of the main controller software. +Separating properties from the global state allows the main controller to +handle these property values as an arbitrary blob, which allows for future +expansion without modification of the main controller software. ==== Commands -The puzzle module framework describes the following commands: +The puzzle module framework describes the following command *types*: + +* ``PROP``: property +* ``MAGIC``: handshake +* ``STATE``: global state -* Read state -* Write state -* Update +Each command also has a specific *action*: -The 'read' and 'write' commands are used to communicate both types of state -defined in <>. +* ``REQ``: request +* ``RES``: response +* ``SET``: (over)write -To avoid issues caused by state synchronization memory consumption on the main -controller and puzzle modules, auxiliary state is only stored on each -respective puzzle module's controller. Only global state is cached on the main -controller to reduce the number of back-and-forth messages required for state -updates. +Not all commands define behavior for all actions (e.g. there is no MAGIC SET +command). -These commands are sufficient to realize the puzzle box, but this means that -the puzzle box would rely heavily on polling-based updates internally. To solve -this, the 'update' command was created. This command is utilized for various -kinds of updates, including registering new puzzle modules and updating global -state. +The Doxygen-generated pages for these command types explain their usage, and +will not be repeated here. === Main Controller diff --git a/docs/figs.drawio b/docs/figs.drawio index 042ce15..d898390 100644 --- a/docs/figs.drawio +++ b/docs/figs.drawio @@ -1,4 +1,4 @@ - + @@ -354,20 +354,20 @@ - + - - + + - + - + @@ -381,28 +381,28 @@ - + - - + + - + - - + + - - + + @@ -415,21 +415,21 @@ - + - - + + - + - - + + @@ -437,465 +437,249 @@ - + - + + + + - + - - + + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + + - - - - - - - - - - + + - + - - + + - - + - - + + + - - + - + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/img/.gitignore b/docs/img/.gitignore new file mode 100644 index 0000000..afafe8d --- /dev/null +++ b/docs/img/.gitignore @@ -0,0 +1 @@ +software-components.svg diff --git a/docs/img/main-controller-state.svg b/docs/img/main-controller-state.svg index 6e0333b..f82a8c0 100644 --- a/docs/img/main-controller-state.svg +++ b/docs/img/main-controller-state.svg @@ -1,3 +1,3 @@ -
Reset
Playing
Solved
\ No newline at end of file +
idle
playing
solved
\ No newline at end of file diff --git a/docs/img/main-controller-top.svg b/docs/img/main-controller-top.svg index 5bb7cda..601b402 100644 --- a/docs/img/main-controller-top.svg +++ b/docs/img/main-controller-top.svg @@ -1,3 +1,3 @@ -
Main controller
Puzzle bus
Wi-Fi
\ No newline at end of file +
main controller
puzzle bus
Wi-Fi
\ No newline at end of file diff --git a/docs/img/power-supply-top.svg b/docs/img/power-supply-top.svg index 89107a6..1ba4c14 100644 --- a/docs/img/power-supply-top.svg +++ b/docs/img/power-supply-top.svg @@ -1,3 +1,3 @@ -
Power supply
Puzzle bus
Charger
\ No newline at end of file +
power supply
puzzle bus
charger
\ No newline at end of file diff --git a/docs/img/puzzle-module-common-state.svg b/docs/img/puzzle-module-common-state.svg index b5688ef..4b1e1fd 100644 --- a/docs/img/puzzle-module-common-state.svg +++ b/docs/img/puzzle-module-common-state.svg @@ -1,3 +1,3 @@ -
Uninitialized
Reset
Playing
Solved
\ No newline at end of file +
uninitialized
idle
playing
solved
\ No newline at end of file diff --git a/docs/img/puzzle-module-top.svg b/docs/img/puzzle-module-top.svg index 79c16ed..bc65730 100644 --- a/docs/img/puzzle-module-top.svg +++ b/docs/img/puzzle-module-top.svg @@ -1,3 +1,3 @@ -
Puzzle module
Puzzle bus
Puzzle outputs
Puzzle inputs
\ No newline at end of file +
puzzle module
puzzle bus
puzzle outputs
puzzle inputs
\ No newline at end of file diff --git a/docs/img/software-components.puml b/docs/img/software-components.puml new file mode 100644 index 0000000..3803334 --- /dev/null +++ b/docs/img/software-components.puml @@ -0,0 +1,37 @@ +@startuml +!include style.ipuml + +component [arduino-sdk] as "Arduino SDK" + +component pbdrv-mod as "Puzzle bus module driver\n(pbdrv-mod)" { + component pbdrv as "Puzzle bus driver\n(pbdrv)" +} + +[freertos] as "FreeRTOS" +[mpack] as "mpack" + +[pico-sdk] as "RPI Pico SDK\n(pico-sdk)" +[i2ctcp] as "i2c over tcp bridge\n(i2ctcp)" + +node pbc as "puzzle box client\n(pbc)" +node main as "main controller software" +node pm as "puzzle module" + +pm ---> [pbdrv-mod] +pm ----> [freertos] +pm ----> [arduino-sdk] + +main ---> [pbdrv-mod] +main ----> [freertos] +main ----> [pico-sdk] +main ---> [i2ctcp] + +pbc ---> [pbdrv] +pbc ---> [i2ctcp] + +[pbdrv] --> [mpack] + +[i2ctcp] --> [mpack] + +@enduml + diff --git a/docs/img/style.ipuml b/docs/img/style.ipuml new file mode 100644 index 0000000..25bb6d7 --- /dev/null +++ b/docs/img/style.ipuml @@ -0,0 +1,3 @@ +!theme plain +skinparam RoundCorner 0 + diff --git a/docs/img/system-bus.svg b/docs/img/system-bus.svg index 440227a..1b04940 100644 --- a/docs/img/system-bus.svg +++ b/docs/img/system-bus.svg @@ -1,3 +1,3 @@ -
Puzzle bus
(I²C + power)
Main controller
Puzzle module 3
(neotrellis)
Puzzle module 4
(safe)
Puzzle module 1
(software)
Puzzle module 2
(hardware)
Power supply
(battery pack)
\ No newline at end of file +
puzzle bus
(I²C + power)
main controller
neotrellis puzzle module
vault puzzle module
software puzzle module
hardware puzzle module
power supply
(battery pack)
\ No newline at end of file diff --git a/docs/img/system-top.svg b/docs/img/system-top.svg index 0a9e8c0..902fa07 100644 --- a/docs/img/system-top.svg +++ b/docs/img/system-top.svg @@ -1,3 +1,3 @@ -
Puzzle outputs
Puzzle box
Puzzle inputs
Player(s)
Charger
Wi-Fi
Bomb
\ No newline at end of file +
puzzle outputs
puzzle box
puzzle inputs
player(s)
charger
Wi-Fi
bomb
\ No newline at end of file diff --git a/docs/makefile b/docs/makefile index eb54a80..15ea3a4 100644 --- a/docs/makefile +++ b/docs/makefile @@ -20,3 +20,8 @@ ASCIIDOCTOR_ARGS += --backend pdf %.pdf: %.adoc $(PDFDEPS) bundle exec asciidoctor $(ASCIIDOCTOR_ARGS) $< +%.svg: %.puml + plantuml -tsvg $< + +design.pdf: img/software-components.svg + diff --git a/lib/pbdrv/pb-types.h b/lib/pbdrv/pb-types.h index 4d085f9..f5fbb74 100644 --- a/lib/pbdrv/pb-types.h +++ b/lib/pbdrv/pb-types.h @@ -21,9 +21,17 @@ typedef uint16_t i2c_addr_t; //! puzzle bus command types enum pb_cmd_id { - PB_CMD_PROP, //!< puzzle module property - PB_CMD_STATE, //!< global state - PB_CMD_MAGIC, //!< magic (handshake) + /** \brief puzzle module property (REQ, RES, SET) */ + PB_CMD_PROP, + /** \brief puzzle module global state variable (REQ, RES, SET) */ + PB_CMD_STATE, + /** + * \brief magic (handshake) (REQ, RES) + * + * This message is used to distinguish between puzzle modules and regular I2C + * slaves on the puzzle bus. + */ + PB_CMD_MAGIC, }; typedef enum pb_cmd_id pb_cmd_id_t; @@ -51,10 +59,31 @@ static const char pb_cmd_magic_res[] = { 0x67, 0x61, 0x6d, 0x69, 0x6e, 0x67 }; //! puzzle bus message header (shared by all commands) typedef struct { - pb_cmd_id_t type; //!< command type - pb_action_t action; //!< command action - i2c_addr_t sender; //!< i2c address of sender - void * cmd; //!< command data (type dependant) + /** + * \brief command type + * + * This is used to identify what the message is about. + */ + pb_cmd_id_t type; + /** + * \brief command action + * + * This is used to specify what should happen as a result of this message. + */ + pb_action_t action; + /** + * \brief I2C address of sender + * + * This is used to facilitate the 'network' features, as the sender of an I2C + * write is unknown. + */ + i2c_addr_t sender; + /** + * \brief command data (type dependent) + * + * Struct containing command type-specific data. + */ + void * cmd; } pb_msg_t; //! PB_CMD_PROP data -- cgit v1.2.3