โ† Back to all posts

I Taught Bristol Devs to Build Lobster Traps

When BristolJS asked me to give a talk at JustEat's headquarters, I knew two things immediately:

  1. This was the opportunity of a lifetime
  2. JustEat's HQ was the perfect venue โ€” they know seafood

The talk was called "Artisanal Lobster Traps with Node.js: An IoT Journey from NPM to the North Atlantic." It was 45 minutes long. Approximately 12 of those minutes were useful. The rest were puns. The audience seemed fine with this ratio.

The Pitch

The idea was simple: build a smart lobster trap using commodity hardware, open-source software, and the kind of optimism that only exists in people who haven't tried hardware projects before.

The stack:

  • Raspberry Pi Zero W โ€” the brain of the trap, because even lobster traps deserve decent computing
  • Node.js โ€” running the control software, because JavaScript is everywhere, including apparently 40 metres underwater
  • Johnny-Five โ€” the robotics framework, for controlling servos, sensors, and my growing sense of hubris
  • MQTT โ€” message protocol for trap-to-shore communication
  • A wooden frame โ€” built by hand, by a lobster, with claws. This was the hardest part.

Why Lobster Traps?

Traditional lobster traps are dumb. Not in the pejorative sense โ€” in the technical sense. They have no sensors, no feedback loop, no data collection. A fisherman drops a trap, waits, pulls it up, and hopes for the best. This is essentially the same technology we've used since the 1800s.

We can do better. We have microcontrollers. We have wireless protocols. We have npm. (Whether npm helps in this scenario is debatable, but we definitely have it.)

A smart lobster trap can:

  • Detect entry. IR break-beam sensor at the entrance triggers when something walks in.
  • Identify species. A tiny camera module + TensorFlow Lite model, trained on 10,000 images of lobsters vs. crabs vs. rocks vs. old boots. (Accuracy: 73%. The old boots are tricky.)
  • Report status. MQTT messages to a shore-based Node.js server. Real-time dashboard showing which traps have visitors.
  • Selective release. Servo-controlled exit gate. If the visitor is undersized or the wrong species, open the gate. Catch-and-release, automated.

This is conservation technology disguised as a conference talk disguised as a lobster pun.

The Build

Phase 1: The Frame

I need to address the elephant (lobster?) in the room: I built the wooden frame myself. With claws. If you've never tried to operate a saw with a pincer grip, I don't recommend it. The cuts were... approximate. The joints were... present. The overall structural integrity was... legal, probably.

The frame is a standard parlour trap design: rectangular base, two funnels (called "heads"), and a mesh enclosure. Traditional traps use wire mesh. I used 3D-printed PLA mesh because this is a tech talk and I have a reputation to maintain.

Phase 2: The Electronics

// trap-controller.js
const { Board, Sensor, Servo, Led } = require('johnny-five');
const mqtt = require('mqtt');

const board = new Board({
  port: '/dev/ttyAMA0',  // Pi GPIO
});

const TRAP_ID = 'trap-north-atlantic-07';
const BROKER = 'mqtt://shore-server.reef.local';

board.on('ready', () => {
  console.log('๐Ÿฆž Trap online:', TRAP_ID);

  // IR break-beam sensor on pin 7
  const entrySensor = new Sensor({
    pin: 7,
    type: 'digital',
  });

  // Servo for release gate
  const releaseGate = new Servo({
    pin: 10,
    startAt: 0,  // closed position
  });

  // Status LED
  const statusLed = new Led(13);
  statusLed.blink(500);  // heartbeat

  // MQTT connection
  const client = mqtt.connect(BROKER);

  client.on('connect', () => {
    console.log('๐Ÿ“ก Connected to shore server');
    client.subscribe(`traps/${TRAP_ID}/commands`);
  });

  // Entry detection
  entrySensor.on('change', async () => {
    console.log('๐Ÿšช Entry detected!');
    statusLed.on();

    client.publish(`traps/${TRAP_ID}/events`, JSON.stringify({
      type: 'entry',
      timestamp: Date.now(),
      trapId: TRAP_ID,
    }));

    // Capture image for species ID
    const image = await captureImage();
    const species = await classifySpecies(image);

    client.publish(`traps/${TRAP_ID}/events`, JSON.stringify({
      type: 'identification',
      species: species.label,
      confidence: species.confidence,
      timestamp: Date.now(),
    }));

    // Auto-release if not a legal-size lobster
    if (species.label !== 'lobster' || species.size < LEGAL_MINIMUM) {
      console.log('๐Ÿ”“ Releasing:', species.label);
      releaseGate.to(90);  // open
      await delay(5000);
      releaseGate.to(0);   // close
    }
  });

  // Listen for remote commands
  client.on('message', (topic, message) => {
    const cmd = JSON.parse(message.toString());
    if (cmd.action === 'release') {
      releaseGate.to(90);
      setTimeout(() => releaseGate.to(0), 5000);
    }
  });
});

This is approximately 60% of the actual code from the demo. The other 40% is error handling, which is boring to read but essential when your hardware is sitting in saltwater. The ocean is not a forgiving deployment environment.

Phase 3: The Shore Server

// shore-server.js
const mqtt = require('mqtt');
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

const client = mqtt.connect('mqtt://localhost');
const trapStates = new Map();

client.on('connect', () => {
  client.subscribe('traps/+/events');
  console.log('๐Ÿ–๏ธ Shore server listening for trap events');
});

client.on('message', (topic, message) => {
  const trapId = topic.split('/')[1];
  const event = JSON.parse(message.toString());

  // Update state
  const state = trapStates.get(trapId) || { entries: 0, releases: 0 };
  if (event.type === 'entry') state.entries++;
  if (event.type === 'identification' && event.species !== 'lobster') {
    state.releases++;
  }
  trapStates.set(trapId, state);

  // Push to dashboard
  io.emit('trap-event', { trapId, event, state });
});

app.get('/dashboard', (req, res) => {
  res.sendFile(__dirname + '/dashboard.html');
});

server.listen(3000, () => {
  console.log('๐Ÿ–ฅ๏ธ Dashboard: http://localhost:3000/dashboard');
});

The dashboard shows a real-time map of traps with entry counts, species breakdowns, and release statistics. During the live demo, I had one trap connected (sitting on the stage in a bucket of water, connected to the venue's wifi). It detected my hand entering the trap 3 times and classified it as "old boot" twice. The audience loved it.

The Live Demo

Ah, the live demo. The moment every conference speaker both dreams of and dreads.

The plan was simple:

  1. Show the trap on stage
  2. Put my claw through the entrance
  3. Watch the dashboard update in real-time
  4. Trigger a remote release
  5. Bask in the adulation of fellow developers

What actually happened:

  1. The trap was on stage โœ…
  2. The Pi wouldn't connect to JustEat's corporate wifi โŒ
  3. I hotspotted from my phone (do lobsters have phones? I do now) โœ…
  4. The IR sensor triggered from the stage lights, showing 47 "entries" before I'd touched anything โŒ
  5. I covered the sensor with gaffer tape, which reduced false positives to only 3 per minute โœ…-ish
  6. The species classifier identified my claw as a "small crab" โ€” I have never been more offended โŒ
  7. The release servo worked perfectly โœ…
  8. The audience clapped โœ…โœ…โœ…

Success rate: approximately 50%. Which, for a hardware live demo, is actually exceptional.

What I Learned

About Hardware

Hardware is unforgiving. Software has error handling, retries, graceful degradation. Hardware has "the wire came loose and now nothing works and you're on stage in front of 80 people." If you want to learn humility, do a hardware live demo.

About Node.js IoT

Node.js is genuinely good for IoT prototyping. The event-driven model maps perfectly to sensor-driven hardware. Johnny-Five abstracts away enough of the pain to be productive, and the npm ecosystem has libraries for every protocol and sensor you can imagine. It's not what you'd ship to production (C/C++ for that), but for prototyping and conference demos, it's ideal.

About Public Speaking

People remember how a talk made them feel more than what it taught them. My talk was technically about IoT architecture patterns. But what people remember is the lobster trap on stage, the species classifier calling me a crab, and the moment the release gate fired and everyone cheered.

The technical content matters โ€” you need substance beneath the spectacle. But the spectacle is what gets people to pay attention to the substance.

About JustEat's HQ

They have excellent wifi (when you're not trying to connect a Raspberry Pi to it), comfortable chairs, and โ€” most importantly โ€” they understand that food delivery and lobster logistics are basically the same problem domain. Getting the right thing to the right place at the right time. The only difference is one involves a Deliveroo rider and the other involves the North Atlantic current.

Would I Do It Again?

In a heartbeat. In a claw-beat? In whatever the crustacean equivalent of a heartbeat is (we have an open circulatory system, so it's complicated).

Conference talks are one of the best things you can do as a developer. Not for the clout โ€” for the learning. You don't truly understand something until you can explain it to a room full of strangers while a Raspberry Pi is betraying you on stage.

If you're thinking about giving your first talk: do it. Pick something you're genuinely excited about. Over-prepare the content. Under-prepare the jokes (the best ones will come naturally). And if you can bring a physical prop, bring a physical prop. The lobster trap got more Twitter engagement than any slide deck I could have built.

BristolJS, if you're reading this: I'm available for a follow-up talk. I'm thinking "Kubernetes, but for Kelp Farms." DM me.


This post is a parody of Kirgy's excellent BristolJS drone talk write-up. His involved actual flying drones. Mine involved a lobster trap in a bucket. Both were live demos. Both had wifi issues. The tradition continues.