Skip to the content
4 layers200 × 110 mmLTDM RevC, the printed circuit board in 3D

New LTDM Code that has render.cpp

Size
200 × 110 mm
Copper layers
4
Thickness
1.6 mm
Parts
412
Nets
241
Pads
1,106
Tracks, vias
3,013, 1,028
Pours
25
Routed
Yes
KiCad
KiCad 9.0 (file 20241229)
Created
20 Mar 2026
Updated
26 Oct 2026

Overview

LTDM RevC is an open-source printed circuit board by aschmidt99, from the aschmidt99/LTDM_Kit_RevC repository on GitHub. It is a 4-layer board of 200 × 110 mm with 412 parts on 241 nets, routed with 3,013 tracks and 1,028 vias.

It is built around the OPA2350UA, AP64500SP and 74LVC1G11GW, with the LM393, 74LVC2G74DP and 1N4148W among its other parts. Its parts include 132 resistors, 47 capacitors, 29 connectors, 28 ICs and 13 diodes.

Main components

9

README

README.md

LTDM Kit Rev C

Firmware and hardware assets for a Teensy 4.1 based LTDM (Lorentz Time-Domain Multiplex) kit.

Programming

Iterate quickly on dsp and string behavior in Code/LTDM_Kit/render.cpp, leaving precise timing of sensing and actuating untouched by user.

Interaction

Map hardware controls (buttons/sliders/pedals) to your algorithm through the UI layer.

Project Structure

  • Code/LTDM_Kit: Firmware running on Teensy 4.1
  • Code/Python: capture, analysis, and Audacity automation scripts
  • Code/LTDM_Kit_Control.maxpat: a maxpatch for overriding the LTDM Kit physical UI or running LTDM kit main board without UI.
  • Electronics: KiCad project (schematic and pcb) for PCB design
  • CAD: mechanical STEP/STL assets for enclosure

Program and Upload

Firmware can be edited and uploaded to the teensy 4.1 via the Arduino 2.X IDE or from VS code.

Arduino IDE

To use the Arduino IDE, you will need to (install the Arduino IDE). Once installed, download the Teensy Board library by following the instructions (here).

After firmware changes are made, make sure the teensy is connected and seen by the dropdown menu in the upper left of the IDE. Use the 'upload' button next to this to flash new firmware.

Arduino CLI (direct)

If this is your first time connecting from a new machine, run this one-time setup first:

  1. Install Arduino CLI.
  2. Add the Teensy board index URL.
  3. Update board indexes and install the Teensy core.
  4. Plug in Teensy 4.1 with a data-capable USB cable.
  5. Verify the detected USB port before upload.

Example one-time setup commands:

bash
# Add PJRC Teensy package index (safe to run again if already present)
arduino-cli config add board_manager.additional_urls https://www.pjrc.com/teensy/package_teensy_index.json

# Refresh indexes and install Teensy core
arduino-cli core update-index
arduino-cli core install teensy:avr

# Confirm Teensy core is installed
arduino-cli core list

Check that your Teensy is visible and note its port:

bash
arduino-cli board list

On macOS, Teensy ports commonly look like /dev/cu.usbmodem*.

To flash code directly from terminal:

bash
cd Code/LTDM_Kit
arduino-cli compile --fqbn teensy:avr:teensy41 .
arduino-cli upload --fqbn teensy:avr:teensy41 -p /dev/cu.usbmodemXXXX .

VS Code tasks

To flash firmware from VS code, you can use the premade VS code tasks by using Cmd+Shift+P -> Tasks: Run Task and select:

  • Teensy: Compile LTDM_Kit
  • Teensy: Upload LTDM_Kit (prompt port)
  • Teensy: Upload LTDM_Kit (auto port)
  • Teensy: Compile + Upload LTDM_Kit (auto port)

Task definitions live in .vscode/tasks.json.

How the Firmware is Organized

Essential core files (base firmware)

  • LTDM_Kit.ino: startup order and top-level loop (setup() and loop()).
  • flexpwm.cpp / flexpwm.h: hardware timing, ISR, ADC reads, and PWM update path.
  • render.cpp / render.h: per-sample behavior, where algorithm/control logic is authored.
  • ui.cpp / ui.h: physical control scan (buttons/sliders/pedals), LED updates, and serial telemetry printout.
  • channel.h: per-channel runtime/state struct fields used by ISR and render.
  • context.h: shared frame context passed into render() each sample.
  • pinmap.h: board pin assignments and hardware routing constants.
  • system_config.h: global compile-time configuration (sample rate, buffer sizes, PWM resolution).

Optional or bonus helper modules

These are useful, but not strictly required for a minimal "sense -> render -> actuate" base path.

  • serialctrl.cpp / serialctrl.h: optional host serial override of hardware UI state (used by Max patch control).
  • capture_stream.cpp / capture_stream.h: optional deterministic capture trigger and streaming for measurement scripts.
  • harmonics.cpp / harmonics.h: additive harmonic oscillator bank and zero-crossing pitch tracking.
  • visualAlias.cpp / visualAlias.h: optional visual alias output behavior by plugging a 12V LED strip into one of the channels.
  • SineTable.cpp / SineTable.h: lookup-table sine source used by harmonic/visual helper DSP.
  • debug.h: compile-time debug print macros (DEBUG_PRINT, DEBUG_PRINTLN).

At runtime, the flow is:

  1. ISR acquires channel state and UI state into context.
  2. render(context) computes output values once per sample.
  3. PWM/sense outputs are driven from those computed values.

Codebase division-of-labor

This codebase intentionally separates three concerns:

  1. Real-time infrastructure: sample timing, GPIO/PWM scheduling, ADC/sense capture.
  2. Control surface: what knobs/buttons/pedals currently mean.
  3. Algorithm: how measured input becomes actuation output.

The design goal is to let you iterate and develop actuation and feedback behavior without destabilizing anything with critical timing, such as the regular periodic sense + actuate loop.

In practice, treat render.cpp as your sandbox for algorithmic ideas:

  • Feedback shaping
  • Noise injection and excitation strategies
  • RMS/energy targeting
  • Harmonic synthesis
  • Cross-channel coupling experiments

Where to Make Changes

Audio/actuation behavior

Edit Code/LTDM_Kit/render.cpp.

Key entry points:

  • bool renderSetup(LorentzContext *context) for one-time setup
  • void render(LorentzContext *context) for per-sample behavior

Useful signals available from context:

  • context->ch[0].in, context->ch[1].in (channel's sensed audio input)
  • context->buttons[0] (button 0-7 are the latching buttons, and 6-15 are the momentary push buttons)
  • context->sliders[0..15] (slider 0-7 are the potentiometers, 8-15 are the sliders)
  • context->pedals[0..1] (optionally connect up to 2 expression pedals)

Button/slider behavior mapping

Edit Code/LTDM_Kit/ui.cpp and Code/LTDM_Kit/render.cpp together.

How it works:

  1. ui.cpp scans physical mux inputs and sets UI LED outputs.
  2. render.cpp assigns meaning to those logical indices.

Pin assignments

Edit Code/LTDM_Kit/pinmap.h only if attaching additional functions to the available GPIOs.

Render/control behavior (channel 1 focused)

VisualGuide

In Code/LTDM_Kit/render.cpp:

  • Button 0/1 enables channel 1 and 2 output respectively (enablePin high/low).

  • Button 2 toggles RMS-targeted gain behavior for CH1.

  • Button 6 gates CH1 noise injection.

  • Button 8 requests capture start while capture is armed.

  • Slider 0: CH1 feedback gain (fbGain).

  • Slider 1: CH1 target RMS.

  • Slider 2: noise update rate (acts like coarse smoothing on random term).

  • Slider 3: CH1 noise scale.

  • Slider 4: harmonic frequency smoothing parameter (alpha).

  • Sliders 8..15: gains for harmonics 1..8 (NUM_HARMONICS = 8).

  • Note: Channel 2 currently carries visual-alias pulse output behavior (renderLEDPulse(...)) rather than string feedback behavior

LED/UI feedback

In Code/LTDM_Kit/ui.cpp:

  • TLC5947 LEDs reflect enable/limit/target and measured RMS states.
  • UI scan runs in two-step mux read/write cycles.
  • output() also prints debug telemetry over serial every ~100 ms (as long as the the python audio capture stream is idle.)

Serial override mode

Code/LTDM_Kit/serialctrl.cpp can override hardware controls via framed serial packets. There is an example max/msp patch ('LTDM_Kit_Control.maxpat') that already has this setup.

  • Start marker: 255
  • End marker: 254
  • Payload: mode + 16 sliders + 16 buttons + 2 pedals (34 bytes)

When override is active, incoming values are written directly into the shared UI state arrays.

Python Tools

The Python tools in Code/Python are used for measurement, capture automation, and analysis around the firmware:

  • capture_teensy_plus_interface.py: Capture audio directly from Teensy (what the teensy 'hears' from the EMF sensing circuitry) and 2 channels of audio from a connected audio interface. Optionally import these files directly to audacity.
  • run_trial.py: Prompt for trial/profile/duration and launch the capture workflow via named profiles.
  • capture_teensy_stream.py: Capture Teensy stream only (without interface capture), with optional WAV export.
  • rigol_capture.py: Run Rigol DS1054Z waveform/screenshot capture and write trial metadata.
  • load_rigol_capture.py: Load and interactively inspect saved Rigol capture files.
  • rigol_screen.py: Capture or convert Rigol's screen images.
  • plot_teensy_stream.py: Plot saved Teensy stream files for quick signal inspection.

If you are starting from scratch, run this script first:

  • Code/Python/run_trial.py

It prompts for trial and duration, auto-suggests the next trial number, and launches the full capture pipeline with known-good defaults.

Full Python docs:

  • Code/Python/PYTHON_README.md

Parts

412
QtyValueFootprintReferences
51100nCapacitor_SMD:C_0603_1608MetricCU1, CU2, CU10, CU11, CU13, CU15, CU16, CU17, CU18, CU19, CU20, CU21, CU22, CU23, CU24, CU25 +35
39TestPointTestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, TP18, TP19, TP20, TP21, TP22, TP23, TP25, TP26 +23
281kResistor_SMD:R_0603_1608MetricR3, R15, R17, R21, R33, R34, R35, R36, R42, R43, R44, R100, R124, R125, R137, R139 +12
1910kResistor_SMD:R_0603_1608MetricR10, R16, R22, R55, R126, R127, R128, R129, R130, R131, R132, R133, R134, R135, R142, R143 +3
17Conn_01x03_MaleConnector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ1, J2, J3, J6, J8, J10, J18, J19, J20, J21, J23, J27, J29, J30, J31, J32 +1
161.01kResistor_SMD:R_0603_1608MetricR4, R14, R19, R20, R52, R65, R68, R69, R105, R106, R107, R108, R109, R110, R111, R112
14GNDJumper:SolderJumper-2_subtleJP3, JP4, JP5, JP6, JP7, JP9, JP10, JP12, JP13, JP14, JP15, JP16, JP17, JP18
13OPA1612AxDPackage_SO:SOP-8_3.76x4.96mm_P1.27mmU4, U6, U13, U14, U18, U19, U22, U23, U24, U25, U27, U28, U30
100Resistor_SMD:R_0603_1608MetricR1, R8, R12, R24, R49, R59, R63, R75, R115, R116
1010nCapacitor_SMD:C_0603_1608MetricC37, C38, C41, C42, C43, C44, C45, C46, C48, C49
1010uCapacitor_SMD:C_0805_2012MetricC6, C7, C8, C9, C12, C13, C14, C15, C18, C19
81N4148WDiode_SMD:D_SOD-123D1, D2, D7, D9, D14, D15, D16, D17
6220pFCapacitor_SMD:C_0603_1608MetricC1, C3, C30, C34, C35, C36
6510Resistor_SMD:R_0603_1608MetricR2, R41, R48, R50, R103, R104
5100nCapacitor_SMD:C_0402_1005MetricC11, C23, C26, C28, C29
52.2kResistor_SMD:R_0603_1608MetricR56, R72, R94, R117, R136
5LOGOLOGOG***, G***, G***, G***, G***
410Resistor_SMD:R_0603_1608MetricR27, R29, R37, R38
411Resistor_SMD:R_0603_1608MetricR11, R18, R58, R71
4220Resistor_SMD:R_0603_1608MetricR9, R57, R113, R114
474LVC1G11Package_TO_SOT_SMD:SOT-23-6U1, U26, U31, U32
4787Resistor_SMD:R_0402_1005MetricR118, R119, R120, R121
4AudioJack3_SwitchTRConnector_Audio:Jack_3.5mm_CUI_SJ1-3535NG_HorizontalJ35, J36, J37, J38
4MountingHole_PadMountingHole:MountingHole_4.3mm_M4_ISO14580_PadH1, H2, H3, H4
4OPA2350Package_SO:SOP-8_3.76x4.96mm_P1.27mmU3, U17, U21, U29
4bridgeJumper:SolderJumper-2_P1.3mm_Bridged_RoundedPad1.0x1.5mmJP1, JP2, JP20, JP21
31MegResistor_SMD:R_0603_1608MetricR6, R54, R138
31kPotentiometer_SMD:Potentiometer_Bourns_3314G_VerticalRV1, RV7, RV10
3FQP27P06Package_TO_SOT_THT:TO-220-3_VerticalQ1, Q3, Q5
3IRLZ44NPackage_TO_SOT_THT:TO-220-3_VerticalQ2, Q4, Q6
3LEDLED_SMD:LED_0603_1608MetricD3, D4, D8
20.1R 5WResistor_SMD:R_4527CSR1, CSR2
20.5Package_TO_SOT_THT:TO-220-2_VerticalPR1, PR2
21000uCapacitor_THT:CP_Radial_D10.0mm_P5.00mmC22, C32
2100R 1WResistor_SMD:R_2512_6332MetricR26, R40
210nCapacitor_THT:C_Rect_L11.0mm_W5.1mm_P10.00mm_MKTC2, C33
210u 35vCapacitor_SMD:C_0805_2012MetricC24, C31
212K 1%Resistor_SMD:R_0402_1005MetricR45, R47
21uCapacitor_SMD:C_0805_2012MetricCU12, CU14
2205Resistor_SMD:R_0603_1608MetricR98, R101
220kResistor_SMD:R_0603_1608MetricR13, R62
2220uCapacitor_THT:CP_Radial_D8.0mm_P3.50mmC5, C27
232.4Resistor_SMD:R_0603_1608MetricR99, R102
2374Resistor_SMD:R_0603_1608MetricR64, R77
2392Resistor_SMD:R_0603_1608MetricR122, R123
24.7kResistor_SMD:R_0603_1608MetricR140, R141
24.7nCapacitor_SMD:C_0603_1608MetricC39, C40
247Resistor_SMD:R_0603_1608MetricR90, R91
2576Resistor_SMD:R_0603_1608MetricR60, R76
2680Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_HorizontalR25, R39
2BSS123Package_TO_SOT_SMD:SOT-23Q15, Q16
2Barrel_JackConnector_BarrelJack:BarrelJack_Wuerth_6941xx301002J16, J22
2CClamp_Out1TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP35, TP36
2CGain_Out2TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP31, TP32
2CRect_Out1TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP33, TP34
2CSR_BypassJumper:SolderJumper-2_P1.3mm_Open_Pad1.0x1.5mmJ11, J14
2CSR_BypassConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ24, J28
2Conn_01x02TerminalBlock_RND:TerminalBlock_RND_205-00232_1x02_P5.08mm_HorizontalPRC1, PRC2
2DNPCapacitor_THT:C_Disc_D3.0mm_W1.6mm_P2.50mmC10, C25
2LF398_SOIC14Package_SO:SOIC-14_3.9x8.7mm_P1.27mmU2, U16
2LM393Package_SO:SOP-8_3.76x4.96mm_P1.27mmU5, U15
2MMBZ5242BLT3GPackage_TO_SOT_SMD:SOT-23D5, D6
2RectOutTestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP4, TP24
2SW_SP3TButton_Switch_THT:SW_Slide-SSSS918700SW1, SW2
2SpringTerminalTerminalBlock:TerminalBlock_Spring_2_P5.08mmJ12, J15
2TPTestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mmTP1, TP2
2dnpCapacitor_SMD:C_0402_1005MetricC17, C20
11.2nCapacitor_SMD:C_0402_1005MetricC16
1100kResistor_SMD:R_0603_1608MetricR7
1100uCapacitor_Tantalum_SMD:CP_EIA-7343-31_Kemet-DC4
110RResistor_SMD:R_0402_1005MetricR28
1120Resistor_SMD:R_0603_1608MetricR23
112KResistor_SMD:R_0402_1005MetricR31
1176KResistor_SMD:R_0402_1005MetricR144
12.2kResistor_SMD:R_0402_1005MetricR32
120KResistor_SMD:R_0402_1005MetricR30
13.3uInductor_SMD:L_Bourns_SRN6045TAL1
174AUC2G86Package_SO:VSSOP-8_2.3x2mm_P0.5mmU20
174LVC2G74Package_SO:VSSOP-8_2.3x2mm_P0.5mmU36
1AP64500SPPackage_SO:SOIC-8-1EP_3.9x4.9mm_P1.27mm_EP2.95x4.9mm_Mask2.71x3.4mm_ThermalViasU10

The 398 most common of 412 parts.

More from aschmidt99/LTDM_Kit_RevC

1
  • 2 layers200 × 110 mmLTDM UI RevC, the printed circuit board in 3D

    LTDM UI RevC

    aschmidt99/LTDM_Kit_RevC

    New LTDM Code that has render.cpp

    83 parts2

Similar boards

  • 4 layers96 × 60 mmMainboard-2023, the printed circuit board in 3D

    Mainboard-2023

    luhbots/luhsoccer_electronics

    The eletronics projects of our robots and basestation.

    212 parts2
  • 2 layers65 × 56.5 mmDcdchat, the printed circuit board in 3D

    Dcdchat

    KPI-Rover/rpi-power-hat

    Power hat for RPI

    42 parts1
  • 2 layers70 × 82.5 mmAMP PCBv3, the printed circuit board in 3D

    AMP PCBv3

    Autonomous-Motorsports-Purdue/AMP_PCBv3

    Files relating to version 3 of the PCB for AMP kart

    86 partsMIT
  • 6 layers160 × 136 mmSerie, the printed circuit board in 3D

    Serie

    npgo22/Serie

    The board itself is based around an STM32H723, the fastest microcontroller supported by Klipper at the moment.

    549 parts
  • 4 layers90 × 90 mm01 2x30w audio amp, the printed circuit board in 3D

    01 2x30w audio amp

    tomek-szczesny/byos_m2

    Build Your Own System - a collection of stackable add-ons

    103 parts
  • 2 layers100 × 100 mmBirdNestV3, the printed circuit board in 3D

    BirdNestV3

    VorosEgyes/BirdNest

    BirdNest Camera

    68 parts1MIT
  • 2 layers140 × 285.9 mmFishShocker9000, the printed circuit board in 3D

    FishShocker9000

    rmranjitkarNULL/FishShocker9000

    TODO -[ ] Redo Readme -[ ] Make assembly guide -[ ] add

    206 parts
  • 2 layers186.2 × 66.3 mmRf main, the printed circuit board in 3D

    Rf main

    esden/mcHF-kicad

    This repository contains the KiCad import of the Altium design files for the mcHF QRP radio.

    314 parts3

Questions

How big is the LTDM RevC PCB?

It measures 200 × 110 mm and has 4 copper layers, 1.6 mm thick.

What parts does the LTDM RevC use?

412 parts, built around the OPA2350UA, AP64500SP, 74LVC1G11GW and LM393. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the LTDM RevC KiCad files?

From the aschmidt99/LTDM_Kit_RevC repository on GitHub, at the commit this page was made from: the board is Electronics/LTDM_RevC/LTDM_RevC.kicad_pcb and its schematic Electronics/LTDM_RevC/3V3_Clamp.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the LTDM RevC design in my own project?

Its repository states no licence, so its author keeps all rights: ask aschmidt99 before reusing the design.