Building a BLE-based aura system for a live-action role-playing game

It all started with a plan for a live-action role-playing game (LARP) inspired by Arcanum: Of Steamworks and Magick Obscura, a computer RPG set in a world where magic and technology coexist but fundamentally clash.

In our game, this conflict was supposed to manifest itself through auras. A mage would radiate a magical aura capable of disrupting nearby machinery. technology, in turn, would interfere with magic.

But we didn't want this to be just another rule that players had to act out. We wanted the conflict to have a real, physical effect: a player could walk up to a working machine and cause it to stop. Or a mage could try casting a spell near a powerful piece of technology, only to find that their magic no longer worked.

We wanted the game world itself to react to the characters.

First Experiments

After looking into a few possible technologies, we settled on Bluetooth Low Energy (BLE).

A BLE token (think of a beacon or tag) is essentially a tiny computer capable of broadcasting data, scanning its surroundings, and running simple programs that respond to other nearby tokens. For what we had in mind, this seemed almost perfect.

A friend gave me a few tokens based on the nRF51822 to play around with. The problem was that they came with practically no documentation, and there weren't even any particularly useful markings on the boards themselves.

Together with my colleague Andrey Gorodetsky I set out to figure out how these things worked. At the time, we knew next to nothing about the chips, but we certainly had enthusiasm to spare.

Our first test subject
Our first test subject

Our first attempt to connect to the chip and reflash it mostly succeeded in making the chip considerably hotter than intended.

After several fruitless attempts to get these devices to do anything useful, we went combing through AliExpress in search of tokens we could actually find some information about.

Eventually, we settled on the HolyIOT YJ15044, also based on the nRF51822. It had one crucial advantage: the board had exposed, clearly labeled GPIO pads, and the manufacturer's website even had documentation.

After our previous experience, this felt positively luxurious.

The tokens also came with ready-made enclosures. That might sound like a minor detail, but when you're building electronics for a LARP, getting the hardware to work is only half the battle. You also need to figure out how a player is supposed to wear it for several days without breaking or losing it, and without having to wander around the game site with a bare PCB hanging from their neck.

The First Working System

Things went considerably more smoothly with the new tokens. Armed with documentation and some help from AI, we managed to write our first sketches, successfully upload them to the boards, and finally get our code running.

Of course, it wasn't long before the limitations of the nRF51822 started to show.

The controller simply didn't have enough resources to do everything we wanted simultaneously. We had to simplify the architecture as much as possible, eventually building almost all communication around BLE advertising.

The tokens periodically broadcast information about themselves, packed into the Manufacturer Data field. Other tokens scanned for nearby broadcasts, picked out the packets they were interested in, and reacted accordingly.

We decided to handle token control through BLE packets as well. A control message contained the MAC address of the target device, followed by a command. Every token could receive the message, but only the intended recipient was supposed to execute it.

Hardware Modifications

The hardware needed a few modifications of its own.

The YJ15044 offered exactly one built-in red LED for user feedback. For our game mechanics, that was clearly not going to be enough.

Players needed to know what was happening to their auras: whether they were active, suppressed by the opposing force, or in conflict - and whether the token itself was working properly.

So, we soldered two additional LEDs - red and green - to the exposed GPIO pads. This gave the token a simple visual interface that players could understand without any special equipment.

We used another GPIO pad as a control output. Through it, a token could operate external peripherals - the interactive props the whole project was really about.

The LEDs were just there to provide feedback. The real goal was to make in-game devices react to the presence of nearby characters.

PCB before and after modification
PCB before and after modification

Fully assembled tokens
Fully assembled tokens

Aura and Interactive Device

Gradually, we developed firmware that allowed us to use identical tokens in two main roles.

The first was Aura - a character's personal token. It represented its owner's aura, broadcasting information about its properties while simultaneously monitoring nearby BLE traffic.

The second was Interactive Device. This type of token was installed inside an in-game device or prop. It scanned for nearby auras, calculated their effects, and changed the device's state accordingly.

Once we had the system working at home, we decided it was time to show it to other people.

And that's when things went spectacularly wrong.

The Festival Fiasco

We were planning to demonstrate our system at a LARP festival.

We had prepared a little performance for the occasion. I was supposed to play a mechanical engineer, showing off a perfectly functional piece of technology. Then Andrey, playing a mage, would approach me, and his magical aura would gradually interfere with the machine.

We even planned a little argument between the engineer and the mage. That way, we could show how this game mechanic would actually play out instead of explaining it as an abstract rule.

At least, that was the plan.

Once the demonstration started, the tokens behaved completely unpredictably. At first, we thought we'd simply calibrated the detection distance incorrectly. Surely, adjusting a few thresholds would fix everything.

But the Interactive Device was behaving almost randomly. It would turn on when it wasn't supposed to, turn off for no apparent reason, turn back on a second later, and generally refuse to demonstrate any of the nice, predictable behavior we'd seen at home.

All our previous tests had taken place at my home, with just a handful of people and relatively few Bluetooth devices nearby.

At the festival, however, we were surrounded by hundreds of people carrying phones, smartwatches, headphones, and all sorts of other electronics.

We suspected the poor old nRF51822 was simply choking on all that Bluetooth traffic.

Back to AliExpress

So, we returned to AliExpress, this time for some more serious research.

We eventually settled on another token from the same manufacturer: the HolyIOT 21014, based on the nRF52810.

The new controller was considerably more powerful, with a higher clock speed, more RAM, and support for newer Bluetooth features.

The token itself was actually smaller than its predecessor and still came in a ready-made enclosure.

Another welcome bonus was the built-in RGB LED. We no longer had to solder additional LEDs onto every board just to provide visual feedback during the game.

There was a catch, though. The new board lacked a convenient GPIO pad for our control signal. On the other hand, it did have a button, and we could solder wires to its contacts and repurpose the corresponding line for controlling peripherals.

That seemed like a perfectly reasonable compromise.

Second-generation token before and after adding the peripheral connector
Second-generation token before and after adding the peripheral connector

Second-generation tokens in their enclosures
Second-generation tokens in their enclosures

An Android App and a Pub Stress Test

The failed festival demo also taught us something else: LEDs alone weren't enough to figure out what was happening inside the system.

So, I vibe-coded a small Android app for diagnosing and controlling the tokens.

It could discover active tokens, send commands to specific devices, monitor nearby BLE advertisements, and simulate different types of tokens by broadcasting the corresponding messages.

At last, we could see what was happening instead of trying to guess.

We ported our firmware to the new nRF52810 and made the necessary adjustments. Then we decided to test it somewhere with plenty of Bluetooth traffic.

So, we took a few tokens to a pub.

According to our app, there were around 400 Bluetooth devices in the vicinity. This time, we had a measurement instead of a guess about how crowded the airwaves were.

And even in those conditions, the new tokens continued to detect one another reliably and respond as expected.

The Human Body vs. Bluetooth

Solving one problem quickly revealed another: the human body does a pretty good job of weakening BLE signals.

We intended players to wear their Aura tokens as medallions on their chests. This meant that, in a sense, each person would radiate their aura primarily forward.

For one-on-one interactions with nearby objects, this was acceptable. But what about a room full of players?

In a crowded room, with people turning, walking around, and occasionally blocking their own tokens' signals, Interactive Devices could end up constantly switching on and off. Someone turns around - their magical aura disappears. They turn back - and suddenly it's there again.

To address this, we introduced another role: Overseer.

An Overseer token would be mounted above the players, perhaps on the ceiling, where it would have a much better chance of reliably receiving signals from their personal tokens.

It would collect information about the auras in the room, calculate the overall balance of power, and then tell the Interactive Devices what state they should be in.

We kept the Overseer role in the firmware, though whether we'd need it would depend on the layout of the game site.

How Messages Were Structured

By this point, our communication protocol had also started taking a more definite shape.

Our data began with a custom header that let the tokens identify messages belonging to our system and ignore unrelated BLE advertisements.

We also defined several header types to distinguish regular messages, control commands, Overseer messages, and other types of packets.

The rest of the data was packed as tightly as possible, down to individual bits.

Each device reported its role - Aura, Interactive Device, and so on - its level, its affinity (Magic, Technology, or Unity, representing the union of the two), and its current state.

We managed to squeeze all of this into just five bytes, so we wouldn't clog already crowded airwaves with long messages.

Scanning Cycles and Taming RSSI

Interactive Devices didn't react to every advertising packet they received.

Instead, they scanned in cycles lasting approximately three and a half seconds. During each cycle, a device recorded the Aura tokens it detected in a hash table. This allowed us to count unique auras rather than the number of packets received from them.

At the end of each cycle, the device calculated the balance of power in its surroundings and determined what state it should be in.

We used RSSI (received signal strength indicator) to estimate the distance between devices. Each type of interaction had its own threshold, and signals that were too weak were simply ignored. This gave auras a limited effective range, rather than having every token react to every other device it could possibly detect.

Unfortunately, RSSI turned out to be rather temperamental.

Even without any obvious changes in the environment, signal strength could fluctuate significantly, suddenly jumping up or dropping off.

Clearly, reacting to individual measurements wasn't going to work.

So we added time-based filtering. Before considering an aura present, a device had to detect it over several consecutive scanning cycles. Similarly, an aura that had already been detected wasn't considered gone just because its signal suddenly disappeared. It had to remain undetected for several consecutive cycles.

Essentially, we implemented a form of temporal hysteresis.

This made the devices much more stable and, perhaps more importantly for a LARP, their behavior much more predictable for the players.

How the Overseer Worked

The Overseer used the same basic scanning mechanism, but processed the results differently.

It didn't simply broadcast a list of detected auras or a count of mages and technologists to other devices.

Instead, after each scanning cycle, it calculated which state each class of Interactive Devices should be in.

The result was essentially a lookup table: level-one Magic devices: on; level-two and level-three Magic devices: off; Technology devices of all three levels: off; and so on.

The Overseer then broadcast this table.

Each Interactive Device only had to look up its own affinity and level in the table and follow the decision.

In other words, the Overseer wasn't just a relay. It was a local arbiter of aura interactions.

Batteries and Other Roles

Once the core system was working, we realized we could add roles for more specific game mechanics.

For example, we added a Battery token.

In game terms, a device powered by a Battery token was still an Interactive Device and followed the usual rules of aura interactions. But it had one additional condition for turning on: a charged Battery token had to be nearby.

Its presence was also detected through BLE, using a configurable RSSI threshold.

This meant the same system could simulate not only the effects of magic and technology, but also more specific physical dependencies between in-game objects.

Configuration and Debug Mode

The nRF52810 had enough resources to operate as a BLE peripheral, accepting connections alongside its main application logic.

This made development considerably easier.

We could connect directly to a token and inspect diagnostic information: what it was detecting, what decisions it was making, and why it was in its current state.

We gradually moved parameters out of hard-coded firmware values and into device configuration. RSSI thresholds and other settings could now be adjusted individually for each token.

During normal operation, the token didn't keep its BLE connection services running. The token simply did its job: scanning for nearby devices and broadcasting BLE advertisements.

To configure a particular token, we sent a special command containing its MAC address over BLE advertising.

When the token received the command to enter debug mode, it enabled the relevant BLE services. We could then connect to it directly for diagnostics or configuration.

The DFU Dream

The next logical step seemed to be over-the-air firmware updates.

When you have 200 tokens to maintain rather than ten, updating firmware over Bluetooth starts to sound extremely appealing.

We experimented with DFU (Device Firmware Update) using MCUboot. The idea was to transfer new firmware over BLE and let the bootloader install the update.

Unfortunately, we ran into the nRF52810's resource limitations once again. We simply couldn't get everything we wanted running on this chip at the same time.

We even started looking at the next generation of tokens from the same manufacturer: the 52008, based on the significantly more powerful nRF54L15.

From a technical perspective, it looked very promising.

But then we ran into a much more mundane problem: the manufacturer didn't offer these boards with suitable ready-made enclosures.

For a lab project, that would hardly matter. For a game that needed some two hundred tokens, it was a different story. We would have had to protect every board and figure out how players could carry the wearable ones.

So we had to give up on the more powerful platform. We stuck with the nRF52810 - and gave up on our dream of proper DFU along with it.

How to Flash Two Hundred Tokens

That left us with a very practical problem: we still had 200 tokens to flash.

We bought and flashed 200 tokens: personal Aura tokens for the players, plus others for Interactive Devices and additional roles. Not all of them ended up being used, since not every player got an Aura token.

At first, we tried designing a special socket that would let us insert a round token and flash it quickly.

Attempts at making a flashing socket
Attempts at making a flashing socket

A much simpler solution won.

We built a small tool with four spring-loaded contacts (pogo pins) - essentially a tiny four-pronged fork, or comb. All we had to do was press it against the corresponding pads on the token's PCB, flash the firmware, release it, and move on to the next one.

No connectors or soldering required for each individual token.

Press. Flash. Next.

Sometimes a simple solution is exactly what you need for mass production, home edition. We later discovered an off-the-shelf solution, but our homemade contraption worked well enough.

Our flashing combs
Our flashing combs

What Did We Hook Up?

We built two types of devices for the game:

  1. A smart power outlet, controlled by a token powered from the same outlet. It could switch 220 V devices, such as lighting, on and off.

  2. Interactive string lights. The token controlled a relay that supplied power to an LED string. Since this drained the battery fairly quickly, we also required a button press to activate the device.

Both devices were designed by Yuri Glushkov-the only person on our team who actually worked with electronics and knew what he was doing. Thanks again, Yuri!

String lights with a relay and push-button activation
String lights with a relay and push-button activation

How It Worked at the Game

Finally, the system made it to the actual game.

As it turned out, we didn't need the Overseer. The game site had no suitable enclosed spaces where large groups of players would gather.

Most of our Interactive Devices ended up being spellcasting focuses.

A mage's spellcasting focus
A mage's spellcasting focus
A mage's spellcasting focus

Mages wore these devices on their arms. When a player pressed a button, the focus checked the surrounding auras.

If the string lights built into the device lit up, the mage could cast a spell.

If they didn't, the surrounding Technology aura was suppressing magic, and the spell failed.

This meant players didn't have to count nearby technologists, compare their levels, or ask a game master (GM) to resolve the interaction. They just pressed a button, and the prop gave them the answer.

We also attached some tokens to various in-game artifacts.

We didn't always have time to build additional hardware for these, so in some cases the token's built-in RGB LED simply indicated whether the artifact was active under the current balance of auras.

We received almost no complaints about the system's stability during the game.

The batteries in the personal Aura tokens easily lasted the two days of the game (we'd planned for four days of battery life) and didn't need replacing.

Interactive Devices were a little trickier. Whenever the same battery also powered LEDs, string lights, or relays, power consumption was noticeably higher. We had to replace some batteries during the game.

The Aurameter

One of the central ideas of the game was not just the local conflict between magic and technology, but also the changing balance of power across the entire city.

Players could develop their characters along the path of Magic, Technology, or a third path that emerged during the game - Unity, bringing the two forces together.

We wanted the results of their choices to be visible to everyone.

To make that possible, we installed a separate device in the town square: the Aurameter.

It scanned BLE advertisements and kept track of unique tokens it had detected, recording their affinities. Using the accumulated data, the Aurameter displayed the overall balance between Magic, Technology, and Unity.

This way, individual player choices contributed to a bigger picture that everyone could literally see in the town square.

Of course, things didn't go entirely without incident.

During the game, we discovered a bug in the Aurameter's GM-facing web interface. Once it had recorded around sixty unique auras, the page showing the full list stopped rendering properly.

Fortunately, the problem was limited to the management interface and didn't affect the actual counting logic.

We fixed the bug on-site and updated the Aurameter. It went back to handling dozens of nearby auras and served its purpose during the game.

The Aurameter
The Aurameter

How It All Turned Out

Overall, the system worked, although we didn't manage to implement everything on the scale we had originally planned.

What we lacked most was time to build a larger variety of Interactive Devices. We wanted to fill the game site with many more objects and mechanisms that would automatically react to nearby auras.

Many of those ideas never made it past the prototype or planning stage. As a result, we only got to demonstrate part of the system's potential during the game.

Physical durability was another problem. Some devices simply fell apart after being worn by players. Clearly, we shouldn't have been so stingy with the hot glue. Anything that could be glued should have been.

Still, the basic concept proved itself.

In the end, around a hundred small, self-contained devices worn by players or attached to in-game objects communicated without a central server. Together, they turned the abstract rule of "magic and technology interfere with each other" into a physical property of the game world.

The architecture also turned out to be quite versatile. Aura, Interactive Device, Overseer, and Battery were simply the roles we came up with for this particular game. There's nothing stopping us from coming up with other types of tokens and entirely different rules for how they interact.

And perhaps that was the most interesting outcome of the whole project.

We started with a fairly specific goal: to create a mage's aura that could make machinery malfunction.

What we ended up with was a small toolkit for building electronic game mechanics - one that could also be used in entirely different LARP games.

Source Code

Originally published in Russian on Habr.