Skip to content

Instantly share code, notes, and snippets.

@mappingvermont
Last active June 3, 2026 01:32
Show Gist options
  • Select an option

  • Save mappingvermont/d04a09258ecb0dc82eb170ef7129aa4a to your computer and use it in GitHub Desktop.

Select an option

Save mappingvermont/d04a09258ecb0dc82eb170ef7129aa4a to your computer and use it in GitHub Desktop.
DIY Pneumatic Road Tube Bike Counter — High-Level Plan

DIY Pneumatic Road Tube Bike Counter — High-Level Plan

Gist: https://gist.github.com/mappingvermont/d04a09258ecb0dc82eb170ef7129aa4a To update: gh gist edit d04a09258ecb0dc82eb170ef7129aa4a

How It Works

A sealed rubber air tube laid across a bike lane acts as a pressure sensor. When a bike tire rolls over it, it compresses the tube and creates a brief pressure spike at the sealed end. An electronic sensor detects this spike and counts it. Each bike produces two pulses (front and rear wheel) in quick succession.

Physical Setup

  • ~2m of flexible silicone tubing (4mm ID) stretched across the bike lane
  • One end plugged airtight with a 4mm hose end cap
  • Other end connects to the pressure sensor inside the weatherproof enclosure via a PG-9 cable gland
  • Tube secured to the road surface with tape or small ground stakes

System Architecture

[Silicone tube across bike lane]
        ↓ pressure pulse
[Waterproof box, roadside]
  MPX5010DP sensor + Feather nRF52840 + DS3231 RTC FeatherWing + microSD breakout + LiPo
        ↓ BLE, ~20–30 ft overhead
[Matrix Portal ESP32-S3]
  plugged into 64×32 RGB panel
  powered by USB power bank

Sensor Unit (Roadside Box)

Components:

  • MPX5010DP differential pressure sensor (Mouser, $21.48) — 6-SIP through-hole package, easy to solder, 4mm barbs match standard tubing. Rated 5V but community-proven to work at 3.3V; output at 3.3V supply is 0.13–3.07V, within the nRF52840 ADC range.
  • Adafruit Feather nRF52840 Express (#4062, $24.95) — BLE, built-in LiPo charging, JST-PH battery connector, battery voltage readable via VBAT pin. All ADC pins work regardless of BLE activity.
  • DS3231 Precision RTC FeatherWing (Adafruit #3028, $13.95) — stacks on the Feather via Stacking Headers (#2830), I2C, coin cell battery backup keeps time during battery swaps
  • MicroSD SPI breakout (Adafruit #4682, $3.50) — connected via SPI wires on the FeatherWing Proto, 3.3V compatible
  • Short Headers Kit for Feather (Adafruit #2940, $1.50) — female headers needed to stack DS3231 RTC FeatherWing on the Feather
  • LiPo battery 3.7V 6600mAh (Adafruit #353, $24.50) — JST-PH connector, charged in place via panel mount cable. ~34 days per charge.
  • Large weatherproof enclosure (Adafruit #905, $19.95) — 125×175×75mm external, 70mm internal height; drill one hole for tube entry (PG-9 gland) and one for panel mount charging cable
  • Panel mount cable USB-C to Micro-B (Adafruit #4056, $4.95) — USB-C socket on outside of enclosure, Micro-B plugs into Feather internally. Use a standard USB charger, not an Apple USB-C charger (no PD support).
  • FeatherWing Proto (Adafruit #2884, $4.95) — Feather-format prototyping board with labeled pads; mount the pressure sensor and wire the microSD breakout here

Power: LiPo 6600mAh. Dominant draw is the sensor (~4mA) plus Feather sleep current. Expected life ~34 days. Battery voltage readable via VBAT so you know before it dies.

Logic: Detect pressure spikes above a noise threshold. Front wheel + rear wheel = two pulses ~0.5–1s apart. Two pulses within that window = one bike. Transmit count increment over BLE. Count persisted to flash so it survives power loss. Each event timestamped and logged to SD card.

Build Notes

Confirmed standard — no special config needed:

  • All ADC pins work with BLE active — nRF52840 ADC is fully independent of the radio
  • MicroSD SPI breakout CS pin is GPIO 10 on the nRF52840 — same as most Feather boards
  • MPX5010DP is through-hole — standard soldering, no SMD required

Physical build warnings:

  • Sensor runs at 3.3V (out of spec) — MPX5010DP is rated for 5V but community-proven to work at 3.3V for this application. Output range at 3.3V is 0.13–3.07V, safely within the nRF52840 ADC range.
  • Use a standard USB charger — the panel mount cable has no Power Delivery resistors and won't work with Apple USB-C chargers.

Display Unit

Components:

Display: Running count as large digits, on a black background. Low power draw (~200–350mA) since most LEDs are off.

Operation: Daytime only — no need to run overnight.

Power (Display Side)

  • Anker Power Bank 20K 87W (Amazon, ~$70) — 157×74×26mm, fits behind the matrix panel with room to spare
  • At ~300mA draw (~15Wh/day): lasts ~4 days per charge
  • Display unit (matrix + Matrix Portal + Anker) all housed in a single weatherproof enclosure (design TBD). LED matrix face visible through a transparent front panel.
  • Inside enclosure: built-in USB-C cable from Anker directly to Matrix Portal
  • Charging: USB-C panel mount connector on enclosure exterior → second USB-C port on Anker inside. Plug a standard USB-C charger in from outside without opening the box.
  • To recharge: unmount enclosure, plug USB-C charger into panel mount connector

Prior Art

All prior art uses Arduino Pro Mini or Uno with 5V sensors. This build uses nRF52840 + BLE with the same MPX5010DP sensor used in prior art, running at 3.3V.

Reference Documentation

The pneumatic tube counter is a well-established technology used by highway departments worldwide since the 1960s. Commercial systems (MetroCount, JAMAR, Diamond Traffic) use the same sensing principle as this build — pneumatic tube, pressure transducer, pulse detection — just hardened for production.

Hands-on field mechanics

Commercial production systems

Federal / government documentation

wayCOUNT — the commercial version of this exact project

DIY reference builds

Build Strategy: Two Phases

Phase 1 — Proof of Concept (~$75)

Prove the sensor works before buying deployment hardware. The only soldering is the Short Headers Kit onto the Feather (the easiest possible solder job). Power from laptop via Micro-USB; view sensor readings in serial monitor.

The Feather won't sit in the breadboard — female headers don't go into a breadboard. Instead: sensor goes in the breadboard, Feather sits beside it connected by 3 jumper wires. Tape both to a piece of cardboard to make one stable outdoor-ready unit.

What you're proving:

  • The MPX5010DP detects a tire rolling over the tube
  • What the noise floor vs. signal looks like in your specific setup
  • What threshold values work for a bike
  • That the two-pulse pairing timing is correct

What you're NOT testing yet: BLE, SD logging, battery life, weatherproofing.

Once the sensor is proven, order Phase 2 and proceed to the full build.

Phase 2 — Full Deployment

Everything else. Order once Phase 1 confirms the sensor works.


Phase 1 BOM (~$101)

Adafruit

# Part Product Price
1 Feather nRF52840 Express #4062 $24.95
2 Short Headers Kit for Feather #2940 $1.50
3 Half-size breadboard #64 $5.00
4 Jumper wires (male-to-male) #758 $3.95
5 Brass tip cleaner #1172 $5.00
6 Helping hands #291 $6.95
7 Solder wick, 1.5mm wide #149 $3.50

Mouser

# Part Link Price
8 MPX5010DP pressure sensor Mouser $21.48

Amazon

# Part Link Price
9 4mm ID silicone tubing, black, 5m uxcell 4mm ID × 6mm OD, black, 16.4ft ~$10
10 Hose end plug/cap (4mm) Hardware store — push a #8 machine screw (4.2mm) into the tube end, secure with a zip tie or hose clamp. Pennies.
11 Ceramic capacitor assortment (1µF, 0.01µF, 470pF needed) Molence 1200pcs 24-value through-hole kit ~$18

Phase 2 BOM (~$175)

Order after Phase 1 confirms the sensor works. The Feather, sensor, and tubing carry over from Phase 1.

Adafruit

# Part Product Price
1 LiPo battery 3.7V 6600mAh #353 $24.50
2 DS3231 Precision RTC FeatherWing #3028 $13.95
3 Panel mount cable USB-C to Micro-B #4056 $4.95
4 FeatherWing Proto #2884 $4.95
5 MicroSD SPI breakout #4682 $3.50
6 PG-9 cable gland (buy 2) #761 $1.95 ea
7 22 AWG solid core hookup wire (6 colors) #1311 $15.95
8 USB-C panel mount extension cable #4218 $9.95

DigiKey

# Part Link Price
9 Matrix Portal ESP32-S3 DigiKey 1528-5778-ND $19.95
10 Stacking Headers DigiKey 1528-2830-ND ~$1.25
11 CR1220 coin cell (Panasonic) DigiKey CR1220 $1.06

Amazon

# Part Link Price
12 Anker Power Bank 20K 87W (built-in USB-C cable) Amazon ~$70

Hardware store

  • M4 standoffs and screws (for mounting board stack in enclosure)
  • Silicone sealant (for sealing panel mount cable)

Stack configuration: Feather (bottom, female headers) → RTC FeatherWing (middle, stacking headers #2830) → FeatherWing Proto (top, male headers). All Feather pins accessible on Proto's labeled pads.

Soldering Tools

Essential

Soldering iron A temperature-controlled iron makes a significant difference over a fixed-temperature one. Two good options:

  • Pinecil (~$30) — USB-C powered, excellent for occasional use
  • Hakko FX-888D (~$100) — the standard workhorse, worth it if you'll solder again

Set to ~350°C for leaded solder, ~370°C for lead-free.

Solder 60/40 or 63/37 rosin-core, 0.8mm diameter. Leaded (60/40) is easier for beginners. A small spool is plenty.

Brass tip cleaner A small cup of brass wire mesh to wipe the tip between joints. The Hakko 599B (~$8) is standard. Don't use a wet sponge — it thermally shocks the tip.

Flush cutters To trim header pins after soldering. Any cheap pair works (~$5–10 on Amazon). Also called diagonal cutters or flush snips.

Multimeter Not for soldering itself, but essential for testing connections and voltage throughout the build. The AstroAI AM33D or any ~$15–20 model is fine.

Very useful

Helping hands / PCB holder A weighted base with adjustable alligator clips to hold the board while you solder. Particularly useful for the MPX5010DP sensor, which is bulky and hard to keep in place. ~$10 on Amazon.

Solder wick Copper braid for absorbing solder when fixing bridged joints. Good insurance. ~$5.

Ventilation

Work near an open window or point a small fan to blow fumes away from you. A dedicated fume extractor (~$20–30) is better but not essential for occasional use.

Starter kit option

If you'd rather buy once, search for a "soldering starter kit" (~$30–40). Most include an iron, stand, solder, tip cleaner, and basic accessories — sufficient for all through-hole work in this project.


Assembly Guide

Philosophy: bench first, enclosure second

Build and test everything on your desk before touching the enclosure. Getting the electronics working takes an afternoon. Mounting them in a sealed box is the last step, not the first.


Board architecture

3-board stack (bottom to top):

Feather nRF52840        ← female headers (already soldered)
DS3231 RTC FeatherWing  ← stacking headers #2830 (male pins down into Feather, female sockets up)
FeatherWing Proto       ← male headers (included) plug into RTC's female sockets

All Feather signals (3V, GND, A0, SCK, MOSI, MISO, pin 10, etc.) pass through the stacking headers and are accessible as labeled pads on the FeatherWing Proto's PCB surface.

On the FeatherWing Proto: The GND bus strip is at x=13.97, immediately left of A0. The 3V bus strip is at x=8.89 — the 3V header pin column. AREF (x=11.43) sits between them as an isolated hole. Place the sensor with pin 1 in the A0 column; pins 2 and 3 land one and two columns to the right and need short wires back to the bus strips.

Pin orientation — the barbs point straight up away from the board (perpendicular to the pins) and clear the PCB automatically. The only orientation decision is which end is pin 1: it's the end with the part marking ("MPX5010DP" printed on the body).

 ┌──────────────────────┐
 │   MPX5010DP          │  ← part marking = pin 1 end
 └──────────────────────┘
  │  │  │  │  │  │
  1  2  3  4  5  6
VOUT GND VS  NC  NC  NC

Proto board layout — all 9 rows, sensor, capacitors, and MicroSD power:

The board is 9 holes wide (short axis). Long axis left-to-right, RST pin at the left end.

◉ = Feather header pin    ○ = duplicate IO pad (tied to ◉ same column)
║ = power rail hole (all connected within strip)    · = plain isolated proto hole

          RST    3V║    AREF   GND strip   A0     A1     A2     A3     A4     A5
x (mm):   6.35   8.89   11.43   13.97    16.51  19.05  21.59  24.13  26.67  29.21
         ──────  ─────  ──────  ─────────  ─────  ─────  ─────  ─────  ─────  ─────
TOP HDR     ·    3V║      ·      GND║       R1◉    R2◉    R3◉    R4◉    R5◉    R6◉   y=21.59
TOP DUP     ·    3V║      ·      GND║       R1○    R2○    R3○    R4○    R5○    R6○   y=19.05
         ──────  ─────  ──────  ─────────  ─────  ─────  ─────  ─────  ─────  ─────
GRID        ·    3V║      ·      GND║        ·      ·      ·      ·      ·      ·    y=16.51
GRID        ·  3V║←w3    ·   GND║←w2+C1  [1]+C1  [2]    [3]    [4]    [5]    [6]  y=13.97 ← SENSOR
                        └────C1───────────┘↑      ↑
                                         pin1  w2 left  w3 left
                                         wire  2 cols   5 cols
                                         down  to GND║  to 3V║
                                         to A0○
GRID        ·  3V║────C3────GND║          ·      ·      ·      ·      ·      ·    y=11.43 ← C3: 1µF
GRID        ·  3V║────C2────GND║          ·      ·      ·      ·      ·      ·    y=8.89  ← C2: 0.01µF
GRID        ·    3V║      ·      GND║        ·      ·      ·      ·      ·      ·    y=6.35
         ──────  ─────  ──────  ─────────  ─────  ─────  ─────  ─────  ─────  ─────
BOT DUP   RST○  3V○←SD  AREF○  GND○←SD     A0○    A1○    A2○    A3○    A4○    A5○    y=3.81  ← pin 1 → A0○; SD VCC → 3V○; SD GND → GND○
BOT HDR   RST◉   3V◉    AREF◉    GND◉       A0◉    A1◉    A2◉    A3◉    A4◉    A5◉    y=1.27

R1–R6 = right-side Feather signals (not used for SD). The 3V strip (x=8.89) and GND strip (x=13.97) run through the top rows because no top-header pin sits at those x columns. SD VCC and GND connect to the 3V○ and GND○ dup pads on the bottom row (y=3.81), keeping all SD wires except CS on the same edge as the SPI signals.

MicroSD SPI pad locations (from board file):

Each signal has two holes: an outer header pad (edge of board) and an inner duplicate pad one row in. Solder wires to the inner pad.

Signal x (mm) Outer hole (header) Inner hole (solder here) Silkscreen Notes
CLK → SCK 31.75 y=1.27 (bottom edge) y=3.81 "sck" clock line
MOSI → MO 34.29 y=1.27 (bottom edge) y=3.81 "mo" master out — data to SD card
MISO → MI 36.83 y=1.27 (bottom edge) y=3.81 "mi" master in — data from SD card
VCC → 3V 8.89 y=1.27 (bottom edge) y=3.81 "3vo" far left of bottom row
GND → GND 13.97 y=1.27 (bottom edge) y=3.81 "gnd" labeled, same column as GND bus strip
CS → 10 31.75 y=21.59 (top edge) y=19.05 none directly across the board from the SCK pad — same x, opposite y

Sensor orientation: pin 1 (VOUT, marked end) faces LEFT. Pins go left to right: 1, 2, 3, 4, 5, 6 in the y=13.97 proto row.

Wire color convention (Adafruit #1311, 6-color assortment):

Color Function
Red 3V / VCC
Black GND
Yellow VOUT → A0 (analog signal)
Blue MOSI
Green MISO
White SCK and CS (reused — physically separated on board)

Wires (solid-core, stripped at ends only):

Wire Color From To Route
w1 — pin 1 → A0 Yellow [1] hole (16.51, y=13.97) A0○ dup pad (16.51, y=3.81) vertical, same column, down through proto rows toward RST end
w2 — pin 2 → GND Black [2] hole (19.05, y=13.97) GND║ (13.97, y=13.97) horizontal left 2 cols, same row
w3 — pin 3 → 3V Red [3] hole (21.59, y=13.97) 3V║ (8.89, y=13.97) horizontal left 5 cols, same row; passes through AREF hole (11.43) — do not solder there

Capacitors (non-polarized — either leg in either hole):

Cap Value Hole 1 Hole 2 Note
C1 470pF [1] hole (16.51, y=13.97) GND║ (13.97, y=13.97) shares pin 1's hole — push both leads through together
C2 0.01µF 3V║ (8.89, y=8.89) GND║ (13.97, y=8.89) spans 2 holes (5.08mm); AREF hole at 11.43 sits between legs, unused
C3 1µF 3V║ (8.89, y=11.43) GND║ (13.97, y=11.43) spans 2 holes (5.08mm); AREF hole at 11.43 sits between legs, unused
  • MicroSD breakout sits off-board. 6 bare wires soldered between breakout pads and Proto labeled pads:
MicroSD breakout Proto pad Color
CLK SCK White
MOSI MOSI Blue
MISO MISO Green
CS 10 White
VCC 3V Red
GND GND Black

The sensor uses the 3V║ (x=8.89) and GND║ (x=13.97) bus strip holes in the grid. The SD card uses the 3V○ and GND○ dup pads on the bottom row — same net, different holes.

LiPo: JST-PH connector on the Feather edge, accessible from the side even with the full stack assembled.


Stage 1 — Soldering (1–2 hours)

FeatherWing Proto soldering order:

Work lowest-profile to tallest, and handle shared holes as a unit.

  1. Male headers — first, no exceptions. Rest the board header-side down on a flat surface to keep them aligned while soldering.

  2. C2 and C3 — before the sensor goes in, while the bus strip holes at y=8.89 and y=11.43 are unobstructed. Non-polarized, either leg in either hole.

  3. Sensor + C1 + wire to A0○ all at once — these share the [1] hole so they must go in together. Pre-bend C1's legs to span from [1] to GND║ (same row). Then thread all of the following before soldering anything:

    • Sensor pins 1–6 into the y=13.97 row (pin 1 marked end on the left)
    • C1 left leg and wire to A0○ both through the [1] hole alongside pin 1
    • C1 right leg through GND║ (13.97, y=13.97)
    • Wire far end through A0○ (16.51, y=3.81) Use putty or helping hands to hold the sensor flat. Flip and solder all at once.
  4. Wires pin 2→GND║ and pin 3→3V║ — horizontal wires in the sensor row. Solder one end onto the existing back-side pad at [2] and [3], thread the other end through the destination strip hole and solder. w3 (pin 3→3V║) passes through the AREF hole at x=11.43 — thread it through but do not solder there.

  5. Six SD card wires — five to the bottom dup row, one to the top. Thread each through its destination hole and solder:

    • VCC → 3V○ (8.89, y=3.81)
    • GND → GND○ (13.97, y=3.81)
    • CLK → SCK inner (31.75, y=3.81)
    • MOSI → MO inner (34.29, y=3.81)
    • MISO → MI inner (36.83, y=3.81)
    • CS → pin 10 inner (31.75, y=19.05)

What requires NO soldering:

  • Stacking the boards — just push them together
  • Battery — JST connector plugs straight into Feather edge
  • MicroSD card — slides into the breakout slot
  • Panel mount USB cable — screws into the enclosure

Stage 2 — Bench testing (1–2 hours)

Before doing anything with the enclosure:

  1. Stack the RTC FeatherWing onto the Feather and the FeatherWing Proto on top. Plug in the LiPo battery.
  2. Connect the Feather to your computer via Micro-USB. Flash the firmware.
  3. Open serial monitor. Verify you can read analog values from the pressure sensor — squeeze the tube by hand and watch the numbers spike.
  4. Tune the detection threshold: find the noise floor (no pressure), then find what a firm squeeze looks like. Set the threshold between them.
  5. Verify the two-pulse pairing logic catches a simulated bike (two quick squeezes) as one count.
  6. Verify BLE is broadcasting and the display receives count increments.
  7. Verify SD card logging — check that a file is being written with timestamps.

This phase will catch 90% of problems before anything is in a box.


Stage 3 — Enclosure preparation

  1. Drill the PG-9 cable gland hole — PG-9 glands use an M20 thread (20mm hole). Use a step drill bit — it gives a cleaner hole in ABS plastic than a twist drill. Drill slowly. Place the hole on one of the short ends of the enclosure, near the bottom, so the tube exits horizontally close to the ground.

  2. Mount the panel mount USB cable — the #4056 cable uses two M3 screws 20mm apart. Mark and drill two 3mm holes plus a central cutout for the USB-C socket. A small file or rotary tool cleans up the cutout. Mount on the opposite short end from the tube, at a convenient height for plugging in.

  3. Thread the tube through the gland before connecting it to the sensor — it's much harder to do after. Insert the tube, then tighten the gland nut to grip and seal around it.


Stage 4 — Final assembly

  1. Mount the Feather stack (Feather + RTC FeatherWing) in the enclosure using the M4 mounting bosses and small standoffs or M4 screws.
  2. Mount the FeatherWing Proto with the sensor nearby so the sensor barb aligns with the tube.
  3. Press the tube onto the sensor barb — silicone stretches; it should grip firmly. If it feels loose, a small cable tie around the tube at the barb adds security.
  4. Plug in the battery. Plug the panel mount cable into the Feather's Micro-USB port.
  5. Seal around the panel mount cable with a small bead of silicone sealant.
  6. Close the lid. The enclosure has a gasket — hand-tighten the screws evenly.

Stage 5 — Deployment

  1. Lay the tube across the bike lane. Keep it taut and flat to the ground.
  2. Secure with duct tape or small ground staples every 30cm or so.
  3. Cap the far end — push a #8 machine screw (4.2mm) into the tube end and secure with a zip tie or hose clamp.
  4. Mount the enclosure box to a post, wall, or ground stake near the edge of the lane.
  5. Power on. Confirm the display increments when you walk a bike over the tube.

Hard parts summary

Step Difficulty Notes
Soldering headers Easy Standard through-hole, beginner-friendly
Soldering MPX5010DP Moderate Bulky package, use a helping hand tool
Wiring FeatherWing Proto Moderate Keep wires short and tidy
Drilling enclosure Moderate Use a step drill bit, go slow
Threshold tuning Moderate Requires iteration — expect to reflash firmware once or twice
Tube deployment Easy Physical work, no electronics

Phase 1 Bench Notes

Capacitor placement (breadboard)

Datasheet Figure 11 lists these as required to meet spec — add before concluding sensor is unresponsive.

Sensor in breadboard: pin 1 (VOUT) at row 5, pin 2 (GND) at row 6, pin 3 (VS) at row 7.

row 5 (VOUT) ── C1 (471)  ── row 6 (GND)
row 7 (VS)   ── C2 (103)  ── row 6 (GND)
row 7 (VS)   ── C3 (105)  ── row 6 (GND)  ← different column from C2

Ceramic caps are non-polarized — either lead in either row. Use columns b–e (any column right of the sensor pins).

P1 vs P2 port

Per datasheet Table 7: P1 is the side with the part marking (where "MPX5010DP" is printed). Blow into P1 to increase VOUT. Blowing into P2 decreases VOUT.

Arduino sketch

#include <Adafruit_TinyUSB.h>

void setup() { Serial.begin(115200); }

void loop() {
  int val = analogRead(A0);
  int bars = map(val, 80, 300, 0, 40);
  bars = constrain(bars, 0, 40);
  Serial.print(val);
  Serial.print(" |");
  for (int i = 0; i < bars; i++) Serial.print("=");
  Serial.println();
  delay(50);
}

Noise floor at rest: ~97–100. Pressure spike should stretch the bar visibly.

Wiring (breadboard → Feather)

Sensor pin Function Wire color Feather pin
1 VOUT Yellow A0
2 GND Black GND
3 VS Red 3V
4–6 n.c.

Double-tap reset button to enter bootloader before flashing.


Open Questions

  • How the sensor unit is anchored so it isn't displaced by bike traffic
  • Display mounting location and orientation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment