Issue linking local libraries

Hi there!

I’m having trouble linking a local library that I wrote:

C:/Users/<user>/.platformio/packages/toolchain-rp2040-earlephilhower/bin/../lib/gcc/arm-none-eabi/14.3.0/../../../../arm-none-eabi/bin/ld.exe: .pio\build\default\src\main.cpp.o: in function `setup': 
main.cpp:(.text.setup+0x6): undefined reference to `_ZN6Bme280C1Eh'

My project structure looks like this:

|- platformio.ini
|-- src
|  |-- main.cpp
|-- lib
|  |-- i2c
|  |  |-- src
|  |  |  |-- bme280.cpp
|  |  |  |-- bme280.h
|  |  |  |-- i2c.cpp
|  |  |  |-- i2c.h
|  |  |-- library.json
...

platformio.ini:

[env]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = adafruit_feather_thinkink
framework = arduino

[env:default]
lib_deps =
    adafruit/Adafruit NeoPixel@^1.15.2
    symlink://lib/i2c

main.cpp:

#include <Arduino.h>
#include <bme280.h>

void setup() {
  Bme280 bme;

  bme.reset();

  byte* chipId = bme.readBytes(0xD0, 1);
  Serial.printf("chip id: %d\n", chipId);
  delete chipId;
}

void loop() {

}

bme280.h:

// bme280.h contains header code for the BME280 sensor from bosch
#ifndef BME280_H
#define BME280_H

#include "i2c.h"

#define BME280_ADDR 0x76
#define BME280_ADDR_ALT 0x77

class Bme280 : public I2C {
    public:
        Bme280(byte addr = BME280_ADDR);

        void reset() override;
};

#endif

bme280.cpp:

// bme280.cpp contains implementation code for the BME280 sensor from bosch
#include "bme280.h"

// Performs a soft reset
void Bme280::reset() {
    Wire.beginTransmission(this->addr);

    // Register is 0xE0
    Wire.write(0xE0);
    // Command to reset is 0xB6
    Wire.write(0xB6);

    byte out = Wire.endTransmission();
    if (out) {
        Serial.printf("Reset returned code: %d\n", out);
    }
}

i2c.h:

// i2c.h contains header code for interfacing with i2c devices
#ifndef I2C_H
#define I2C_H

#include <Arduino.h>
#include <Wire.h>

#define WRITE_MODE 0x00
#define READ_MODE 0x80

class I2C {
    public:
        I2C(byte addr);

        virtual void reset() = 0;
        virtual byte* readBytes(uint registerAddr, uint numBytes);
    protected:
        byte addr;
};

#endif

i2c.cpp:

// i2c.cpp contains implementation code for interfacing with i2c devices
#include "i2c.h"

I2C::I2C(byte addr) {
    this->addr = addr;

    Wire.begin();
    Serial.begin();
    while(!Serial);
}

// Read bytes from a register
byte* I2C::readBytes(uint registerAddr, uint numBytes) {
    // Read chip id
    Wire.beginTransmission(this->addr);

    // Which register to read
    Wire.write(registerAddr);

    byte out = Wire.endTransmission();
    if (out) {
        Serial.printf("Failed to write with error code %d\n", out);
        return NULL;
    }

    Wire.beginTransmission(this->addr | READ_MODE);

    out = Wire.endTransmission();
    if (out) {
        Serial.printf("Failed to read with error code %d\n", out);
        return NULL;
    }

    Wire.requestFrom(this->addr, numBytes);

    byte* returnArray = new byte[numBytes];
    for (int i = 0; i < numBytes && Wire.available(); i++) {
        byte b = Wire.read();
    }

    return returnArray;
}

It does seem to be seeing all my files:

Compiling .pio\build\default\lib5c9\i2c\bme280.cpp.o
Compiling .pio\build\default\lib5c9\i2c\i2c.cpp.o
...
Archiving .pio\build\default\lib5c9\libi2c.a

I’m sure I’m missing something simple as it’s also been years since I’ve written c++, but I set up my project folder as best I could, following the docs. Does anyone have any ideas?

The error means that your Bme280 class is missing the definition of the constructor.
Adding this line to bme280.cpp will fix the error:

Bme280::Bme280(byte addr) : I2C(addr) {}

Remove this line!
The library is already located inside the project folder.

Only use symlink if it is a global library located outside the project directory.
For example see this post: Include search path redundency platformio.ini and vscode settings include path - #2 by sivar2311