Distributed Alarm System
2023
Embedded systems group project at Chalmers: a burglar alarm split across three MD407 boards talking over a CAN bus: a door unit, a motion unit, and a central unit that supervises both, takes its configuration from a PC over USART and is disarmed with a keypad. I built and was responsible for the messaging layer (CAN bus), the supervision that notices a peripheral going quiet, and a jammer unit for load-testing the bus.
- C
- Embedded systems
- CAN bus
- MD407
- Interrupts
- USART
- Sensor modules
- Designed the application protocol carried inside each CAN frame: a function ID in the first payload byte, its parameters in the rest and wrote the sending and receiving halves on both the central unit and the peripherals.
- Built the ping supervision on the central unit: every peripheral heartbeats once a second off a hardware timer, and pulling a bus cable raises the alarm within two seconds.
- Wrote the jammer unit that floods the bus on demand, and load-tested the system at 100 ms, 10 ms and 1 ms send intervals and traffic between the central unit and the peripherals survived all three.
The problem
A burglar alarm is a distributed system whose parts are joined by wires an intruder can reach. Detecting an open door is the easy half. The difficult half is noticing that a sensor has stopped reporting because someone cut its cable, because a system that tells you everything is fine when it is being attacked is worse than no system at all.
The brief made that explicit: cutting the link to a peripheral had to set the alarm off rather than silence it. So the interesting work was never the sensors. It was the bus between them, and what the central unit could infer from what it did and did not hear on it.
The system

- Three MD407 boards: wired together over RJ-11 cable and a splitter. One acts as the central unit which holds the system state. The others are peripherals.
- A keypad on the central unit's GPIO pins takes a four-digit code to disarm, and a USART link to a PC carries config settings as input and alarm reports as output.
- The door unit watches up to fifteen door switches. Fifteen because every door also needs its own status LED and the alarm needs one more, so the pin count sets the limit, not the software.
- A door left open lights a local LED after four minutes and only escalates to a bus alarm a minute later, so a door propped open briefly stays a local matter instead of waking the whole system.
- The switches are read through pull-down resistors, which means a disconnected sensor reads exactly like an open door. Unplugging one raises the alarm rather than removing it.
- The motion unit compares distance sensor ultrasound against a reference distance measured when the area is empty, and reads a vibration sensor on an EXTI interrupt so a knock is caught without anything polling for it.
The protocol
CAN hands you an arbitration ID, a length, and up to eight bytes of payload, and nothing above that. Every meaning the system has had to be built inside those eight bytes.

- The arbitration ID was used to identify the publisher node. The first payload byte is a function ID: 0 for an alarm, 1 for a ping, one number per configuration command. The bytes following it are that function's parameters.
- Standardizing the message protocol this way made the system easier to keep track of, easier to debug, and easier to scale.
- Peripherals set filters for CAN, so data not addressed to them is discarded by the hardware before the CPU sees them. This matters, as unknown data should be filtered out.
- CAN is bitwise with dominant zeros, meaning that lower ID data wins the bus outright. The scheme is priority-aware by construction, which we never utilized: alarms were given no better ID than ordinary traffic, so nothing on the bus was actually prioritized.
Supervision and load testing
The central unit cannot distinguish a peripheral with nothing to report from one that has been unplugged, unless the peripheral keeps talking when it has nothing to say. So each one pings once a second off a hardware timer, and the central unit records when the last ping from each arrived. Then the jammer: a fourth board that does nothing but transmit on a timer, with the interval and the payload both settable over USART. It exists so the bus can be made hostile on purpose rather than hoping it never is.
Supervision is one comparison per peripheral: the system takes the difference between now and the last ping, against a 2000 ms threshold on a millisecond timer, and raises the same global alarm a tripped sensor would. The test is to pull the RJ-11 cable mid-run. The alarm fires roughly two seconds later, which is the threshold behaving exactly as specified.
With the jammer running at 100 ms, 10 ms and finally 1 ms intervals, the central unit and the motion unit kept communicating through all three. The reason is the acceptance filters: the disruption never reached the peripherals' software at all, so the load was filtered by the CAN controller instead of by the main loop.
What the sensors could actually do
Characterising the sensors was divided across the group. The ranging numbers are worth repeating because they, rather than any of the software, set what the system could genuinely protect.
- In a clear room the rangefinder tracked a wall to within a couple of centimetres out to 3.5 m, and a ±2 % band around the calibrated reference ran a full minute without a false alarm.
- In a cluttered room the same 2 % band was unusable past 1 m, and covering 3.5 m meant widening it to 10 %.
- At 4 m a 10 % band is ±40 cm of dead zone, and someone moving slowly along the wall walked through it undetected. Sensitivity and range trade directly against each other: a room the sensor cannot cover tightly is a room it cannot cover.
What I would do differently
The system passed every test we set it, which says as much about the tests as about the system. Writing up the bus work is where its limits got clear, and all of them are in the protocol rather than the hardware.
Alarm frames share ID priority with ordinary traffic. CAN would let alarms win arbitration outright if they were given lower IDs, and we never split the ID space that way. So hypothetically a jammer picking an ID below ours would starve the important messages.
Nothing authenticates the central unit. Any node able to put frames on the bus can send the disarm command, which makes physical bus access the same thing as disarm access. Signing control messages is the fix, and in something described as a security product it is not optional.
There is no sequence number and no replay protection, so a captured disarm frame stays valid forever.
Two seconds of missing pings is a tight threshold. It is responsive, but it assumes a bus never busy enough to delay a heartbeat, which is precisely the assumption the jammer was built to break.
The application never handles transmission failure. The CAN controller detects errors and re-transmits them by itself, which is why this went unnoticed for the whole project, but nothing above it ever learns that a frame did not arrive.