Jakub Szypicyn

Engineering for electric mobility and energy storage — from the motor controller's first boot to the app in your pocket.

Jakub Szypicyn

Now
Head of Connected Systems, Skarper
Previously
Minimal, Breathe Battery Technologies, Skarper, WeCorp Industries
Education
PhD, Imperial College London

About (draft)

Jakub Szypicyn, smiling, in a white shirt and striped tie

I like products that move. For seven years I've taken e-bikes, battery labs and electric cargo vehicles from first boot to fleets in the field.

I design the boards, write the firmware, and build the pipelines that keep machines updated long after they leave the factory.

Before industry, a PhD at Imperial on reconfigurable analogue circuits. Away from the desk: cycling and touring, Formula 1, photography.

Journey

Five machines,
all electric.

01 — Drones

2020 · WeCorp Industries, London

EEE Research Technical Lead

Built an STM32 drone-propeller test rig with C++ firmware and a Python DAQ GUI, and a field-oriented motor controller that cut power losses by 5%. Designed the mechatronic prototypes, harnesses and sensor instrumentation around it, and was made research technical lead within the year.

STM32 / C++ / FOC / Python DAQ / Mechatronics

Propeller test rig

02 — E-bikes

2021 – 2024 · Skarper, London

Senior Electronics Engineer

Second hire, leading a team of three from concept to mass production: 2,000+ units shipped. Architected the 48V drive system (motor controller, VCU, UI and BMS) and wrote the first firmware for every board, from bare-metal bring-up to a FreeRTOS system controller with BLE, CAN to the BMS and EN 15194 assist limits. Cut the production PCB BOM by more than half, built the production test jigs (~95% functional coverage) and owned release sign-off through launch.

48V drives / FreeRTOS / BLE / CAN / EN 15194 / MISRA C / Production test

The Skarper drive unit: a grey body with a round disc carrying a lens, status lights and a button
Skarper drive unit

The problem

Assistance follows the road gradient, read from an IMU in the Skarper unit. The accelerometer only sees gravity and the bike’s speed changes, and when the bike leans into a corner part of the gravity reading moves onto the sensor’s lat axis. The up share shrinks, so a naive estimate reads every climb steeper than it is. roadGradient measures the lean, rolls the reading back into the bike’s plane with rodrigues(fwd, -lean), and returns the slope alone. All that while compensating for speed and vibration artefacts.

Why leaning corrupts a gradient reading.

A bike on an 8 degree climb leans into a corner. The accelerometer reading, accel, stays vertical while the bike's up and lat axes roll with the lean, so part of the reading lands on lat and the gradient reads steeper than it is. Rolling the reading back by the lean, R(minus lean) times accel, returns it to the bike's plane and recovers the true 8 degrees.

At 34° of lean the gradient reads 9.7° naively, 8.0° after R(−lean).

The fix

// Rotation by angle a about unit axis k: R = cos(a) I + sin(a) [k]x + (1 - cos(a)) k k^T
static mat3_t rodrigues(const vec3_t k, const float32_t a)
{
    assert_param(fabsf(vec3_dot(k, k) - 1.0f) < 1e-3f);  // axis must be a unit vector
    assert_param(isfinite(a));

    const float32_t c = cosf(a), s = sinf(a), t = 1.0f - c;
    return (mat3_t){ .m = {
        { c + t * k.x * k.x,       t * k.x * k.y - s * k.z, t * k.x * k.z + s * k.y },
        { t * k.y * k.x + s * k.z, c + t * k.y * k.y,       t * k.y * k.z - s * k.x },
        { t * k.z * k.x - s * k.y, t * k.z * k.y + s * k.x, c + t * k.z * k.z       },
    } };
}

// Road gradient (rad) from an accelerometer sample (g) and wheel acceleration (m/s^2)
float32_t roadGradient(const vec3_t accel, const float32_t wheelAccel)
{
    // A NaN would poison every filter downstream; out-of-range values are clamped below
    assert_param(isfinite(accel.x) && isfinite(accel.y) && isfinite(accel.z));
    assert_param(isfinite(wheelAccel));
    assert_param(fabsf(Mount.pitch) < (float32_t) M_PI_4);  // calibration sanity

    // The IMU is mounted pitched, so the bike's forward and up axes are not the sensor's
    const float32_t cp = cosf(Mount.pitch), sp = sinf(Mount.pitch);
    const vec3_t fwd = {  cp, sp, 0.0f };
    const vec3_t up  = { -sp, cp, 0.0f };

    // Leaning in a corner reads as slope: measure the lean, roll gravity back upright
    const float32_t lean = atan2f(accel.z, vec3_dot(accel, up));
    const vec3_t    g    = mat3_mul_vec3(rodrigues(fwd, -lean), accel);

    // Remove the bike's own acceleration and clamp what no road can produce
    const float32_t along = constrainf32(vec3_dot(g, fwd) - wheelAccel / G_MPS2, -3.0f, 3.0f);
    const float32_t above = constrainf32(vec3_dot(g, up), 0.0f, 2.0f);

    return atan2f(along, above);
}
Lean-compensated road gradient (C)

03 — Batteries

2024 – 2025 · Breathe Battery Technologies, London

Embedded Firmware & Data Engineer

Designed an Ethernet/TFTP bootloader for zero-touch updates across the lab’s STM32 assets. Built the GitLab CI/CD and Python microservices that automate battery testing, co-designed the SQL schema and REST layer behind them, and took gRPC services into production on Azure. Modelled a battery pack in Simscape to validate BMS control in the battery-in-the-loop harness.

Bootloaders / GitLab CI / Python / SQL / gRPC / Azure / Simscape

An aisle of battery cyclers and test cabinets in the battery lab, with orange cabling overhead
The battery cycler fleet

How it works

Zero-touch updates for the battery lab’s STM32 controllers: no programmer cable, no visit to the rig. An AT command over UART asks the running application to hand over to the bootloader, which brings Ethernet up, takes an address over DHCP and accepts the image from any standard TFTP client on UDP :69. Every 512-byte block is programmed, read back and checked against its CRC32. Built on ST’s lwIP in-application-programming example, extended with the AT interface, boot flags shared with the application, and per-block verification.

Firmware update over Ethernet with a TFTP bootloaderA lab PC tells the running application, over UART, to reboot into the bootloader. The bootloader sees the request flag, brings Ethernet up and takes a DHCP address, then accepts a TFTP write request on UDP port 69. It erases the application sectors, and for every 512-byte block programs it, reads it back and compares CRC32 before acknowledging. A lost block is resent after a timeout. After the short final block it rewrites the boot flags, resets, finds a valid application and jumps to it.Lab PCserial · TFTP client> AT+ENTER_BOOTLOADER?OK… board resets into bootloader> AT+APP_OR_BOOT?BOOT> AT+IP?192.168.0.42$ tftp 192.168.0.42tftp> put app.binWRQ app.bin · ACK 0DATA 1 512 B · ACK 1DATA 2 512 B · ACK 2DATA 3 lost on the wiretimeout · resend DATA 3DATA 3 512 B · ACK 3… 620 more blocks …DATA 624 312 B · ACK 624Sent 319,288 bytes> AT+RESET?… new application runningUART · AT commands · 115200 8N1Ethernet · UDP→ :69ATcommandDHCPleaseWRQwrite requestDATA512 B blockACKacknowledgetime-lapseSTM32F427APPIP192.168.0.42TFTPlistening on UDP :69running new applicationFlash · 1 MB0x0806_DF38bootapplication · sectors 5–10flagsREQEXECEach blockDATAprogram ×128read backCRC32Block624 / 624Written319,288 B · CRC32 ok1 · Request2 · Reboot3 · Network4 · Erase5 · Stream & verify6 · Swap & runThe flags are rewritten, the board resets, finds a valid stack pointer and jumps to the new application.
Ethernet bootloader, one update end to end. Addresses and sizes are illustrative.

04 — Four-wheel cargo

2025 – 2026 · Minimal, London

Senior Embedded Firmware & Data Engineer

Firmware and fleet software for an electric cargo vehicle: STM32 body-control, sound, immobiliser and DC-DC ECUs, VESC motor-control work, and a Raspberry Pi telematics stack on balenaOS. Built the CAN bootloader and signed over-the-air updates for 30+ connected vehicles, plus the CI that took builds from half a day to under ten minutes and releases from monthly to weekly. Refactored the core ECUs to MISRA C with GoogleTest, cutting static-analysis findings by 90%, and mentored two engineers.

STM32 / CAN / Fleet OTA / balenaOS / MISRA C / EN ISO 13849-1

Cargo fleet
[Code snippet]

05 — Back on e-bikes

2026 – Now · Skarper, London

Head of Connected Systems

Rejoined to lead embedded engineering and the connected product. Building one platform for manufacturing, users, the app and servicing on Supabase, Cloudflare and Expo, with secure OTA (MCUboot, signed A/B images, BLE flashing), product cybersecurity and the EU Battery Passport. Also running the electronics cost-down and a new BLE handlebar controller, with AI agents built into how the team engineers.

MCUboot / BLE GATT / Supabase / Expo / EU Battery Passport / AI agents

Today
[Code snippet]

Stack

Silicon to cloud.

  • 08AIClaude Code, agent and skill design, MCP servers, the Anthropic API, AI review on pull requests
  • 07App & cloudSupabase (PostgreSQL, row-level security, Auth with MFA, Deno edge functions), React Native and Expo / EAS, React and Vite, MapLibre, AWS IoT Core, IoT Jobs, S3 and CodeArtifact, Azure, Cloudflare, FastAPI
  • 06Edge & dataRaspberry Pi gateways in Python, MQTT, cellular and GNSS over ModemManager, balenaOS fleets, Protobuf, gRPC, pandas, NumPy, Plotly, Streamlit, Simscape
  • 05OTA & releaseMCUboot with signed A/B images and rollback, Ed25519-signed CAN flashing, CAN and TFTP bootloaders, BLE firmware update, fleet manifests to AWS IoT Jobs, GitHub Actions, GitLab CI
  • 04FirmwareC and C++, STM32 (F4, G0, L4, WB55), Nordic nRF52, FreeRTOS, ChibiOS, bare metal, VESC and LispBM, FOC, PID and Kalman estimation, CMake, PlatformIO
  • 03Test & qualityGoogleTest, Ceedling with Unity and CMock, pytest, pgTAP, Jest, Renode and Robot Framework emulation, cppcheck and PC-lint Plus, gcov
  • 02InterfacesCAN and CAN FD, DBC tooling, UDS over ISO-TP, XCP, SocketCAN, BLE GATT as peripheral and central, NFC (ISO 14443A), SPI, I2C, I2S, UART, Ethernet
  • 01HardwarePCB design and bring-up, 48V motor drives, BMS, DC-DC conversion, IMUs and encoders, production test and flashing rigs
  • ComplianceStandardsEN 15194, EN ISO 13849-1, EN 17860, MISRA C:2012 and 2023, IEC 60730 Class B, EU Cyber Resilience Act, UK PSTI, EU Battery Passport, product cybersecurity

AI

AI in the loop.

AI agents are part of how I engineer: each with one job and only the tools that job needs, arguing over the decisions that are expensive to undo, and never trusted with the last word.

  • 01Agents with rolesArchitect, reviewer, database, research and red-team agents, each fenced to its own tools. The ones that review cannot write what they judge.
  • 02Adversarial reviewSchema, keys, wire formats and the boot path get two agents in parallel, one making the case and one trying to break it. The decision is recorded with what would change it.
  • 03Release gatesAI review on every pull request, and nothing reaches production until an independent red-team pass has tried to break it. Confirmed findings block the release.
  • 04Skills and contextAgent instructions and skills written into firmware and platform repos; Claude skills that turn test-rig telemetry into range reports and flag tickets that no longer tell the truth; Jira and Confluence over MCP, and an MCP server I wrote for Basecamp.
  • 05Models in softwareA briefing agent on the Anthropic API: deterministic rules first, the model only returns strict JSON with no tools, and the prompt is hardened against injection.
[Code snippet]

Education

PhD

Circuits & Systems

Imperial College London, 2017 – 2021

Thesis on memristor-enabled reconfigurable analogue systems. Graduate Teaching Assistant in EE labs, amplifier design and FPGA.

MEng

Electronic Engineering, First Class

Imperial College London, 2013 – 2017

85%, top 10% of cohort. Dean's List and the Nicholas Battersby Prize for analogue electronics.