Show Source


The Carnegie Mellon University KGB plays a delightful game called Capture The Flag With Stuff. The game relies on wall-clock time and previously we just used a bunch of stop-watches manually set by hand. That stunk.

What have you done?

We have brought technology to bear. In particular, we use MQTT to dispatch messages about the current game state to subscribers and rely on NTP to keep devices in temporal synchrony. We have hardware devices, based around the ESP8266 and nodemcu, at the teams’ jails within the game, and additionally offer open, anonymous subscription to the data feed.

The devices’ firmware (in Lua) is available here. The device itself, at least for v1, looks like this:


Because machine-readable messages over MQTT is perhaps not the friendliest thing in the world, we have written several wrappers around the core, provided in addition to the devices themselves.

For player use, there is

  • an Android application (on the market) that I wrote (with some help from Cameron Wong), which can display various stats about the game.
  • a webpage (deployed here) which recreates much of the above application’s interface and has its own stylistic tweaks. Full credit to Michael Murphy for this.

Beside the webpage source, there are also some utility scripts for speaking the protocol, suitable for use by the head judge.

How Can I Help?

Would you like to write another frontend for us? Please feel free to observe the existing clients’ behaviors, but the below should be a more or less complete description of the wire protocol.

The Protocol

Topic Tree

All numbers herein are base-10 encoded and devoid of leading zeros for ease of parsing.

Centrally-set topics
  • ctfws/game/config the string none or a whitespace-separated text field:
    • starttime – POSIX seconds indicating start state
    • setupduration – setup duration, in seconds
    • rounds – number of rounds
    • roundduration – seconds per round
    • nflags – number of flags per team
    • gamecounter – (integer) which game in a bunch is this? Since often several are played in a night, it is likely useful to indicate to clients which game this is. The value 0 may be interpreted as suppressing indication in the client; we 1-index games to be friendly to people. ;)
    • any additional fields are to be ignored.
  • ctfws/game/flags – the string ? or a whitespace-separated text field:
    • red – red team flag capture count (int)
    • yel – yellow team flag capture count (int)
    • any additional fields are to be ignored.
  • ctfws/game/endtime – a single number, denoting POSIX seconds of a forced game end. If this is larger than the last starttime gotten in a config message, then the game is considered over.
  • ctfws/game/message – Message to be displayed everywhere. This, and all other messages have a POSIX-seconds timestamp followed by whitespace before the message body. These permit messages from previous games to be suppressed, should they end up resident on the MQTT broker.
  • ctfws/game/message/player – Message to be displayed specifically to players, if they ever come to have their own devices (e.g. apps)
  • ctfws/game/message/jail – Message to be displayed specifically at jail glyph units. For the moment, that’s all of them, but maybe we want to allow other things in the future.

Messages should be set persistent so that devices that reboot or lose their connection will display the right thing upon reconnection. Most messages are designed to be idempotent, in the sense that they carry timestamps of their veracity, so out-of-order delivery is partially mitigated.


Flag bitmaps?

Do we want to publish a bitmap of captured flags or are we happy with counts?

Optionally, we may wish to grant read-only views of the above topics to a guest account for a hypothetical CtFwS app.

Device-set topics
  • ctfws/dev/$DEVICENAME/beat

    • one of alive, beat, or dead (LWT; no further fields)
    • time (UNIX time, from local clock)
    • ap (MAC addr)
    • any additional fields are to be ignored.

    The device should publish alive at gain of MQTT connectivity and having registered a last will and testament to set the message dead. Thereafter, it should periodically publish to beat messages.

ACL Configuration

For example:

# global read permissions
pattern read ctfws/#

# allow devices to publish their heartbeats
pattern write ctfws/dev/%u/#

# master write to all ctfws parameters
user ctfwsmaster
pattern write ctfws/game/#

Example Command Line Usage

For the sake of simplicity in the below examples, set:

M=(-h $MQTT_SERVER -u ctfwsmaster -P $CTFWSMASTER_PASSWD -q 1)

To watch what’s going on in the world:

mosquitto_sub "$M[@]" -t ctfws/\# -v

To send MQTT messages, try variants of these. Note that in all cases, we set messages persistent so that devices that (re)connect mid-way into a game get the latest messages automatically.

  • To start a game:

    mosquitto_pub "$M[@]" -t ctfws/game/flags -r -m '0 0'
    mosquitto_pub "$M[@]" -t ctfws/game/config -r -m `date +%s`' 900 4 900 10 0' # starting now: 15 minute setup, 4 x 15 minute rounds, 10 flags, 0th game
  • To post information (The messages must have date stamps on the front!):

    mosquitto_pub "$M[@]" -t ctfws/game/flags -r -m '1 2'
    mosquitto_pub "$M[@]" -t ctfws/game/message -r -m `date +%s`' Red team captured a flag!'
  • Note that you can deliberately hide the flag scores, if you like, by publishing ? to the /flags topic:

    mosquitto_pub "$M[@]" -t ctfws/game/flags -r -m '?'
  • To end a game:

    mosquitto_pub "$M[@]" -t ctfws/game/endtime -r -m `date +%s`


Due to a bug in nodemcu (, do not send empty messages or messages with QoS 2; stick to QoS 1 and it appears to work. Ideally these should be QoS 2, but that will have to wait.