Ich habe neulich an einem Arduino-Projekt gearbeitet und musste meine Entwicklungsumgebung anpassen. Anstatt der Arduino IDE, nutze ich lieber den Editor Zed mit clangd als Compiler und PlatformIO für die Hardwareintegration.

Ich möchte bereits erstellten Code und Bibliotheken wiederverwenden, wenn ich neue Projekte erstelle. Obwohl der Code kompiliert und auf dem Arduino läuft, funktioniert die Zed-Integration nicht ideal, da der integrierte Linter für C/C++ dennoch Fehler/Warnungen zeigt. Das können zum Beispiel folgende Meldungen sein:

Use of undeclared identifier 'LED_BUILTIN'
Use of undeclared identifier 'Serial'
'Led.h' file not found

PlatformIO und clangd brauchen dafür dieselbe Compiler-Umgebung. Dies habe ich folgendermaßen realisiert:

1. Erstellung der PlatformIO “compilation database”

Mit PlatformIO kann man die Datei compile_commands.json generieren:

pio run -t compiledb

Dies entspricht dann in etwas folgendem Compiler-Kommando für src/main.cpp

avr-g++ ... \
  -DARDUINO_AVR_UNO \
  -DF_CPU=16000000L \
  -DARDUINO_ARCH_AVR \
  -DARDUINO=10808 \
  -Iinclude \
  -Isrc \
  -Ilib/Led \
  -I.../framework-arduino-avr/cores/arduino \
  -I.../framework-arduino-avr/variants/standard \
  src/main.cpp

Das ist der bevorzugte Weg anstatt die Arduino Include-Pfade in .clangd zu pflegen.

2. Arduino-Header explizit verwenden

Der Quellcode sollte die Arduino-Header explizit verwenden:

#include <Arduino.h>

3. Kein Vermischen der Pfade von Arduino IDE und PlatformIO

Zunächst hat .clangd folgende Pfade:

~/Library/Arduino15/packages/arduino/...

Diese gehören zur “Arduino IDE”-Installation. Für ein PlatformIO-Projekt befindet sich das Framework aber hier:

~/.platformio/packages/framework-arduino-avr/

Daher sollte die PlatformIO “compilation database” die Wahrheit enthalten anstatt manuell Pfade zu kopieren oder zu pflegen.

4. Diagnostik

Der folgende Befehl zeigte das wahre Problem:

clangd --check=src/main.cpp

Clangd nutzt das integrierte clang von Apple:

/Library/Developer/CommandLineTools/usr/bin/clangd

und hat das target im Compiler-Befehl von PlatformIO als macOS-ARM-Architektur erkannt:

target 'arm64-apple-darwin'

obwohl der Quellcode für etwas anderes gedacht war:

ATmega328P / AVR

Die Diagnostik liefert:

unsupported option '-mmcu=' for target 'arm64-apple-darwin'

und daraus folgt:

'avr/pgmspace.h' file not found

Das erklärt, warum die Arduino-Symbole, wie z.B. Serial, nicht aufgelöst wurden.

5. clangd muss den PlatformIO AVR Compiler nutzen

Die finale .clangd Konfiguration war:

# yaml-language-server: $schema=https://json.schemastore.org/clangd.json

CompileFlags:
  CompilationDatabase: .
  Compiler: /Users/<YOUR_USER>/.platformio/packages/toolchain-atmelavr/bin/avr-g++

Diagnostics:
  MissingIncludes: None
  UnusedIncludes: Strict

Die Compiler-Zeile in den CompileFlags sagt clangd, dass die gleiche AVR-Compiler-Toolchain verwendet werden soll, die PlatformIO nutzt. Damit nutzt clangd folgendes:

  • AVR-Targets
  • -mmcu=atmega328p
  • AVR-Systemheader
  • Arduino-AVR-Header
  • boardspezifische Definitionen, wie z.B. ARDUINO_AVR_UNO

Der native AVR-Compilertest bestätigt, dass die PlatformIO-Toolchain funktionierte:

/Users/<YOUR_USERNAME>/.platformio/packages/toolchain-atmelavr/bin/avr-g++ \
  ... \
  -I.../framework-arduino-avr/cores/arduino \
  -I.../framework-arduino-avr/variants/standard \
  -E src/main.cpp

Und der Präprozessor fand auch HardwareSerial.h. Somit war die Arduino-Installation nicht defekt, sondern lediglich clangd-Konfiguration.

7. Projektbibliotheken

Nachdem die AVR-Toolchain repariert war, konnte clangd die Include-Pfade des PlatformIO-Projekts aus der compile_commands.json nutzen, z.B. -Ilib/Led, sodass #include <Led.h> ebenfalls funktionierte.

Das zeigt, warum es sinnvoll war die Compilation-Database zu nutzen, anstatt manuell in einzelnen Projekten .clangd-Dateien zu pflegen.