aboutsummaryrefslogtreecommitdiff
path: root/docs/design.adoc
blob: 533c02a72446f76d26139c8b953b18370323698c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
:document: Software Design
include::share/meta.adoc[]

== Introduction

This document contains all the design considerations made for the separate
software components that make up the puzzle box. This document has a top-down
structure, and has three levels of design 'depth':

. Top-level (hardware diagrams, OSes, communication buses, etc.)
. Module-level (puzzle inputs/outputs, top-level software diagram, etc.)
. Component-level (software dependencies, game state, etc.)

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.

[[sec:lv1]]
== Top-Level

This section of the design document establishes the development target
hardware. It also specifies the modules that are elaborated further in
<<sec:lv2>>.

<<fig:system-top>> shows a block diagram that describes the context in which
the puzzle box is used. The puzzle box in this diagram internally consists of a
main controller and multiple puzzle modules. Other notable details include:

* The charger is removable, and the puzzle box is intended to be used as a
  battery-powered device (i.e. not while tethered).
* Puzzle outputs are used to complete a feedback loop (gameplay) with the
  players, as well as eventually provide a solution to diffuse a bomb. This
  bomb is part of a standalone project that has already been finished at the
  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.

[[fig:system-top]]
.Context block diagram
image::img/system-top.svg[]

The rest of this section details the internal hardware modules and the
separation of functionality of these modules.

=== Puzzle Modules

The puzzle box hardware produced by the 21-22 group consists of 6 sides, 4 of
which are utilized by puzzle modules. This section defines the properties of a
puzzle module.

Puzzle modules can occupy one or more physical sides of the puzzle box to
implement the physical interface required for a puzzle or game. In order to
realize a complete game, each of these puzzle modules must have the ability to
control game inputs and outputs. Two approaches for this were considered:

. Let the main controller handle game state and logic for all puzzle modules.
+
This approach has the main benefit of allowing puzzle module controllers to be
substituted for I^2^C I/O expanders that draw less power than a complete MCU.
+
The major drawback of this approach is that the main controller's software
needs to be configured for each different hardware configuration, which makes
the main controller software more complicated.

. Design an abstract 'game interface' and give each puzzle module its own MCU.
+
This approach provides the most flexibility, as the main controller's software
is no longer dependent on the physically installed hardware. This approach is
also favorable with regards to testability, as each puzzle module can run
standalone.
+
The main drawback of this approach is the possible increase in power
consumption, as each puzzle module now must have its own MCU for managing game
state and communication with the main controller.

The current hardware (developed by the 21-22 group) uses the second approach,
though with MCUs that were not designed for power efficiency. This year
(23-24), the hardware produced by the 21-22 group was utilized due to the 23-24
group not including students from the hardware study path. Minimizing power
draw for each puzzle module is still a priority, so a different microcontroller
was selected.

The criteria for a puzzle module controller are:

* Must have an I^2^C peripheral (<<reqs.adoc#req:pm-i2c-ctrl>>).
* Should have enough I/O ports to directly control moderately complex puzzles
  (<<reqs.adoc#req:pm-gpio>>).
* Should be power efficient (<<reqs.adoc#req:pm-pwr-efficient>>).

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.

[[fig:puzzle-module-top]]
.Generic puzzle module top-level block diagram
image::img/puzzle-module-top.svg[]

<<fig:puzzle-module-top>> shows a block diagram of how most puzzle modules are
implemented. Since the internal components of the puzzle module block from
<<fig:puzzle-module-top>> differ for each puzzle, they are left out in this
section. <<sec:lv2>> includes the next detail level for all of the implemented
puzzles this year. The puzzle bus is detailed further in
<<sec:lv1-communication>>.

=== Main Controller

This section describes the responsibilities of the main controller inside the
puzzle box. The main controller is a central processor that is responsible for
the following:

* Integrate installed puzzle modules to form a cohesive experience by—
** Detecting and initializing installed puzzle modules.
** 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—
** Sending state updates
** Manually updating game state
** Integration with the bomb

The specific requirement of being able to serve TCP socket connections was
created so this year's puzzle box could keep compatibility with the software
produced by the 21-22 group.

As mentioned in the research document cite:[research], the 21-22 group produced
the hardware that is used as development target for this year's (23-24) run of
the puzzle box project. The existing hardware utilizes a Raspberry Pi 3B+ as
main controller, but this controller caused issues with power consumption
cite:[2122_handover]. Choosing a different controller during development
requires significant refactoring, so a different main controller has been
selected at the start of this year's run of the puzzle box project.

The criteria for the main controller are:

* Must have an I^2^C peripheral (<<reqs.adoc#req:main-i2c-ctrl>>).
* Must be able to connect to a standard 802.11b/g/n access point
  (<<reqs.adoc#req:main-802-11-ap>>).
* Must be able to serve TCP socket connection(s)
  (<<reqs.adoc#req:main-tcp-socket>>).
* Should be power efficient (<<reqs.adoc#req:main-pwr-efficient>>).

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.

[[fig:main-controller-top]]
.Main controller top-level block diagram
image::img/main-controller-top.svg[]

<<fig:main-controller-top>> shows a block diagram of the main controller and
its inputs and outputs. The main controller is the only module in the puzzle
box that is able to communicate over Wi-Fi and is therefore responsible for all
communication between the puzzle box and game operator. The puzzle bus is
detailed further in <<sec:lv1-communication>>.

[[sec:lv1-communication]]
=== Communication

Communication between puzzle modules, the main controller and other auxiliary
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].

Address definitions and protocol specifications are further detailed in
<<sec:lv2-bus>>.

=== Power supply

One of the user requirements is that the puzzle box runs on battery power
(<<reqs.adoc#req:89>>). Due to the team composition of this year's (23-24) run
of the puzzle box project, a new power supply was not chosen, even though the
current power supply was determined insufficient by the 21-22 group. This year,
additional requirements were specified for the power supply, which were used
when selecting MCUs suitable for battery-powered applications.

[[fig:power-supply-top]]
.Power supply module top-level block diagram
image::img/power-supply-top.svg[]

<<fig:puzzle-module-top>> shows a block diagram of how most puzzle modules are
implemented. Besides the additional requirements, the power supply remains the
same, and will not be elaborated further on in this document.

=== Overview

<<fig:system-bus>> is the resulting combination of the modules from
<<fig:puzzle-module-top>>, <<fig:main-controller-top>> and
<<fig:power-supply-top>>.

[[fig:system-bus]]
.Hardware component overview
image::img/system-bus.svg[]

[[sec:lv2]]
== Modules

This section elaborates on the top-level specifications from <<sec:lv1>> with
additional hardware specifications and 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.

The puzzle framework is the foundation of the puzzle box software, and is
designed to facilitate the following:

* Allow puzzle modules to be swapped with minimal downtime or maintenance
  (<<reqs.adoc#req:132>>).
* Simplify the development process and integration of new puzzle modules
  (<<reqs.adoc#req:130>>).
* Provide abstracted interfaces to allow for easy integration of the puzzle box
  as part of a larger whole (<<reqs.adoc#req:133>>).

[[sec:framework-state]]
==== State

All puzzle modules implement the same state machine shown in
<<fig:puzzle-module-common-state>>. 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 <<fig:puzzle-module-common-state>>, which can be used to
skip a puzzle if a player is stuck (<<reqs.adoc#req:168>>) or reset a game if
it is malfunctioning (<<reqs.adoc#req:167>>).

Puzzle modules start in the 'uninitialized' state, where they repeatedly send
messages to the main controller (see <<sec:main-bridge>>). The state transition
from 'uninitialized' to 'reset' is forced by the main controller upon
initialization. States on the right half of <<fig:puzzle-module-common-state>>
are used during gameplay.

[[fig:puzzle-module-common-state]]
.Global puzzle module state machine
image::img/puzzle-module-common-state.svg[]

The state machine described in <<fig:puzzle-module-common-state>> 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.

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.

==== Commands

The puzzle module framework describes the following commands:

* Read state
* Write state
* Update

The 'read' and 'write' commands are used to communicate both types of state
defined in <<sec:framework-state>>.

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.

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.

=== Main Controller

This subsection defines the function and state of the main controller.

==== State

The global state of the main controller is an aggregated version of all
installed puzzle modules and is defined by the state machine shown in
<<fig:main-controller-state>>.

[[fig:main-controller-state]]
.Main controller global state machine
image::img/main-controller-state.svg[]

The following list describes when each state is active:

* If all puzzle modules are in the 'reset' state, the main controller is also
  in the 'reset' state.
* If all puzzle modules are in the 'solved' state, the main controller is also
  in the 'solved' state.
* Else, the main controller is in the 'playing' state.

Because the main controller's state is only dependent on the installed puzzle
modules' state, it is only updated when a puzzle module sends an update
notification. When the global state of the main module changes, an update
broadcast is sent to all puzzle modules.

To simplify the commands used to control the puzzle box, the list of installed
puzzle modules is stored as an auxiliary state variable of the main controller.

==== Initializing Puzzle Modules

Puzzle modules start in the 'uninitialized' state (see
<<fig:puzzle-module-common-state>>). In this state, the puzzle module
repeatedly sends an update command to the main controller. The main controller
responds to this message by sending a 'set state' command with the target state
as 'reset' as reply. Before this response is sent, the main controller
internally adds the bus address of the puzzle module requesting to be
initialized to the list of installed puzzle modules. From the main controller's
point of view, this reply marks the moment the initialization is complete.

[[fig:sequence-puzzle-module-init]]
.Puzzle module initialization sequence diagram
image::img/sequence-puzzle-module-init.svg[]

(Activated lifeline indicates the module is no longer in 'uninitialized' state)

[[sec:main-bridge]]
==== Bridge

The bridge is used to remotely access and control the puzzle box.

The Raspberry Pi 3B+ used as main controller during the 21-22 run of the
project set up a Wi-Fi Mesh network cite:[2122_design] to communicate with the
puzzle box. This year's main controller (Raspberry Pi Pico W cite:[research])
uses a standard 802.11b/g/n access point instead (<<reqs.adoc#req:137>>).

On this network, the main controller hosts a server that serves TCP socket
connections. These sockets directly forward all internal messages sent to the
main controller bidirectionally (i.e. on behalf of the main controller).
Detailed specifications on the TCP socket server are in
<<sec:lv3-remote-control>>.

==== Operating System

The research document cite:[research] contains a detailed comparison of various
operating systems that are suitable to realize the functionality described in
this section. After this comparison, the decision was made to utilize FreeRTOS
as the operating system on the Rasberry Pi Pico W.

[[sec:lv2-bus]]
=== Puzzle Bus

This section describes the addresses and communication protocol used on the
puzzle bus. These specifications only apply to messages sent internally in the
puzzle box, as messages forwarded by the bridge (see <<sec:main-bridge>>) are
sent on behalf of the main controller.

==== Addresses

The I^2^C addresses remain mostly unchanged from the 20-21 group's
implementation cite:[2021_design]. Addresses that were modified since the 20-21
implementation are marked with an asterisk. Table 1 lists these addresses for
reference. These addresses are also used to identify specific puzzle modules.

.I^2^C address reference
[%autowidth]
|===
| Peripheral | Address

| Main controller | 0x10*
| Neotrellis puzzle controller | 0x11*
| Neotrellis button matrix | 0x12*
| Software puzzle controller | 0x03
| Hardware puzzle controller | 0x04
| Vault puzzle controller | 0x06
|===

==== Messages

All messages sent over the puzzle bus have a similar format. This format is
shown in Table 2. Notable details include:

The 'subject' field does not have to match the I^2^C address of the message
sender or recipient

Property 0x00 stores a module's global state

.Puzzle bus message format
[%autowidth]
|===
| Field | Content

| Command | Enum: read, write, update
| Subject | I^2^C address (7-bit)
| Property | Address (8-bit)
| Value | Byte string (variable length)
|===

The messages described in Table 2 are (de)serialized using Google's protocol
buffer library. This choice was made after considering various alternative
options for sending structured messages cite:[research].

<<fig:sequence-puzzle-module-init>> shows an example of how messages are
exchanged for the initialization of a puzzle module.

<<fig:sequence-puzzle-finish>> shows an example exchange where the last puzzle
module (A) is solved while (B) is already solved.

. First, module A sets it's own state to 'solved' and subsequently informs the
  main controller of this change.
. As a result of this update notification, the main controller queries puzzle
  module A for its new global state.
. Once the main controller has received and confirmed that all puzzle module
  global states are set to 'solved', the main controller sets its own state to
  'solved', and broadcasts an update message to all puzzle modules.
. As a result of the update message from the main controller, module B requests
  the main controller's new global state, and is able to verify that all puzzle
  modules have been solved.

In this example, module B could be the vault puzzle module, which displays a
code when the entire puzzle box is solved.

[[fig:sequence-puzzle-finish]]
.Puzzle box finish sequence diagram
image::img/sequence-puzzle-finish.svg[]

=== NeoTrellis Puzzle

This subsection defines aspects of the 'NeoTrellis puzzle' module and gives a
summary of how the puzzle is meant to be solved. This module will be created to
facilitate the NeoTrellis puzzle game logic and communication with the main
controller about the puzzle state.

==== NeoTrellis puzzle gameplay

The NeoTrellis puzzle is an 8x8 button matrix with Neopixels underneath each
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.

==== Puzzle inputs & outputs

The inputs and outputs of this puzzle have been taken from the design document
of the previous group which worked on this project (??). This input and output
diagram has been shown in <<fig:neotrellis-io>>.

[[fig:neotrellis-io]]
.NeoTrellis puzzle in-out
image::img/neotrellis-io.png[]

=== Software Puzzle

This subsection defines aspects of the 'software puzzle' module and gives a
summary of how the puzzle is meant to be solved. This module will be created to
facilitate the software puzzle game logic and communication with the main
controller about the software puzzle state.

[[sec:software-gameplay]]
==== Software puzzle gameplay

The software puzzle consists of 12 input ports which can be connected using a
banana plug connector. The 6 input ports on the left side of the puzzle each
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.

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
bomb participants will have description of the C-code fragments, while the
puzzle box participants only have the logical circuits on the puzzle box. The
participants must communicate with each other to figure out which a fragment of
C code corresponds with a logical circuit engraved on the puzzle box. Once this
has been done the puzzle box participants can use a banana plug cable to
connect the input and output to each other. Once the correct combination of
logical gates with the correct letter is made, the puzzle is solved (shown by
an LED lighting up above the puzzle). Allowing the participants to both see a
binary code using 16 LEDs above the puzzle, and to continue to the next puzzle.

==== Puzzle inputs & outputs

As stated in <<sec:software-gameplay>> the puzzle has 12 inputs, as well as an
LED which shows whether the puzzle has been solved and 16 LEDs showing a binary
code. This is shown in <<fig:software-io>>.

[[fig:software-io]]
.Software puzzle in-out
image::img/software-io.png[]

=== Hardware Puzzle

==== Hardware Puzzle gameplay

The hardware puzzle has a logic gate puzzle engraved on the front plate of the
puzzle box. To solve the puzzle, the user must set the toggle switches to the
correct position. To solve the puzzle, a truth table is used.

The second part of the puzzle is unlocked after solving the logic gate puzzle,
the user has to listen to a Morse code from which a four-digit code follows.
The user then turns potentiometers to change this code on the display. The
puzzle is solved when the user has put the correct code on the display. Once
successful, the indicator LED will light up.

==== Puzzle inputs / outputs

The inputs and outputs of this puzzle have been taken from the design document
of the previous group which worked on this project (21-22). This input and
output diagram has been shown in Figure ??.

=== Vault Puzzle

==== Vault puzzle gameplay

The vault puzzle is a puzzle created to test the communication skills of the
student. It shows a code on the puzzle box, which then needs to be given to
students with the game manual, who communicates this to the students at the
puzzle box the button they must click. This needs to be done 5 times before the
vault opens and the last code is given to defuse the bomb if a wrong button is
clicked the vault resets and they need to start over from the beginning.

==== Puzzle inputs & outputs

[[fig:vault-io]]
.Vault puzzle in-out
image::img/vault-io.png[]

=== Bomb device

==== Bomb device connection

The bomb connects to a WiFi-network using the 802.11x standard. The hub hosts
an interface that can be used to identify all the devices including the bomb
and also pair it to a puzzlebox. After that the game can be set-up and a given
countdown time and start time will be communicated to the bomb over a TCP
socket connection. The hub generates a code that will be send to both the
puzzlebox and bomb so that both devices know what would be or can be expected.

The bomb can also use the WiFi connection to sync. the time.

==== Device inputs & outputs

[[fig:bomb-io]]
.Bomb device in-out
image::img/bomb-io.png[]

== Components
[[sec:lv3-remote-control]]
=== Remote Control
==== Socket Server
==== Socket Commands
=== Neotrellis Puzzle
=== Game state diagrams, activity diagrams (if applicable)
=== Software Puzzle
=== Hardware Puzzle
=== Vault Puzzle

[appendix]
== NeoTrellis puzzle example

[appendix]
== Software puzzle example

include::share/footer.adoc[]