Intellisense #include errors with Arduino-MBED framework and Raspberry Pi Pico

Hi,

I am setting-up a Raspberry Pi Pico project on PlatformIO with the Arduino-MBED framework and I notice a number of #include errors relating to dependencies of “Arduino.h”.

Chasing down the unresolvable files, I can make Intellisense happy by adding the following paths to the includePath setting of c_cpp_properties.json - yes, I know those changes won’t persist when the project is reloaded etc.

The paths I need to add are:

/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/cmsis/CMSIS_5/CMSIS/TARGET_CORTEX_M/Include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/hal/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/platform/cxxsupport
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/platform/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/boards/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/common/pico_base/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/common/pico_time/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/generated
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2040/hardware_regs/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2040/hardware_structs/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_adc/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_base/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_gpio/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_i2c/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_irq/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_pwm/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_resets/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_spi/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_timer/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_uart/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_platform/include
/var/home/neil/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/TARGET_RASPBERRY_PI_PICO

I have two questions relating to implementing a solution to resolving these #include errors.

  1. Is there a standardised way to add those paths persistently to the Intellisense logic?
  2. The C/C++ Tools extension has a setting to enable the “Tag Parser” as a fallback for Intellisense parsing. Is this a suitable alternative to the above includePath additions?

The basic project builds OK so I’m only looking for a way to get effective use of Intellisense with this framework. Using the Tag Parser fallback, the Red Squiggly under Arduino.h is replaced by a Blue one and the main.cpp tab filename changes from Red to Green.

Thanks in advance for any help.
Neil

Something must have changed in VSCode :frowning: I remember this to be working on Linux. Now it only seems to be working on Windows. We recently had the same problem in Arduino-Pico (Earlephilhower) support, PicoProbe Debugging by maxgerhardt · Pull Request #36 · platformio/platform-raspberrypi · GitHub, and I had to adapt the builder script to transform an -iprefix <path> @<some file> to separate -I folders.

Which version of VSCode are you using, on which OS specifically?

Hi @maxgerhardt,

Thank you for replying on a Sunday. I am using VSCodium 1.68.0 (flatpak) on fedora 36 Silverblue edition.

Regards,
Neil

Oh that might be a special case then – does that use the Microsoft C/C++ extension at all to parse the .vscode/c_cpp_properties.json or a different, clang-based extension?

When you create a new Arduino Uno + Arduino project, does it have include errors as well?

Yes. VSCodium is VSCode without the Microsoft branding and telemetry disabled. It uses the VSCode C/C++ Tools extension and GCC semantics.

I installed the Atmel AVR Platform and imported the Blink example and there were no #include errors reported. That would suggest the Arduino-MBED framework integration is problematic.

Component versions:
C/C++ Tools v1.10.3
PlatformIO IDE v2.4.3
PlatformIO Core 6.0.2
PlatformIO Home 3.4.1

Regards,
Neil

@maxgerhardt,

Anticipating your next question, I replaced the VSCodium flatpak with the VSCode one and it exhibits exactly the same behaviour. I am thus confident that this isn’t a VSCodium-specific problem

Regards,
Neil

Then very likely changing the builder script as I did in Fix IDE include paths / Intellisense by maxgerhardt · Pull Request #615 · earlephilhower/arduino-pico · GitHub ought to fix it, though we might have the same problem in other cores as well :confused: . Let me give that a try.

Please open the file /var/home/neil/.platformio/platforms/raspberrypi/builder/frameworks/arduino/mbed-core/arduino-core-mbed.py and replace its content with

# Copyright 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Arduino

Arduino Wiring-based Framework allows writing cross-platform software to
control devices attached to a wide range of Arduino boards to create all
kinds of creative coding, interactive objects, spaces or physical experiences.

https://github.com/arduino/ArduinoCore-mbed
"""

import os

from SCons.Script import DefaultEnvironment

env = DefaultEnvironment()
platform = env.PioPlatform()
board = env.BoardConfig()

FRAMEWORK_DIR = platform.get_package_dir("framework-arduino-mbed")
assert os.path.isdir(FRAMEWORK_DIR)


def load_flags(filename):
    if not filename:
        return []

    file_path = os.path.join(FRAMEWORK_DIR, "variants", board.get(
        "build.variant"), "%s.txt" % filename)
    if not os.path.isfile(file_path):
        print("Warning: Couldn't find file '%s'" % file_path)
        return []

    with open(file_path, "r") as fp:
        return [f.strip() for f in fp.readlines() if f.strip()]


def configure_flash_layout(board_config):
    # Currently only Portenta board needs this functionality
    board_id = env.subst("$BOARD")
    if not board_id.startswith("portenta_h7"):
        return

    flash_layout = board_config.get("build.arduino.flash_layout", "50_50")
    defines = []
    if flash_layout == "50_50":
        defines.append(("CM4_BINARY_START", "0x08100000"))
        if board_id == "portenta_h7_m4":
            defines.append(("CM4_BINARY_END", "0x08200000"))
    elif flash_layout == "75_25":
        defines.append(("CM4_BINARY_START", "0x08180000"))
        if board_id == "portenta_h7_m4":
            board_config.update("upload.offset_address", "0x08180000")
            defines.append(("CM4_BINARY_END", "0x08200000"))
    elif flash_layout == "100_0":
        defines.append(("CM4_BINARY_START", "0x60000000"))
        if board_id == "portenta_h7_m4":
            defines.extend(
                [("CM4_BINARY_END", "0x60040000"), ("CM4_RAM_END", "0x60080000")]
            )

    env.Append(
        LINKFLAGS=["-D%s=%s" % (name, value) for name, value in defines],
        CPPDEFINES=defines,
    )


cflags = set(load_flags("cflags"))
cxxflags = set(load_flags("cxxflags"))
ccflags = cflags.intersection(cxxflags)

env.Append(
    ASFLAGS=[f for f in ccflags if isinstance(f, str) and f.startswith("-m")],
    ASPPFLAGS=["-x", "assembler-with-cpp"],

    CFLAGS=sorted(list(cflags - ccflags)),

    CCFLAGS=sorted(list(ccflags)),

    CPPDEFINES=[d.replace("-D", "") for d in load_flags("defines")],

    CXXFLAGS=sorted(list(cxxflags - ccflags)),

    LIBPATH=[
        os.path.join(FRAMEWORK_DIR, "variants", board.get("build.variant")),
        os.path.join(FRAMEWORK_DIR, "variants", board.get("build.variant"), "libs")
    ],

    LINKFLAGS=load_flags("ldflags"),

    LIBSOURCE_DIRS=[os.path.join(FRAMEWORK_DIR, "libraries")],

    LIBS=["mbed"]
)

if board.get("build.mcu", "").startswith("nrf52840"):
    env.Append(LIBS=["cc_310_core", "cc_310_ext", "cc_310_trng"])


# read includes from this file to add them into CPPPATH later
includes_file = os.path.join(FRAMEWORK_DIR, "variants", board.get("build.variant"), "includes.txt")
file_lines = []
includes = []
with open(includes_file, "r") as fp:
    file_lines = fp.readlines()
for l in file_lines:
    path = l.strip().replace("-iwithprefixbefore/", "").replace("/", os.sep)
    # emulate -iprefix <framework path>.
    path = os.path.join(FRAMEWORK_DIR, "cores", board.get("build.core"), path)
    includes.append(path)

env.Append(
    # Due to long path names "-iprefix" hook is required to avoid toolchain crashes
    CCFLAGS=[
        #"-iprefix" + os.path.join(FRAMEWORK_DIR, "cores", board.get("build.core")),
        #"@%s" % os.path.join(FRAMEWORK_DIR, "variants", board.get(
        #    "build.variant"), "includes.txt"),
        "-nostdlib"
    ],

    CPPDEFINES=[
        ("ARDUINO", 10810),
        "ARDUINO_ARCH_MBED"
    ],

    CPPPATH=[
        os.path.join(FRAMEWORK_DIR, "cores", board.get("build.core")),
        os.path.join(FRAMEWORK_DIR, "cores", board.get(
            "build.core"), "api", "deprecated"),
        os.path.join(FRAMEWORK_DIR, "cores", board.get(
            "build.core"), "api", "deprecated-avr-comp")
    ],

    LINKFLAGS=[
        "--specs=nano.specs",
        "--specs=nosys.specs",
        "-Wl,--as-needed"
    ]
)


def is_pio_build():
	from SCons.Script import COMMAND_LINE_TARGETS
	return "idedata" not in COMMAND_LINE_TARGETS and "_idedata" not in COMMAND_LINE_TARGETS

# expand with read includes for IDE, but use -iprefix command for actual building..
if not is_pio_build():
    env.Append(CPPPATH=includes)
else:
    env.Append(CCFLAGS=[
        "-iprefix" + os.path.join(FRAMEWORK_DIR, "cores", board.get("build.core")),
        "@%s" % os.path.join(FRAMEWORK_DIR, "variants", board.get(
            "build.variant"), "includes.txt"),
    ])

#
# Configure dynamic memory layout
#

configure_flash_layout(board)

#
# Linker requires preprocessing with specific defines
#

if not board.get("build.ldscript", ""):
    ldscript = os.path.join(
        FRAMEWORK_DIR, "variants", board.get("build.variant"), "linker_script.ld")
    if board.get("build.mbed.ldscript", ""):
        ldscript = env.subst(board.get("build.arduino.ldscript"))
    if os.path.isfile(ldscript):
        preprocessed_linker_script = env.Command(
            os.path.join(
                "$BUILD_DIR", "cpp.linker_script.ld"
            ),
            ldscript,
            env.VerboseAction(
                "$CXX -E -P -x c %s $SOURCE -o $TARGET"
                % " ".join(f for f in env["LINKFLAGS"] if f.startswith("-D")),
                "Generating LD script $TARGET",
            ),
        )

        env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", preprocessed_linker_script)
        env.Replace(LDSCRIPT_PATH=preprocessed_linker_script)
    else:
        print("Warning! Couldn't find linker script file!")

# Framework requires all symbols from mbed libraries
env.Prepend(_LIBFLAGS="-Wl,--whole-archive ")
env.Append(_LIBFLAGS=" -Wl,--no-whole-archive -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys")

libs = []

if "build.variant" in board:
    env.Append(CPPPATH=[
        os.path.join(FRAMEWORK_DIR, "variants", board.get("build.variant"))
    ])

    libs.append(
        env.BuildLibrary(
            os.path.join("$BUILD_DIR", "FrameworkArduinoVariant"),
            os.path.join(FRAMEWORK_DIR, "variants", board.get("build.variant"))))

libs.append(
    env.BuildLibrary(
        os.path.join("$BUILD_DIR", "FrameworkArduino"),
        os.path.join(FRAMEWORK_DIR, "cores", board.get("build.core"))))

env.Prepend(LIBS=libs)

Then in your project, use Ctrl+Shift+P → Rebuild Intellisense.

Does the regenerates .vscode/c_cpp_properties.json have all include paths so that no error is shown? Does building still work?

@maxgerhardt,

Excellent, thank you. That has fixed the issue and the project still builds. I’ve preserved the original arduino-core-mbed.py so I can examine the changes you made - for my personal interest.

Regards,
Neil

Interesting. I looked through the implementation and was suspicious about the -iwithprefixbefore in the includes.txt file. Mainly because I’ve not seen it before but more because it made my “Spider-sense” tingle.

I guess some form of generic solution would be beneficial - if that’s at all possible - rather than having to deal with this on a framework-by-framework basis?

Regards,
Neil

I’ve opened Intellisense broken on Unix-like OSes · Issue #4 · platformio/builder-framework-arduino-core-mbed · GitHub for reference, but I’ve just made a discovery: Downgrading the Microsoft C/C++ extension version from 1.10.5 to 1.9.8 fixes the error too… :confused:

Will hopefully be discussed in Intellisense broken on Unix-like OSes and new C/C++ extension version · Issue #4317 · platformio/platformio-core · GitHub further.

@maxgerhardt,

The VSCode C/C++ Tools 1.10.6 provides a fix for this problem.

Regards,
Neil

Thankfully Microsoft fixed it – no need for the changes to the builder script anymore.