Three things the solution got wrong, all found by opening it in the GUI rather than building it headlessly. The project listed only main.cpp, so none of the headers could be opened from Solution Explorer. Every source and header is listed now, with <Link> mirroring the on-disk src/ layout, which is what master's project did. Neither configuration compiled in the GUI: libavr was found through $(LIBAVR_ROOT), and a variable exported in a shell is not visible to Studio launched from the Start menu. Release only looked healthy because its objects were already up to date from a headless build. The path is now anchored to the project directory, which also side-steps a second trap: a plain relative include is resolved against the generated makefile's own directory, the configuration's output directory, not the project's. Pinning libavr as a submodule would remove the assumption that the two sit side by side, and is on libavr's task list. Both configurations verified with LIBAVR_ROOT deliberately unset, Release still byte-identical to the CMake build and the flag gate still green. Debug's own translation unit carries DWARF-4 as intended. Studio's per-user state under ide/.vs/ and the build logs are ignored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fantemp
Temperature-controlled fan firmware (ATmega328P, 16 MHz), rewritten on
libavr: thermistor on ADC0 sampled
free-running and averaged over 1000 conversions, fan on OC0B at 50 kHz,
115200 Bd serial console (help lists the commands), temperature
histogram persisted to EEPROM, watchdog-reset path into a boot-section
bootloader.
The Steinhart–Hart math of the legacy firmware (runtime doubles + libm log) is gone: the Beta equation and the cubic fan curve are evaluated consteval into flash tables — the firmware itself never touches floating point.
Build against a libavr checkout:
LIBAVR_ROOT=/path/to/libavr cmake --preset atmega328p-generated
cmake --build --preset atmega328p-generated
Atmel Studio
master carries a Studio solution, so this branch does too: ide/fantemp.atsln
builds the same firmware — byte-identical .text and .data to the CMake
build — from the same sources, with the flags mirrored by hand.
Studio expects libavr checked out beside this repo, and finds it at
$(MSBuildProjectDirectory)\..\..\libavr\include — anchored to the project
rather than written relative to the generated makefile, which runs from the
configuration's output directory and would need a different number of ...
Unlike the CMake build there is no LIBAVR_ROOT to point elsewhere: an
environment variable set in a shell is not visible to Studio launched from the
Start menu, which fails as a missing libavr/libavr.hpp rather than as anything
about the variable. Pinning libavr as a submodule is the better answer and is on
libavr's task list.
It also needs a GCC 16.1 toolchain registered as flavour avr-g++-16.1.0;
nothing older can compile -std=c++26.
One generated file is required before the project will load, and one command
checks the flags have not drifted (both from libavr's tools/atmelstudio/):
python ../libavr/tools/atmelstudio/componentinfo.py \
ide/fantemp.componentinfo.xml --device ATmega328P
python ../libavr/tools/atmelstudio/check-flags.py --solution ide/fantemp.atsln \
--compile-commands build/atmega328p-generated/compile_commands.json \
--log build/atmelstudio.log
CMake remains the build system; the solution is there so the project opens in
Studio as its predecessor did. Only the Release configuration is gated against
CMake — the presets define no debug build — and Debug carries the -Og -gdwarf-4 pair libavr's own debug preset uses.
Legacy (yazoalfa submodules) stays on master.