Builder in Residence

Project indexBuild 04

ResQmesh

A lightweight ESP32-based drone swarm for resilient emergency communication and environmental monitoring.

Status
Active
Weekly log
10 of 10 weeks written up
Last commit
20 Sept 2026
Source
GitHub

Weekly log

Nine weeks, written down as they happened.

Week 01

The ₹3,000 Swarm Gamble

Every drone swarm project we’ve ever seen starts with a jaw-dropping budget: tens of thousands of dollars, proprietary radio links, and drones so expensive that watching one clip a tree branch feels like watching a stack of cash catch fire.

We wanted to flip that script completely. What if a swarm wasn't made of precious, monolithic aircraft, but of disposable, dirt-cheap micro-drones that work together like ants? If one runs out of juice or crashes into a wall, the mission shouldn’t fail—the rest of the swarm should just adapt and keep moving.

That was the core question that kicked off ResQmesh.

Setting an Impossible Constraint

We started on the whiteboard with one non-negotiable rule: every single flight node has to cost under ₹3,000 (~$35 USD).

Commercial Swarm Unit:  [ $2,000+ ]  --> Lose one = Catastrophe
ResQmesh Target Unit:   [ < ₹3,000 ]  --> Lose one = Keep flying
  • Brushless motors and 4-in-1 ESCs? Out. Too heavy for a micro frame, and an ESC stack alone would eat our entire budget.
  • Full-sized STM32 flight controllers? Out. Expensive and bulky.
  • Commercial GPS modules? Out. They don't work reliably indoors anyway, and good ones blow past our price ceiling instantly.

Instead, we decided to build around the Seeed Studio XIAO ESP32-S3. It packs a dual-core Xtensa LX7 running at 240 MHz, built-in 2.4 GHz Wi-Fi and BLE, native USB, and enough GPIOs for an I2C bus and 4 PWM channels—all on a postage-stamp-sized PCB for a fraction of the cost of dedicated flight boards.

Brainstorming Where This Actually Matters

We didn’t want to build a swarm just to watch blinking LEDs in an auditorium (though that’s cool too). We wanted a platform with real, dirty, practical utility. Over a marathon whiteboarding session, we narrowed down six mission profiles where expendable micro-swarms solve problems single big drones can’t touch:

  1. Search & Rescue in Tight Rubble: After a structural collapse, heavy drones can't squeeze through broken concrete or navigate dust-filled hallways. Micro quadcopters can slip into gaps, sniff for survivors, and act as short-range audio/video beacons.
  2. Precision Canopy & Microclimate Mapping: Instead of taking one reading at a time with a handheld probe, imagine dropping ten tiny nodes across a greenhouse or orchard to measure humidity, temperature, and light gradients simultaneously.
  3. Off-Grid Aerial Mesh Repeaters: Ground radios die when hills or thick concrete walls get in the way. Two or three hovering ESP-NOW nodes can form a dynamic airborne hop-line, relaying emergency sensor data over obstacles.
  4. GPS-Denied Warehouse & Duct Inspection: Flying where GPS signals don't penetrate—ventilation shafts, crawl spaces, and factory rafters.
  5. Open Swarm Robotics Testbed: Most university labs can't afford a fleet of 20 research drones for testing Reynolds Boids flocking or multi-agent reinforcement learning (MARL). A sub-₹3,000 node changes that equation.
  6. Synchronized Miniature Light Choreography: Coordinated indoor formation flights using high-brightness addressable RGBs without needing a football stadium.

The First Architecture Sketch

By the end of the week, our rough system architecture was locked in:

SubsystemSelected ComponentWhy We Picked It
Brain / MCUSeeed Studio XIAO ESP32-S3Dual-core 240 MHz compute + native ESP-NOW radio on a tiny board
IMUInvenSense MPU9250 / MPU6050Fast 400 kHz I2C attitude tracking with well-documented register maps
Motors720 Coreless Brushed DC (7×20mm)~40g thrust each on 55mm props; pennies per motor; zero ESC needed
SwitchesAO3400A N-Channel MOSFETsLogic-level gate threshold (<1.45V) driven directly by 3.3V GPIOs
Battery1S 3.7V LiPo (~300–450 mAh)Keeps all-up weight under 35g to maintain a healthy >1.8:1 thrust margin
Mesh CommsPeer-to-Peer ESP-NOWSub-5ms packet latency without routers or cellular towers

What Kept Us Up at Night

The biggest open question wasn’t the code—it was the physics. A brushed 720 coreless motor is cheap, but it generates nasty inductive flyback when you pulse it with high-frequency PWM. If those voltage spikes bleed into the ESP32’s power rail or the MPU9250’s I2C lines, the microcontroller will brown out, the sensor bus will hang, and the drone will tumble out of the sky.

Next week, we fire up Fusion 360 and the 3D printers to see if we can build an airframe stiff enough to survive motor vibration without blowing our weight budget.

Week 02

Iterations, Snapped Arms, and Slicer Shrinkage

Before you write a single line of flight control code, your drone has to obey Newton. If the frame flexes when the motors spool up, your gyroscope will measure the bend of the plastic instead of the angle of the aircraft. When that happens, your PID loop tries to correct a phantom tilt, and the drone oscillates itself straight into the floor.

This week was all about fighting that mechanical reality on our 3D printer bed.

Three Iterations and a Pile of Scrap PLA

We went into CAD with three competing design philosophies, sliced them, printed them, and put them through the wringer:

[ Iteration 1: Skeletal X ]   -->  Weight: ~5.0g  -->  Arm flex like a wet noodle
[ Iteration 2: Ducted Guard ]  -->  Weight: ~12.0g -->  A flying brick, choked airflow
[ Iteration 3: Unibody Micro ] -->  Weight: ~7.5g  -->  Rigid, clean CG, the sweet spot

Iteration 1: The Skeletal Toothpick (~5.0g)

We got greedy on weight. We designed ultra-thin carbon-style arms with minimal trussing, weighing just over 5 grams. It looked great in CAD, but the moment we press-fit a test motor and spun it up, the arm vibrated like a tuning fork. Under full throttle, the motor pod deflected visibly by nearly 3 degrees. If we mounted our MPU9250 IMU on that, the vibration noise floor would completely swamp our attitude filter. Scrapped.

Iteration 2: The Ducted Prop Guard (~12.0g)

Swinging to the opposite extreme, we designed fully enclosed circular ducts around each 55mm prop to make the drone indestructible for indoor testing. It was definitely tough—you could throw it against a wall—but at 12 grams, it ate over a third of our total vehicle weight budget before we even added a battery or flight controller. Worse, our bench tests showed that at micro scales, the duct walls created turbulent vortex recirculation around the prop tips, killing hover efficiency. Scrapped.

Iteration 3: The Reinforced Unibody Micro-X (~7.5g)

This was the breakthrough. We abandoned round ducts and went for an open Quad-X geometry with an I-beam arm cross-section:

  • 100% Solid Perimeters: We set the slicer to print the motor arms with solid walls, eliminating hollow infill resonance.

  • Flight Controller Standoffs: Built raised vibration-dampening mounting pads in the center to isolate the XIAO ESP32-S3 and IMU from motor shock.

WhatsApp Image 2026-09-19 at 11 42 30 PM

The 0.15mm Slicer Headache

The hardest lesson of the week had nothing to do with aerodynamics and everything to do with plastic shrinkage.

Our coreless motors have an exact outer diameter of 7.00mm. On our first print of Iteration 3, the motor bores came off the bed measuring 6.85mm. We tried to press a motor in with our thumbs, heard a sickening snap, and split the entire motor pod down the layer lines.

Motor Outer Diameter:  7.00 mm
Raw 3D Print Bore:     6.85 mm  --> [CRACK!]
Tweaked with +0.12mm Hole Expansion: 7.02 mm  --> Snug friction fit!

Instead of re-modelling every motor pod in Fusion 360, we dug into slicer settings and calibrated Horizontal Hole Expansion by +0.12mm. On the next print, the bore came out at a perfect 7.02mm. The motors slid in with firm thumb pressure—snug enough that they wouldn't twist out under torque, but without stressing the layer adhesion.

Where We Stand

By Sunday evening, we had a completed, rigid unibody frame sitting on the scale at exactly 7.4 grams. Motor pods held firm, the battery snapped cleanly into the belly, and torsional stiffness passed our twist tests with flying colors.

Next week, we turn our attention to the electronics hunt: tracking down every resistor, diode, and MOSFET to build our flight hardware without breaching the ₹3,000 ceiling.

Week 03

Counting Pennies & Chasing Specs

Building a prototype on a breadboard with whatever parts you have lying around is easy. Building a repeatable, ultra-low-cost drone where every component has to hit a ruthless weight, electrical, and budget threshold is an exercise in ruthless optimization.

This week was all about component selection, datasheet digging, and placing the hardware orders for our fleet.

The ₹3,000 Bill of Materials Challenge

We sat down with a spreadsheet, scouring domestic suppliers, local robotics shops, and wholesale electronics portals. Every single rupee had to justify its existence on the bill of materials.

Here’s where our numbers landed:

Subsystem / PartQtyUnit Cost (₹)Total (₹)Why This Specific Part?
Seeed Studio XIAO ESP32-S31850850Dual-core compute, onboard antenna, native USB-C, ultra-tiny footprint
InvenSense MPU9250 IMU1420420Fast 400 kHz I2C attitude tracking with 3-axis gyro + 3-axis accel
720 Coreless Brushed Motors (CW/CCW pairs)47530050,000+ RPM punch; lightweight cylindrical form factor
55mm Micro Propellers (2x CW, 2x CCW)42080High static thrust matched to 7mm motor shaft
AO3400A N-Channel MOSFETs (SOT-23)41248Logic-level switching ($R_{ds(\text{on})} < 30\text{ m}\Omega$ @ 3.3V gate)
1N5819 Schottky Flyback Diodes4416Ultra-fast back-EMF clamping across motor inductive loads
Passives (Caps, Resistors, Star-Ground Perfboard)1 set6060470µF bulk low-ESR cap, 100nF decoupling, gate pulldowns
1S 3.7V 380 mAh LiPo Battery1320320High discharge rate (25C), lightweight (~10.5g)
3D-Printed Unibody Micro-X Frame18080Raw PLA filament cost for our 7.4g optimized frame
Misc (Wiring, JST connectors, silicone lead)1 set5050High-strand-count silicone wire for minimal resistance
TOTAL BOM PER FLIGHT NODE₹2,224Well below our ₹3,000 ceiling!

Coming in at ₹2,224 per drone gave us a comfortable ₹776 buffer per node for test jigs, spare propellers, and replacement motors when bench testing inevitably claims a few casualties.

The Logic-Level MOSFET Discovery

Our biggest technical win of the week came from digging deep into the AO3400A MOSFET datasheet.

In many DIY quadcopter projects, people try to drive standard power MOSFETs directly from 3.3V microcontroller pins, only to discover that the FETs never fully turn on. Standard gates often need 4.5V to 10V to reach saturation. If your gate isn't fully open, the MOSFET acts like a resistor, dissipates massive heat, drops the battery voltage, and burns out.

Standard FET @ 3.3V Logic:   Partially Open --> Hot! High Rds(on) --> Wasted battery
AO3400A FET @ 3.3V Logic:    Fully Saturated --> Cool (<30 mΩ) --> 98%+ Efficiency!

The AO3400A has a gate threshold voltage ($V_{gs(\text{th})}$) between 0.65V and 1.45V. At our ESP32-S3’s 3.3V GPIO level, it is slammed completely into full saturation with an on-resistance under $30\text{ m}\Omega$.

That meant we didn't need a heavy, expensive dedicated gate driver IC or level shifters. We can drive the gates directly from the ESP32 pins through a simple 100Ω damping resistor. That decision alone saved us ₹200 and about 2 grams of board weight per drone.

The Weight Budget Reality

We also put our foot down on battery capacity. It’s always tempting to order bigger batteries—who doesn't want 15 minutes of flight time? But with 720 coreless motors, the math is brutal:

  • Each 720 motor on 55mm props produces about 35–40g of static thrust at full throttle.
  • Four motors give us ~150g total thrust.
  • For crisp attitude recovery and snappy hover control, you want a thrust-to-weight ratio of at least 1.8:1 to 2.0:1.
  • That caps our maximum all-up weight (AUW) at 35–38 grams.

Every extra 50 mAh on a battery adds 2–3 grams of dead weight. We finalized our battery order at 380 mAh (10.5g), which hits the exact sweet spot: ~4.5 minutes of hover time while keeping total flight weight right around 32 grams.

The Orders Are In

By Friday afternoon, we had confirmed inventory and placed purchase orders across suppliers for all core electronics, spare motors, and battery packs.

Next week, we enter every hardware developer's favorite purgatory: refreshing tracking numbers and preparing the test bench for unboxing day.

Week 04

Refreshing Courier Tracking & Workbench Prep

If you've ever built physical hardware, you know the feeling: you have the designs finalized, the CAD printed, the schematics in your head, and your entire progress comes screeching to a halt because a parcel of microchips is stuck at a regional logistics hub three states away.

That was Week 4. But instead of twiddling our thumbs, we turned our workbench into an assembly line so that the moment the courier rings the doorbell, we hit the ground running.

The Supply Chain Limbo

Every morning this week started the same way: opening tracking portals, watching the progress bars, and seeing "In Transit - Delayed due to logistics sorting."

The Seeed XIAO ESP32-S3 boards, our MPU9250 IMU breakouts, and the batch of 720 coreless motors were all caught in transit across two different couriers.

Component Wishlist:  [ XIAO ESP32-S3, MPU9250, 720 Motors, AO3400A FETs ]
Current Location:    [ "Out for sorting" ... for 5 days straight ]

We briefly considered rushing out to buy generic substitute parts from local hobby stores at three times the price, but that would defeat our core goal of building a strictly budgeted ₹2,200 platform. We decided to hold our ground, wait for our verified BOM to arrive, and put the downtime to work.

Getting the Bench Ready

Nothing kills hardware momentum like realizing you're missing heatshrink, have a dirty soldering tip, or don't have a clean power supply when your parts finally arrive. We spent the week systematically prepping our build environment:

  1. Soldering Station Tune-Up: Soldering SOT-23 MOSFETs (which are smaller than a grain of rice) onto perfboard with bare copper wire requires a razor-sharp conical tip, fresh no-clean tacky flux, and 0.3mm fine solder wire. We cleaned and tinned our irons, verified temperature calibration, and set up our desktop magnifying loupe.

  2. Motor Testing Rig: We rigged up an acrylic test block with 7mm vertical motor holes and a digital scale. When the motors arrive, we won't just trust the manufacturer's thrust specs—we'll mount them to the load cell, run a sweep from 10% to 100% PWM, and measure exact gram-thrust versus battery voltage sag.

  3. Battery Charging & Storage Station: Micro LiPo cells require care. We wired up a 1S multi-port balance charging board, calibrated a digital multimeter for checking cell internal resistances, and verified that our 5V USB step-down regulators deliver clean, ripple-free power to our bench programmer.

  4. Wire Harness Preparation: We cut, stripped, and pre-tinned dozens of ultra-flexible 30 AWG silicone wire leads for the I2C bus and motor gate signals, color-coded for power (red), ground (black), signals (yellow), and I2C lines (green/blue).

Reflections from the Quiet Workbench

Hardware projects have a rhythm. There are frantic weeks where motors are screaming and hot glue is flying, and there are quiet weeks where progress is measured in clean workspaces, checked datasheets, and mental preparation.

Tracking updates show that the parcels have finally cleared the main distribution center and are scheduled for doorstep delivery early next week.

Next week, the packages arrive, the soldering irons get hot, and we begin turning this pile of silicon into a living, breathing flight controller.

Week 05

Schematics, Star Grounds & KiCad Workflows

Before you connect four brushed DC motors capable of pulling 8 Amps of combined burst current to a delicate 3.3V microcontroller, you’d better have your electrical paths figured out down to the millimeter.

This week, we booted up KiCad to design the complete electrical schematic for ResQmesh. The goal wasn’t to send a board out to a commercial PCB fab—custom fab turnaround takes weeks and adds tooling fees that would violate our sub-₹3,000 budget. Instead, we used KiCad to design a bulletproof schematic and map out an exact point-to-point perfboard wiring layout that we could hand-solder directly onto the drone frame.

WhatsApp Image 2026-09-20 at 12 00 20 AM

The Silent Killer: Motor Flyback and Ground Bounce

If you've ever hooked a DC motor directly to an Arduino and watched the board reset the second the motor spins down, you've met Back-EMF (Electromotive Force).

An electric motor is an inductor. When you turn a MOSFET ON, current builds up in the motor windings. When you turn the MOSFET OFF, the magnetic field collapses instantly, generating a massive reverse voltage spike:

$$V = -L \frac{di}{dt}$$

On a 3.7V battery, that inductive kick can easily spike to 30V or higher for a few nanoseconds. If that voltage reaches your microcontroller or the gate of your MOSFET, it punches through the gate oxide and destroys the silicon instantly.

       [ +3.7V LiPo Rail ]
                |
          +-----+-----+
          |           |
        [Motor]   [1N5819 Diode] <--- Clamps reverse flyback safely!
          |           |
          +-----+-----+
                |
              [Drain]
      GPIO ---> [Gate]  AO3400A N-Channel MOSFET
              [Source]
                |
          [Power GND] (Battery Negative)

To kill this problem before it killed our hardware, our KiCad schematic incorporated three critical safety barriers:

  1. 1N5819 Schottky Flyback Diodes: Placed antiparallel across every motor terminal. When the MOSFET shuts off, the reverse inductive kick is immediately shunted through the diode back to the positive rail, clamping the spike safely to $V_{bat} + 0.45\text{ V}$.
  2. 100Ω Series Gate Resistors: Dampens high-frequency $LC$ ringing between the ESP32 pin capacitance and the MOSFET gate trace.
  3. 10kΩ Gate Pull-Down Resistors: Microcontroller GPIO pins float in high-impedance mode for a few milliseconds during bootup or firmware flashing. Without pull-down resistors, the gates pick up stray capacitive charge and turn the motors on unpredictably while the drone is sitting on your desk. The 10kΩ resistors hold the gates firmly at 0V until the firmware actively takes control.

WhatsApp Image 2026-09-19 at 11 45 43 PM WhatsApp Image 2026-09-19 at 11 44 38 PM

The Star Grounding Architecture

The other major trap on a micro quadcopter is ground bounce.

When four coreless motors pulse at 20 kHz, several amps of current rush through the ground wire. If your IMU sensor shares that same ground trace, the resistance of the wire creates momentary voltage fluctuations ($\Delta V = I \cdot R$).

To an MPU9250 listening on a 400 kHz I2C bus, a 200mV ground bounce looks like invalid data or a bus collision. The I2C state machine hangs, the flight loop misses its timing, and the drone crashes.

WRONG (Daisy Chain):
[Battery -] ------> [Motor GND] ------> [ESP32 GND] ------> [IMU GND]  <-- NOISE!
                                            ^
                       Motor current distorts IMU ground!

CORRECT (Star Ground):
                    +----> [Motor GND Returns] (Thick power trace)
                    |
[Battery Negative] -+----> [ESP32 Digital Ground]
                    |
                    +----> [IMU Ground] (Quiet, isolated analog rail)

We routed our schematic using strict Star Grounding: all high-current motor source returns converge at one single physical point—the negative battery solder pad. The ESP32 logic ground and IMU sensor ground branch off independently from that quiet origin point.

Battery Sensing & Bulk Decoupling

To keep our LiPo from dropping below the danger threshold (3.2V under load), we added a simple 2:1 resistive divider:

  • Two precision 100kΩ / 100kΩ resistors step down the maximum 4.2V battery voltage to 2.1V.
  • This feeds directly into the XIAO ESP32-S3’s ADC pin (D0 / GPIO1), allowing our flight firmware to monitor battery health in real time without exceeding the 3.3V ADC limit.

Finally, we placed a 470µF low-ESR bulk electrolytic capacitor directly across the main battery pads to absorb sudden current surges during punch-outs, accompanied by 100nF ceramic decoupling caps soldered directly across each motor tab to choke high-frequency brush arcing noise.

Ready for the Iron

With our schematic validated through KiCad’s Electrical Rules Check (ERC) and our point-to-point perfboard footprint planned out, the circuit blueprint is set in stone.

Next week: the final component packages clear delivery, and we begin hand-wiring this entire circuit with tweezers, flux, and silicone wire.

Week 06

The Anatomy of a Hand-Wired Micro Flight Controller

Designing a schematic on a computer screen is clean and theoretical. Everything is a neat orthogonal line, nets connect with a click, and wires never accidentally touch each other.

Building a physical flight controller by hand on a 25mm × 25mm perfboard slice is an entirely different beast. At this scale, stray capacitance, wire routing paths, and millimeter-long solder bridges can make the difference between a rock-solid hover and a smoky microcontroller.

With the final courier delivery scheduled for next week, we dedicated Week 6 to turning our KiCad schematic into a meticulous 3D wiring blueprint.

The Geometry of a SOT-23 on Perfboard

The most challenging component in our circuit is the AO3400A MOSFET. It comes in an ultra-compact surface-mount SOT-23 package—designed for machine pick-and-place on manufactured PCBs, not for human hands holding a soldering iron.

       SOT-23 Pinout (Top View):
              +---+---+
        Gate  | 1   3 |  Drain (To Motor Negative)
              |       |
      Source  | 2     |
              +-------+
                 |
         (To Power GND)

Standard perfboard has a 2.54mm (0.1-inch) hole pitch. SOT-23 pins are spaced at a minuscule 0.95mm. If you try to jam a SOT-23 flat onto standard perfboard holes, the legs don't reach the pads, and any excess solder will bridge Gate to Source.

  1. We mapped out a custom "dead-bug / bridge" mounting technique: bending the Drain lead upward to connect directly to the flyback diode cathode, while Source solders flat to a shared solid-copper ground bus wire running along the bottom.
  2. We placed the 100Ω gate resistor directly across the Gate pin before attaching any flexible wire, acting as a physical bridge and dampening high-frequency reflections before they travel down the signal line.

Combating EMI: Twisting Wires Like Network Cables

Brushed coreless motors are essentially tiny mechanical spark generators. As the internal commutator brushes sweep across the armature segments at 50,000 RPM, they create continuous micro-arcing. That arcing broadcasts high-frequency electromagnetic interference (EMI) into the air.

If you run parallel, straight wires from your motors right past your MPU9250 sensor, those wires act as miniature antennas. They radiate RF hash straight into the 3.3V power rails and induce false spikes on the I2C Clock (SCL) and Data (SDA) lines.

Parallel Motor Wires:  =================  --> Radiates EMI directly into sensor lines!
Twisted Motor Pairs:   -X-X-X-X-X-X-X-X-  --> Magnetic fields cancel out! Low EMI.
  • Tightly Twisted Pairs: Every motor's positive and negative power leads must be twisted together with at least 4 turns per centimeter before running inward to the central driver board. The opposing currents create equal and opposite magnetic fields that cancel each other out.
  • Physical Separation: Motor power lines are routed along the bottom arms of the 3D-printed frame, while the MPU9250 I2C signals run along the top deck, maintaining an air gap between high-current power switching and low-voltage logic.

The Complete Pin Mapping

By the end of the week, our physical wiring harness was fully mapped to the Seeed Studio XIAO ESP32-S3:

XIAO PinGPIOFunctionConnection Details
D0GPIO1Battery Voltage ADC2:1 divider (100kΩ / 100kΩ) sensing 1S LiPo voltage
D1GPIO2Motor 1 PWM (Front-Left)20 kHz LEDC PWM output -> 100Ω gate resistor -> AO3400A Gate
D2GPIO3Motor 2 PWM (Front-Right)20 kHz LEDC PWM output -> 100Ω gate resistor -> AO3400A Gate
D3GPIO4Motor 3 PWM (Rear-Right)20 kHz LEDC PWM output -> 100Ω gate resistor -> AO3400A Gate
D4GPIO5I2C SDAFast-Mode 400 kHz data line to MPU9250 IMU
D5GPIO6I2C SCLFast-Mode 400 kHz clock line to MPU9250 IMU
D6GPIO43Motor 4 PWM (Rear-Left)20 kHz LEDC PWM output -> 100Ω gate resistor -> AO3400A Gate
3V3Regulated 3.3V OutDedicated quiet power rail to MPU9250 IMU
GNDLogic GroundStar-ground tie point to battery negative terminal

WhatsApp Image 2026-09-20 at 9 37 58 AM 20260916_021730

https://github.com/user-attachments/assets/fb0bdad3-6bdf-4919-a269-c35ca3fdb0d8

On Deck

The blueprint is ready, the routing paths are marked, and courier tracking confirmed the components are in the local delivery van for early Monday morning.

Next week, we write and validate our entire 500 Hz flight loop on a PC simulator before flashing the actual hardware.

Week 07

Flying Inside the Terminal: The Host Simulator

There is an old, painful rule in drone development: if your flight controller has a bug, it will explain that bug to you by crashing into your forehead at 50,000 RPM.

Spinning untested PID code on raw hardware with spinning props is a recipe for snapped plastic, fried motors, and bruised fingers. So this week, before touching physical components, we built a complete C++ desktop flight dynamics simulator (simulate_flight.cpp) and automated test runner (run_all_tests.cpp) to fly the drone inside our computer terminal.

Simulating Physics at 500 Hz

Our flight controller runs at a deterministic 500 Hz—a new sensor read, attitude calculation, and motor command every 2,000 microseconds ($2.0\text{ ms}$).

To simulate this on a PC without needing an ESP32 attached, we built lightweight hardware abstraction hooks into our core flight headers (imu.h, receiver.h, motors.h). The math doesn’t care whether an angular rate reading comes from a physical MPU9250 silicon wafer over I2C or from a virtual rigid-body physics integrator running in a C++ for loop.

       [ Simulated Pilot Stick Input ]      [ Simulated Wind Gust / Disturbance ]
                       \                                  /
                        v                                v
                 +-----------------------------------------------+
                 |        Cascaded Dual-Loop PID Controller      |
                 |      (Outer Angle Loop -> Inner Rate Loop)    |
                 +-----------------------------------------------+
                                         |
                                         v
                 +-----------------------------------------------+
                 |       Quad-X Anti-Saturation Mixer Math       |
                 +-----------------------------------------------+
                                         |
                                         v
                 +-----------------------------------------------+
                 |    Simulated Quadcopter Rigid Body Dynamics   |
                 |     (Moments of Inertia: Ixx, Iyy, Drag, dt)  |
                 +-----------------------------------------------+
  • Quad-X motor thrust geometry and torque reactions ($M_1..M_4$).
  • Physical moments of inertia along Roll, Pitch, and Yaw axes.
  • Aerodynamic drag and gravitational acceleration ($9.81\text{ m/s}^2$).
  • Ground effect and sensor noise.

1. Bootup & Disarm Lockout

We simulated the first 500 milliseconds of power-on. Confirmed that motor PWM values remain strictly at 0, and the system refuses to accept pilot commands until a clean disarm state is confirmed.

2. The Accidental Flip Arming Trap

What happens if the pilot tries to arm the drone while it's upside down or in someone's hand? We set the simulated pitch to $+30^\circ$ and injected an arming command. The safety watchdog caught it instantly: arming was rejected because vehicle tilt exceeded our $25^\circ$ safety gate.

3. Steady-State Level Hover

At 40% throttle (400 PWM), the drone lifted smoothly off the virtual floor, auto-stabilized roll and pitch to $<0.2^\circ$, and held all four motor outputs within 2 PWM units of each other.

4. The 15° Wind Shear Punch

While hovering at $0^\circ$ pitch, we suddenly injected a violent $+15^\circ$ nose-up aerodynamic disturbance. Within 120 milliseconds, the cascaded PID controller clamped down: front motors ($M_1, M_2$) spooled up, rear motors ($M_3, M_4$) dialed back, and the vehicle returned to perfectly level flight with zero overshoot.

5. High-Bank Slalom Rolls

We commanded sharp $\pm20^\circ$ roll steps. The inner angular rate loop ($K_p = 1.8, K_d = 0.04$) provided snappy differential torque response without ringing or sluggishness.

6. The 95% Throttle Punch & The Saturation Discovery

This was our biggest simulation breakthrough. When a pilot punches collective throttle to 95% (970 PWM) and simultaneously commands a hard roll to the right, standard mixer math does this:

$$M_1 = \text{Throttle} + \text{Roll} = 970 + 150 = 1120$$

Because 10-bit PWM caps out at 1023, $M_1$ and $M_4$ clip at maximum power. With both left motors pegged at 1023, the flight controller has no headroom left to create differential thrust. The drone loses all roll control and flips uncontrollably!

WITHOUT Dynamic Anti-Saturation:
Motor 1: [==================== CLIP! 1023 ]  --> Lost attitude authority!
Motor 2: [==================== CLIP! 1023 ]

WITH Dynamic Anti-Saturation:
Excess Demand: 1120 - 1023 = 97 PWM
Dynamically subtract 97 from Collective Throttle!
Motor 1: [==================== 1023 ]  --> Attitude authority preserved!
Motor 2: [============== 726 ]       --> Differential torque maintained!

To fix this, we implemented Dynamic Priority Anti-Saturation: when any motor output exceeds 1023, the mixer calculates the excess demand and subtracts it equally from the collective throttle. The drone dips slightly in altitude for a split second, but maintains 100% attitude stabilization.

7. The Radio Dropout Failsafe

At $t = 3.5\text{s}$, we cut the simulated radio packet feed. After exactly 200 ms with no heartbeat, the watchdog timer tripped: the state machine transitioned instantly to FAILSAFE, cutting all four motors to 0 PWM to prevent a runaway flyaway.

41 Unit Tests Passed

We wrapped our algorithms in a standalone C++ unit test suite covering:

  • Cascaded PID anti-windup clamping limits.
  • Complementary attitude filter fusion accuracy ($\alpha = 0.98$).
  • CRC16 packet checksum generation and corruption rejection.
  • ADC battery moving-average digital filtering.

All 41 assertions passed with zero memory leaks and an average loop execution time of under $450\ \mu\text{s}$—leaving massive headroom inside our $2000\ \mu\text{s}$ flight window.

Next week: the physical components are finally here. We unbox the silicon, verify the MPU9250 sensor over real I2C, and see how our theoretical math translates to physical reality.

Week 08

Unboxing, Hard Trade-Offs & First Gyro Life

Monday morning felt like Christmas: three courier boxes sat on the workshop bench. Inside were our Seeed Studio XIAO ESP32-S3 boards, a tray of MPU9250 9-axis IMUs, bags of 720 coreless motors, 55mm propellers, and reels of AO3400A MOSFETs.

We immediately checked the 1S LiPo cells with a digital multimeter: 3.82V per cell, right on nominal storage voltage. The build was officially underway.

The Brutal Reality Check: Killing Onboard LoRa

Before we heated up the soldering iron, we had to make a tough engineering decision that we had been dreading for two weeks.

Our original concept proposal had included long-range LoRa telemetry on every drone. We loved the idea: kilometer-range swarm command links operating on 868/915 MHz. But holding the physical hardware in our hands shattered that fantasy.

Expected:  Tiny ESP32-S3 with built-in LoRa module (<1g extra)
Reality:   No such micro board exists in stock. 
           External SX1262 LoRa Breakout = 6.8 grams + bulky antenna!

On a 500g cinematic drone, 7 grams is nothing. On a micro-quadcopter whose total bare airframe weighs 7.4 grams and whose motors can only lift 50g comfortably, a 7-gram breakout module is an anchor. It would have reduced our flight time to under 90 seconds and ruined vehicle agility.

We made the executive call: cut onboard LoRa from the micro flight nodes.

Instead, we pivoted 100% of our swarm communication architecture to native ESP-NOW:

  • Built directly into the ESP32-S3 silicon (zero extra grams).
  • Sub-5 millisecond packet latency (over 10x faster than LoRa).
  • Native peer-to-peer mesh broadcasting between nodes without needing external routers.
  • LoRa isn't dead—it's being shifted to dedicated ground relay stations where weight doesn't matter.

Wiring the IMU and the 400 kHz I2C Mystery

With that weight off our shoulders, we wired our first MPU9250 IMU breakout to the Seeed Studio XIAO ESP32-S3:

  • SDA -> GPIO5 / D4
  • SCL -> GPIO6 / D5
  • VCC -> 3.3V
  • GND -> Common GND

We flashed a quick I2C scanner script. Within two seconds, the serial monitor printed:

Scanning I2C bus at 400 kHz...
Found device at address 0x68 (MPU9250 / MPU6050)
WHO_AM_I register check: 0x71 -> SUCCESS!

Then came the bug.

The sensor would stream live gyro and accelerometer values for 15 seconds, and then the I2C bus would abruptly freeze solid. Resetting the ESP32 would sometimes bring it back, but touching the wires would cause it to lock up again.

ESP32 Internal Pull-ups (~45kΩ):  Weak! Slow rise time on 400 kHz clock --> I2C Bus Freeze!
Added External 4.7kΩ Pull-ups:   Sharp, crisp square waves --> Rock-solid stability!

We hooked up our logic analyzer and looked at the SCL clock line. Because the ESP32’s internal software pull-up resistors are weak (~45kΩ), the signal edges at 400 kHz were rounded into soft ramps instead of sharp square waves. Parasitic wire capacitance was stretching the clock transitions until the IMU missed an ACK bit.

We soldered two 4.7kΩ external pull-up resistors directly between the SDA/SCL lines and the 3.3V rail. The waveform snapped into razor-sharp square edges, and the bus ran for 4 hours straight without dropping a single frame.

First Attitude Tracking in 3D

With the I2C bus stabilized, we flashed our complementary filter and gyro calibration routine:

  1. Stationary Bias Sweep: On bootup, the drone sits flat on the desk for 1.0 second, collecting 500 consecutive gyro samples. It averages out the earth's rotation and thermal bias, locking sensor zero-drift to under $0.08^\circ/\text{s}$.

  2. Complementary Fusion:

    $$\text{Angle} = 0.98 \times (\text{Angle} + \omega \cdot dt) + 0.02 \times \text{AccelAngle}$$

We held the tiny board in our fingers and tilted it forward, backward, left, and right. On our laptop screen, the serial plotter tracked the roll and pitch angles with zero lag, snapping back to dead-level $0.0^\circ$ every time we set it down on the table.

Next Up: The Power Stage

Next week, we move to the high-power side of the board: soldering four AO3400A MOSFETs, 1N5819 flyback diodes, and spinning our 720 coreless motors for the very first time.

Week 09

SOT-23 Tweezers, Solder Fumes & 20 kHz Ultrasonic Silence

If you want to test your soldering sanity, try hand-soldering four surface-mount SOT-23 MOSFETs—each about the size of a sesame seed—onto a perfboard scrap using tweezers and a magnifying glass while breathing through a fume extractor.

This week was the trial by fire: building the high-current motor driver stage, fighting acoustic motor whine, and spinning all four 720 coreless motors for the very first time.

Surgery with SOT-23s

To keep our flight controller as light as possible, we opted against bulky breakout boards. We hand-wired the AO3400A MOSFETs directly on a 20mm × 20mm perfboard section:

        +-----------------------------------------------+
        |  [M1 FET]   [M2 FET]   [M3 FET]   [M4 FET]   |
        |   (FL CW)   (FR CCW)   (RR CW)    (RL CCW)    |
        |                                               |
        |  1N5819 Schottky Clamping Diodes Across Each  |
        |  100Ω Gate Series + 10kΩ Gate Pull-Downs      |
        |  470µF Low-ESR Bulk Rail Capacitor            |
        +-----------------------------------------------+

20260915_001343 20260915_001017 20260916_021638

  1. We bent the Source pins of all four FETs downward and soldered them to a thick, solid-copper bare ground rail running along the perimeter (our Star Ground).
  2. We soldered tiny 100Ω resistors directly to the floating Gate pins, connecting them via 32 AWG flexible enamel wires back to the XIAO ESP32-S3's PWM pins (D1, D2, D3, D6).
  3. We tacked 1N5819 Schottky diodes across the motor output pads, double-checking cathode band orientations to avoid shorting the battery directly to ground.
  4. We bridged a 470µF low-ESR electrolytic capacitor directly across the main LiPo input pads to swallow transient voltage dips during aggressive throttle punches.

Before plugging in a battery, we spent thirty minutes with our multimeter on continuity mode, probing every single adjacent trace. Zero shorts.

WhatsApp Image 2026-09-20 at 9 05 32 AM WhatsApp Image 2026-09-20 at 10 27 49 AM WhatsApp Image 2026-09-20 at 10 27 48 AM

The Ear-Ringing 1 kHz Mosquito Whine

We wrote a minimal PWM test script to spin Motor 1 at 25% duty cycle. We plugged in the 1S LiPo, sent the test command, and were immediately greeted by an ear-splitting, piercing squeal that sounded like an angry mosquito directly inside our eardrums.

At standard microcontroller PWM frequencies (1 kHz to 4 kHz), the rapid pulsing of current through the motor windings causes the motor casing and armature coils to physically vibrate at audio frequencies. It turns the motors into tiny mechanical loudspeakers.

20260916_021717 20260916_021621 20260916_021721

https://github.com/user-attachments/assets/4e7536c5-63f1-473d-8210-16360614ab90

Standard PWM (1 kHz - 4 kHz):    [EEEEEEEEEEEEEEEE!] --> Painful acoustic resonance!
Ultrasonic PWM (20 kHz):         [Dead Silence...]    --> Above human hearing range!

We went straight into the ESP-IDF LEDC PWM peripheral configuration and reconfigured our timer:

  • Frequency: Bumped from 2 kHz straight to 20 kHz.
  • Resolution: Set to 10-bit (0 to 1023 duty steps).

We hit enter.

The squeal vanished completely. In its place was eerie, dead silence—just the quiet whoosh of air as the 55mm propeller spun up smoothly on the test stand. 20 kHz is safely above the human hearing limit (~18–19 kHz for adults), giving us silky-smooth torque delivery without the deafening whine.

Measuring Real Efficiency: 45 Millivolts

Once the motors were spinning quietly, we brought out the oscilloscope and multimeter to verify our MOSFET saturation math from Week 3.

At full 100% throttle, a 720 coreless motor draws approximately 1.5 Amps of continuous current. We placed our probes across the Drain and Source pins of the AO3400A:

$$V_{ds} \approx 45\text{ mV} \quad (0.045\text{ V})$$

Using Ohm's law, we calculated the real-world in-circuit on-resistance:

$$R_{ds(\text{on})} = \frac{V_{ds}}{I} = \frac{0.045\text{ V}}{1.5\text{ A}} = 0.030\ \Omega \quad (30\text{ m}\Omega)$$

$$P_{\text{loss}} = I^2 \cdot R = (1.5\text{ A})^2 \times 0.030\ \Omega = \mathbf{0.067\text{ Watts}}$$

At less than 70 milliwatts of heat loss, the MOSFETs stayed completely cold to the touch even after 3 minutes of continuous full-throttle bench testing. That means $>98%$ of our battery energy is going straight to the propellers, not wasted as heat in the driver stage.

Checking Quad-X Rotation Directions

Finally, we used our custom interactive USB serial CLI to spin each motor individually and verify rotation directions against our Quad-X mixer geometry:

    M1 (Front-Left, CW)       M2 (Front-Right, CCW)
            \                       /
             \                     /
              +-------------------+
              |     ResQmesh      |
              +-------------------+
             /                     \
            /                       \
    M4 (Rear-Left, CCW)       M3 (Rear-Right, CW)
  • test_motor 1 5 -> Front-Left spun clockwise (CW).
  • test_motor 2 5 -> Front-Right spun counter-clockwise (CCW).
  • test_motor 3 5 -> Rear-Right spun clockwise (CW).
  • test_motor 4 5 -> Rear-Left spun counter-clockwise (CCW).

Every single channel responded with zero jitter. The hardware is built, tested, and electrically verified.

Next week, we flash our complete dual-core FreeRTOS flight firmware, mount the electronics to the frame, and see if our drone can stabilize itself in the physical world.

Week 10

Breathing Life into the Swarm: Dual-Core FreeRTOS Firmware

Ten weeks ago, this project was just numbers on a whiteboard: a crazy idea to build a collaborative swarm drone for under ₹3,000.

This week, all the threads converged. We assembled the 7.4g unibody frame, mounted the custom MOSFET driver board, wired the Seeed Studio XIAO ESP32-S3, and flashed our ground-up C++ flight control and swarm networking firmware.

Why We Wrote Our Own Flight Stack

Early on, several people asked us: "Why not just flash Betaflight or ArduPilot and call it a day?"

The answer comes down to what a swarm actually is. Off-the-shelf flight stacks are designed for single acrobatic FPV quads or heavy GPS autonomous drones running on STM32 microcontrollers. They expect dedicated radio receivers (CRSF/ELRS) feeding UART ports, and their monolithic scheduling loops don't play nicely with ESP32 Wi-Fi/RF radios.

To make a true swarm drone, we needed:

  1. Direct Dual-Core Control: The ESP32-S3 has two 240 MHz Xtensa LX7 cores. We wanted zero jitter on our flight stabilization loop, which meant dedicating an entire core exclusively to flight physics.
  2. Native ESP-NOW Swarm Protocol: We needed lightweight, peer-to-peer packet broadcasting between drones at sub-5ms latency, without the overhead of heavy IP/TCP network stacks.
  3. Tiny Binary Footprint: Our entire compiled firmware flashes in under 4 seconds over USB-C, leaving huge flash headroom for future sensor drivers and autonomous swarm behaviors.
+-------------------------------------------------------------------------+
|                       ESP32-S3 Dual-Core Architecture                   |
+------------------------------------+------------------------------------+
|               CORE 0               |               CORE 1               |
|       (Asynchronous Swarm & IO)    |     (Deterministic 500 Hz Flight)  |
+------------------------------------+------------------------------------+
| • ESP-NOW Swarm Mesh Radio         | • 500 Hz Timer Interrupt (2000 µs) |
| • CRC16 Packet Validation          | • MPU9250 Fast I2C Burst Read      |
| • 200 ms Failsafe Watchdog         | • Complementary Attitude Filter    |
| • USB Interactive Serial CLI       | • Cascaded Angle + Rate PID Loop   |
| • Battery Voltage ADC Monitoring   | • Anti-Saturation Quad-X Mixer     |
| • Telemetry Logging                | • 20 kHz Ultrasonic LEDC PWM Out   |
+------------------------------------+------------------------------------+
                   \                                      /
                    +----> FreeRTOS Shared Memory <------+

The 500 Hz Deterministic Loop (Core 1)

On Core 1, our flight loop runs strictly every 2,000 microseconds ($2.0\text{ ms}$):

  1. IMU Burst Read (14 Bytes): Grabs 3-axis gyro and 3-axis accelerometer registers in a single contiguous I2C transaction at 400 kHz (~35 µs).
  2. Attitude Fusion: Merges gyro rates and accelerometer angles through our complementary filter to track vehicle Roll and Pitch.
  3. Cascaded Dual-Loop PID:
    • Outer Angle Loop: Takes pilot/swarm tilt demands ($\pm25^\circ$) and calculates desired angular rates ($\text{deg/s}$).
    • Inner Rate Loop: Computes instantaneous motor torque corrections, filtered through a low-pass D-filter ($\tau = 0.005\text{s}$) to kill vibration noise.
  4. Anti-Saturation Mixer: Combines Throttle, Roll, Pitch, and Yaw into 4 motor duties. If full throttle tries to clip any motor past 1023, it dynamically scales down collective throttle, ensuring the drone never loses attitude authority in an aggressive maneuver.
  5. Hardware PWM Output: Writes 10-bit duty cycles to the 20 kHz LEDC timers.

We measured our total loop execution time on an oscilloscope GPIO toggle: $420\ \mu\text{s}$ out of our $2000\ \mu\text{s}$ budget. That leaves over 75% idle CPU headroom on Core 1!

Swarm Comms & The 200 ms Dead-Man Switch (Core 0)

While Core 1 is hyper-focused on balancing the drone, Core 0 handles the outside world via ESP-NOW:

  • Swarm Packet Framing: Every packet is packed into a compact binary structure containing a target Node ID (0x01 through 0xFF), a rolling packet sequence number, 4 joystick axes, and a CRC16 checksum. If RF noise corrupts a single bit in the air, the checksum fails and the corrupted packet is discarded in microseconds.
  • The 200 ms Failsafe Watchdog: If an individual drone drops out of radio range or an interfering signal blocks communication for more than 200 milliseconds, an independent watchdog state machine trips. It instantly overrides the mixer, cuts all four motor duties to 0, and transitions the drone to DISARMED. No rogue flyaways, no runaway drones.
  • Interactive USB CLI: You can plug the drone into a laptop, open a serial terminal at 115200 baud, and run live diagnostic commands (status, imu, motors, test_motor) without interrupting the 500 Hz flight loop on the other core.

The Hand Test: It Fights Back!

With everything flashed, we held the assembled quadcopter loosely between two fingers, armed the flight controller, and brought the throttle up to 25%.

The four 55mm propellers spun into a quiet, smooth blur.

Then we tried to tilt the drone forward with our fingers.

Instantly, the front two motors roared to life with increased RPM while the rear two backed off, delivering a distinct, firm gyroscopic counter-torque right into our fingertips. When we tilted it left, the left motors surged to push it level. It felt alive—actively resisting our hand, fighting with everything it had to hold a perfectly flat, level plane in 3D space.

Pitch Nose Down by Hand:  [ Front Motors Spool Up! Rear Motors Cut Back! ]
Roll Left Wing by Hand:   [ Left Motors Surge! Right Motors Back Off! ]
Release to Neutral:       [ All Four Motors Settle into Smooth Equilibrium ]

https://github.com/user-attachments/assets/9f020abd-1319-4267-80d9-c85300ebb805

Ten weeks of late nights, broken prints, courier delays, schematic revisions, and microscopic soldering had brought us to this moment. The hardware works, the electrical design is $>98%$ efficient, the flight loop is rock-solid at 500 Hz, and the entire drone weighs 32.2 grams on a total build cost of ₹2,224.

What Comes Next

With the core flight platform fully operational, the foundation for ResQmesh is built:

  1. Indoor Tethered Hover Flights: Fine-tuning PID gains ($K_p, K_i, K_d$) in free air with safety tethers.
  2. Multi-Node Swarm Pairing: Flashing three identical units and broadcasting synchronized throttle and formation vectors via ESP-NOW.
  3. Sensor Modules: Snapping on the VL53L1X Time-of-Flight sensor for ground-distance altitude hold and obstacle avoidance.

The swarm has taken its first breath. Now, it's time to fly.