Portable controller-core export (experimental): a new Generic target generates a self-contained, hardware-free controller you drop into your own project on any MCU — call controller_step() every sample period and wire up simple named stubs (adc_read/pwm_set/…) for your peripherals. Outputs are plain floats, so no actuator type is assumed. Available in both Rust and C (the C export writes a controller.h/.c pair for the controls-subset). Switching a project to Generic offers to convert your MCU I/O blocks into named Port In / Port Out blocks in place (wiring preserved); each port carries Float, Logic, or Integer.
STM32G4 support (experimental): the STM32G431KB and 48-pin STM32G431CB are selectable chip targets with digital I/O, analog input, single- and multi-channel PWM, and a configurable clock — still being validated on hardware.
Bus diagnostics: experimental SPI/I2C/QSPI/flash blocks can report how long a transfer stalled the control loop (pending cycles for async, microseconds for blocking), labelled by I/O role, in the Timing readout.
Signed register reads (experimental): I2C Read Register and SPI Transfer gained a Data Format option, so sensors that return two's-complement values (IMUs, thermocouples, bipolar ADCs) no longer read negatives as large positive numbers.
Int to Float and Float to Int blocks: convert between whole-number and decimal signals, with a scale and offset built in — so a raw sensor count becomes a real physical value (g, °C, volts) in a single block. Int to Float asks how to read the incoming bits: signed by default, or unsigned if the top bit is data rather than a sign, with an optional sign-extend for narrow fields at any width from 2 to 32 bits — so odd-width readings like a 12-bit field convert correctly.
Analog Input also gives you the raw ADC count alongside the voltage — useful for ratiometric sensors, calibration, or anywhere the raw code is what you actually want.
PWM blocks now say what full duty is, on the block itself. The 1- and 2-channel blocks previously had no setting for it at all and silently assumed 1.0, so driving one from a 0-100 output sat at full duty with no indication. The app also warns when a high PWM frequency leaves too few duty steps for fine control.
Number fields accept arithmetic: type the ratio your datasheet publishes — an MPU-6050 says 16384 LSB/g, so Scale takes 1/16384 instead of 0.000061035. It handles values a single number cannot express cleanly too, like 3.3/4096 for a 12-bit ADC. The field shows the resolved number at rest and your original text when you click in, so retuning a sensor range is a one-character edit rather than a recalculation.
Signed fields on Bit Extract (experimental): pull a signed field out of a register and it now reads as a negative number where it should, instead of a large positive one — and reads the same way everywhere it goes, including on a Digital Scope.
Fixed: Shift Left and Shift Right ignored their shift amount and always shifted by zero.
SPI and I2C peripherals are set up for you (experimental): a blocking bus design used to export a project that wouldn't link until you hand-wrote the hardware functions yourself. Export now generates the peripheral setup and the transfers, so a sensor design builds, flashes, and runs with no hand-written files — verified by reading a live MPU-6050 on an STM32F411, and on an STM32G431. Async transfers still use the stub path.
Bus pin and clock settings (experimental): SPI and I2C blocks now carry their own pin choices and bus clock, offered as dropdowns of exactly the pins that peripheral can use on your chip. Blocks sharing one bus are kept in step automatically.
Bus fault handling (experimental): an unresponsive sensor could previously hang the control loop forever — including on the wake-up write most sensor designs do first, before any reading is taken. Blocking reads and writes now bound how long they wait, and new Project Settings cap the time a transfer may take (Bus Timeout Budget) and stop a dead device being retried every cycle (Bus Retry Hold-Off) — so a missing sensor reports an error instead of stopping your controller.
Fixed: designs using SPI, I2C, or QSPI failed to build with a linker error. Blocking SPI/I2C now needs no hand-written code at all; designs still on the stub path get a hal_stubs.rs scaffold with a placeholder for each hardware function.
Clearer Timeout help on bus blocks (experimental): every bus block claimed its Timeout bounded the transfer, which was only true for I2C. The help now says what each bus really does — I2C bounds the transfer (and the value you enter is capped against your loop period), SPI accepts the setting without enforcing it, and QSPI/flash hand it to your own stub code.
Async I2C reads need no hand-written code (experimental): ticking Async Mode on an I2C Read Register used to export a project that wouldn't link until you wrote the hardware functions yourself. Export now generates the whole non-blocking transfer, so the read advances a step per cycle instead of stalling your control loop — verified on a live MPU-6050 on an STM32F411, including waking the sensor from sleep through a generated async write. F4 I2C for now; async SPI and G4 still use the stub path.
Fixed: SPI Transfer never drove its chip-select pin (experimental). The CS Pin setting existed and was checked for conflicts, but generated firmware never toggled it — so any SPI design talking to a real device got nothing back. Verified against the onboard flash on an STM32F411 Black Pill; fixes blocking and async alike.
Async SPI transfers need no hand-written code (experimental): as with I2C, ticking Async Mode on an SPI Transfer used to export a project that wouldn't link until you wrote the hardware functions yourself.
Fixed: a bus block could be locked out forever (experimental). With three or more async SPI/I2C blocks sharing one peripheral under Deferred output timing, the last one never got a turn — it just waited, with no error to show for it. The peripheral now hands the bus round in rotation so every block gets served.
v0.2.2
Latest
Output Timing: a new Project Settings option for how hardware writes are scheduled each cycle. Immediate (grouped) removes write-order jitter with zero added latency by flushing all writes together at the end of the cycle; Deferred (shadow buffer) flushes at a fixed point for a rock-steady output phase at the cost of one sample of latency.
I/O-phase badges: turning on Show Execution Order now tags each hardware block with a colour-coded R/W/RW/P badge showing which I/O phase its read, write, or async poll belongs to — and whether a write is grouped or inline under the selected Output Timing mode.
Show Signal Types: a new View menu toggle labels every port with its data type (f32, i32, Q1.31, Logic, Any) right on the canvas.
Choose your MCU package: the chip selector is now family-then-package, so you can pick a specific package and use pins that the default package doesn't expose.
Timing diagnostics: a new Real-Time Monitor block measures your control loop's execution time, jitter, and deadline overruns on the STM32 and shows them — plus a CPU-utilization gauge — in the Signal Monitor.
Chip-specific block settings: options like ADC oversampling now appear only on chips that support them, with a warning (not a silent drop) when you switch away.
Signal analysis: open-loop Bode plots with automatic gain and phase margins, via a new Frequency Probe block.
Redesigned Analysis Console: Signal Monitor, Eigenvalue/Root Locus, and Bode views, shown when their data is available.
The app now checks for new releases on launch and shows a prominent update banner.