first commit

This commit is contained in:
stuce-bot 2025-06-30 20:47:33 +02:00
commit 5893b00dd2
1669 changed files with 1982740 additions and 0 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"cmake.sourceDirectory": "/home/stuce/Arduino/libraries/FastLED"
}

View file

@ -0,0 +1 @@
{"requests":[{"kind":"cache","version":2},{"kind":"codemodel","version":2},{"kind":"toolchains","version":1},{"kind":"cmakeFiles","version":1}]}

68
build/CMakeCache.txt Normal file
View file

@ -0,0 +1,68 @@
# This is the CMakeCache file.
# For build in directory: /home/stuce/Arduino/build
# It was generated by CMake: /nix/store/np45wjc61sy6d021dnrgyizga8f859k5-cmake-3.31.6/bin/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//No help, variable specified on the command line.
CMAKE_BUILD_TYPE:STRING=Debug
//No help, variable specified on the command line.
CMAKE_CXX_COMPILER:FILEPATH=/run/current-system/sw/bin/g++
//No help, variable specified on the command line.
CMAKE_C_COMPILER:FILEPATH=/run/current-system/sw/bin/gcc
//No help, variable specified on the command line.
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE
//Value Computed by CMake.
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/home/stuce/Arduino/build/CMakeFiles/pkgRedirects
########################
# INTERNAL cache entries
########################
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=/home/stuce/Arduino/build
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=31
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=6
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=/nix/store/np45wjc61sy6d021dnrgyizga8f859k5-cmake-3.31.6/bin/cmake
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=/nix/store/np45wjc61sy6d021dnrgyizga8f859k5-cmake-3.31.6/bin/cpack
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=/nix/store/np45wjc61sy6d021dnrgyizga8f859k5-cmake-3.31.6/bin/ctest
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=Ninja
//Generator instance identifier.
CMAKE_GENERATOR_INSTANCE:INTERNAL=
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=/home/stuce/Arduino/libraries/FastLED
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=/nix/store/np45wjc61sy6d021dnrgyizga8f859k5-cmake-3.31.6/share/cmake-3.31

View file

@ -0,0 +1 @@
# This file is generated by cmake for dependency checking of the CMakeCache.txt file

View file

@ -0,0 +1,34 @@
Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
BME68x Library
Date : 2021/06/26
Usage : Arduino Library and example code for the BME68x family of sensors
BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,11 @@
# Bosch BME68x Library
This Arduino library wraps the [BME68x Sensor API](https://github.com/BoschSensortec/BME68x-Sensor-API) to provide a simpler experience to use the BME680 or BME688 Sensors from Bosch Sensortec.
## Supported sensors
- [BME680](https://www.bosch-sensortec.com/products/environmental-sensors/gas-sensors/bme680/)
- [BME688](https://www.bosch-sensortec.com/products/environmental-sensors/gas-sensors/bme688/)
---
### Copyright (c) 2021 Bosch Sensortec GmbH

View file

@ -0,0 +1,232 @@
/**
*
* Copyright (C) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/**
* bme688_dev_kit.ino :
* This is an example to log data using the BME688 development
* kit which has been designed to work with Adafruit ESP32 Feather Board
* For more information visit :
* https://www.bosch-sensortec.com/software-tools/software/bme688-software/
*/
#include "Arduino.h"
#include "bme68xLibrary.h"
#include "commMux.h"
#include <SdFat.h>
#include <Esp.h>
/* Macros used in BME68x_datalogger module */
#define N_KIT_SENS 8
#define SD_PIN_CS 33
#define PANIC_LED LED_BUILTIN
#define PANIC_DUR 1000
/* measurement duration */
#define MEAS_DUR 140
#define LOG_FILE_NAME "/BME688_Datalogger_Log.csv"
/* Declaration of variables */
Bme68x bme[N_KIT_SENS];
commMux commSetup[N_KIT_SENS];
uint8_t lastMeasindex[N_KIT_SENS] = {0};
bme68xData sensorData[N_KIT_SENS] = {0};
String logHeader;
uint32_t lastLogged = 0;
static SdFat sd;
/**
* @brief Initializes the sensor and hardware settings
* Initializes the SD card module
*/
void setup(void) {
Serial.begin(115200);
/* Initiate SPI communication */
commMuxBegin(Wire, SPI);
pinMode(PANIC_LED, OUTPUT);
delay(100);
/* Setting SD Card */
if (!sd.begin(SD_PIN_CS, SPI_EIGHTH_SPEED)) {
Serial.println("SD Card not found");
panicLeds();
} else {
sd.remove(LOG_FILE_NAME);
File file;
if (!file.open(LOG_FILE_NAME, (O_RDWR | O_CREAT))) {
Serial.println("Failed to open file for writing");
panicLeds();
}
/* Parameters for logging in the file */
logHeader = "TimeStamp(ms),Sensor Index,Temperature(deg "
"C),Pressure(Pa),Humidity(%),Gas Resistance(ohm),Gas "
"Index,Meas Index,idac,Status,Gas Valid,Heater Stable";
if (file.println(logHeader)) {
Serial.println(logHeader);
file.close();
} else {
panicLeds();
}
logHeader = "";
}
/* Communication interface set for all the 8 sensors in the development kit */
for (uint8_t i = 0; i < N_KIT_SENS; i++) {
commSetup[i] = commMuxSetConfig(Wire, SPI, i, commSetup[i]);
bme[i].begin(BME68X_SPI_INTF, commMuxRead, commMuxWrite, commMuxDelay,
&commSetup[i]);
if(bme[i].checkStatus()) {
Serial.println("Initializing sensor " + String(i) + " failed with error " + bme[i].statusString());
panicLeds();
}
}
/* Setting the default heater profile configuration */
for (uint8_t i = 0; i < N_KIT_SENS; i++) {
bme[i].setTPH();
/* Heater temperature in degree Celsius as per the suggested heater profile
*/
uint16_t tempProf[10] = {320, 100, 100, 100, 200, 200, 200, 320, 320, 320};
/* Multiplier to the shared heater duration */
uint16_t mulProf[10] = {5, 2, 10, 30, 5, 5, 5, 5, 5, 5};
/* Shared heating duration in milliseconds */
uint16_t sharedHeatrDur =
MEAS_DUR - (bme[i].getMeasDur(BME68X_PARALLEL_MODE) / INT64_C(1000));
bme[i].setHeaterProf(tempProf, mulProf, sharedHeatrDur, 10);
/* Parallel mode of sensor operation */
bme[i].setOpMode(BME68X_PARALLEL_MODE);
}
}
void loop(void) {
uint8_t nFieldsLeft = 0;
int16_t indexDiff;
bool newLogdata = false;
/* Control loop for data acquisition - checks if the data is available */
if ((millis() - lastLogged) >= MEAS_DUR) {
lastLogged = millis();
for (uint8_t i = 0; i < N_KIT_SENS; i++) {
if (bme[i].fetchData()) {
do {
nFieldsLeft = bme[i].getData(sensorData[i]);
/* Check if new data is received */
if (sensorData[i].status & BME68X_NEW_DATA_MSK) {
/* Inspect miss of data index */
indexDiff =
(int16_t)sensorData[i].meas_index - (int16_t)lastMeasindex[i];
if (indexDiff > 1) {
Serial.println("Skip I:" + String(i) +
", DIFF:" + String(indexDiff) +
", MI:" + String(sensorData[i].meas_index) +
", LMI:" + String(lastMeasindex[i]) +
", S:" + String(sensorData[i].status, HEX));
panicLeds();
}
lastMeasindex[i] = sensorData[i].meas_index;
logHeader += millis();
logHeader += ",";
logHeader += i;
logHeader += ",";
logHeader += sensorData[i].temperature;
logHeader += ",";
logHeader += sensorData[i].pressure;
logHeader += ",";
logHeader += sensorData[i].humidity;
logHeader += ",";
logHeader += sensorData[i].gas_resistance;
logHeader += ",";
logHeader += sensorData[i].gas_index;
logHeader += ",";
logHeader += sensorData[i].meas_index;
logHeader += ",";
logHeader += sensorData[i].idac;
logHeader += ",";
logHeader += String(sensorData[i].status, HEX);
logHeader += ",";
logHeader += sensorData[i].status & BME68X_GASM_VALID_MSK;
logHeader += ",";
logHeader += sensorData[i].status & BME68X_HEAT_STAB_MSK;
logHeader += "\r\n";
newLogdata = true;
}
} while (nFieldsLeft);
}
}
}
if (newLogdata) {
newLogdata = false;
digitalWrite(PANIC_LED, HIGH);
appendFile(logHeader);
logHeader = "";
digitalWrite(PANIC_LED, LOW);
}
}
/*!
* @brief Configuring the sensor with digital pin 13 as
* an output and toggles it at one second pace
*/
static void panicLeds(void) {
while (1) {
digitalWrite(PANIC_LED, HIGH);
delay(PANIC_DUR);
digitalWrite(PANIC_LED, LOW);
delay(PANIC_DUR);
}
}
/*!
* @brief Writing the sensor data to the log file(csv)
* @param sensorData
*/
static void writeFile(String sensorData) {
File file;
if (!file.open(LOG_FILE_NAME, (O_RDWR | O_AT_END))) {
Serial.println("Failed to open file for writing");
panicLeds();
}
if (file.print(sensorData)) {
Serial.print(sensorData);
} else {
Serial.println("Write failed");
}
file.close();
}
/*!
* @brief Appending the sensor data into the log file(csv)
* @param sensorData
*/
static void appendFile(String sensorData) {
File file;
if (!file.open(LOG_FILE_NAME, (O_RDWR | O_AT_END))) {
Serial.println("Failed to open file for appending");
panicLeds();
}
if (file.print(sensorData)) {
Serial.print(sensorData);
} else {
Serial.println("Write append");
}
file.close();
}

View file

@ -0,0 +1,155 @@
/**
Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@file commMux.cpp
@date 11 Jan 2023
@version 1.2.40408
*/
#include "commMux.h"
#define CLOCK_FREQUENCY 400000
#define COMM_SPEED 8000000
const uint8_t I2C_EXPANDER_ADDR = 0x20;
const uint8_t I2C_EXPANDER_OUTPUT_REG_ADDR = 0x01;
const uint8_t I2C_EXPANDER_OUTPUT_DESELECT = 0xFF;
const uint8_t I2C_EXPANDER_CONFIG_REG_ADDR = 0x03;
const uint8_t I2C_EXPANDER_CONFIG_REG_MASK = 0x00;
/**
* @brief Function to configure the communication across sensors
*/
commMux commMuxSetConfig(TwoWire &wireobj, SPIClass &spiobj, uint8_t idx, commMux &comm)
{
comm.select = ((0x01 << idx) ^ 0xFF);
comm.spiobj = &spiobj;
comm.wireobj = &wireobj;
return comm;
}
/**
* @brief Function to trigger the communication
*/
void commMuxBegin(TwoWire &wireobj, SPIClass &spiobj)
{
wireobj.begin();
wireobj.setClock(CLOCK_FREQUENCY);
wireobj.beginTransmission(I2C_EXPANDER_ADDR);
wireobj.write(I2C_EXPANDER_CONFIG_REG_ADDR);
wireobj.write(I2C_EXPANDER_CONFIG_REG_MASK);
wireobj.endTransmission();
spiobj.begin();
}
/**
* @brief Function to set the ship select pin of the SPI
*/
static void setChipSelect(TwoWire *wireobj, uint8_t mask)
{
// send I2C-Expander device address
wireobj->beginTransmission(I2C_EXPANDER_ADDR);
// send I2C-Expander output register address
wireobj->write(I2C_EXPANDER_OUTPUT_REG_ADDR);
// send mask to set output level of GPIO pins
wireobj->write(mask);
// end communication
wireobj->endTransmission();
}
/**
* @brief Function to write the sensor data to the register
*/
int8_t commMuxWrite(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
commMux *comm = (commMux*) intf_ptr;
uint32_t i;
if (comm)
{
setChipSelect(comm->wireobj, comm->select);
comm->spiobj->beginTransaction(SPISettings(COMM_SPEED, MSBFIRST, SPI_MODE0));
comm->spiobj->transfer(reg_addr);
for (i = 0; i < length; i++)
{
comm->spiobj->transfer(reg_data[i]);
}
comm->spiobj->endTransaction();
setChipSelect(comm->wireobj, I2C_EXPANDER_OUTPUT_DESELECT);
return 0;
}
return 1;
}
/**
* @brief Function to read the sensor data from the register
*/
int8_t commMuxRead(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
commMux *comm = (commMux*) intf_ptr;
uint32_t i;
if (comm)
{
setChipSelect(comm->wireobj, comm->select);
comm->spiobj->beginTransaction(SPISettings(COMM_SPEED, MSBFIRST, SPI_MODE0));
comm->spiobj->transfer(reg_addr);
for (i = 0; i < length; i++)
{
reg_data[i] = comm->spiobj->transfer(0xFF);
}
comm->spiobj->endTransaction();
setChipSelect(comm->wireobj, I2C_EXPANDER_OUTPUT_DESELECT);
return 0;
}
return 1;
}
/**
* @brief Function to maintain a delay between communication
*/
void commMuxDelay(uint32_t period_us, void *intf_ptr)
{
(void) intf_ptr;
delayMicroseconds(period_us);
}

View file

@ -0,0 +1,98 @@
/**
Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@file commMux.h
@date 11 Jan 2023
@version 1.2.40408
*/
#ifndef COMM_MUX_H
#define COMM_MUX_H
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
/**
* Datatype working as an interface descriptor
*/
typedef struct {
TwoWire *wireobj;
SPIClass *spiobj;
uint8_t select;
} commMux;
/**
* @brief Function to configure the communication across sensors
* @param wireobj : The TwoWire object
* @param spiobj : The SPIClass object
* @param idx : Selected sensor for communication interface
* @param comm : Structure for selected sensor
* @return : Structure holding the communication setup
*/
commMux commMuxSetConfig(TwoWire &wireobj, SPIClass &spiobj, uint8_t idx, commMux &comm);
/**
* @brief Function to trigger the communication
* @param wireobj : The TwoWire object
* @param spiobj : The SPIClass object
*/
void commMuxBegin(TwoWire &wireobj, SPIClass &spiobj);
/**
* @brief Function to write the sensor data to the register
* @param reg_addr : Address of the register
* @param reg_data : Pointer to the data to be written
* @param length : length of the register data
* @param intf_ptr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t commMuxWrite(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr);
/**
* @brief Function to read the sensor data from the register
* @param reg_addr : Address of the register
* @param reg_data : Pointer to the data to be read from the sensor
* @param length : length of the register data
* @param intf_ptr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t commMuxRead(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr);
/**
* @brief Function to maintain a delay between communication
* @param period_us : Time delay in micro secs
* @param intf_ptr : Pointer to the interface descriptor
*/
void commMuxDelay(uint32_t period_us, void *intf_ptr);
#endif /* COMM_MUX_H */

View file

@ -0,0 +1,70 @@
/**
* Copyright (C) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "Arduino.h"
#include "bme68xLibrary.h"
#ifndef PIN_CS
#define PIN_CS SS
#endif
Bme68x bme;
/**
* @brief Initializes the sensor and hardware settings
*/
void setup(void)
{
SPI.begin();
Serial.begin(115200);
while (!Serial)
delay(10);
/* initializes the sensor based on SPI library */
bme.begin(PIN_CS, SPI);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
Serial.println("TimeStamp(ms), Temperature(deg C), Pressure(Pa), Humidity(%), Gas resistance(ohm), Status");
}
void loop(void)
{
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData())
{
bme.getData(data);
Serial.print(String(millis()) + ", ");
Serial.print(String(data.temperature) + ", ");
Serial.print(String(data.pressure) + ", ");
Serial.print(String(data.humidity) + ", ");
Serial.print(String(data.gas_resistance) + ", ");
Serial.println(data.status, HEX);
}
}

View file

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(forced_mode_idf)

View file

@ -0,0 +1,20 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32@6.6.0
board = esp32dev
framework = espidf
platform_packages =
platformio/framework-espidf@~3.50201.0
lib_deps =
../../
https://github.com/natanaeljr/esp32-I2Cbus
monitor_speed = 115200

View file

@ -0,0 +1,6 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})

View file

@ -0,0 +1,111 @@
/**
* Copyright (C) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "bme68xLibrary.h"
#include "I2Cbus.hpp"
#define BME688_ADD 0x77
static const char* TAG = "main";
Bme68x bme;
int8_t read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr) {
return static_cast<I2C_t *>(intfPtr)->readBytes(BME688_ADD, a_register, len, data)==ESP_OK ? 0 : -1;
}
int8_t write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len,
void *intfPtr) {
return static_cast<I2C_t *>(intfPtr)->writeBytes(BME688_ADD, a_register, len, data)==ESP_OK ? 0 : -1;
}
uint32_t IRAM_ATTR millis() { return (uint32_t) (esp_timer_get_time() / 1000ULL); }
void IRAM_ATTR delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t IRAM_ATTR micros() { return (uint32_t) esp_timer_get_time(); }
void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
uint32_t start = micros();
const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop.
// it must be larger than the worst-case duration of a delay(1) call (hardware tasks)
// 5ms is conservative, it could be reduced when exact BT/WiFi stack delays are known
if (us > lag) {
delay((us - lag) / 1000UL); // note: in disabled-interrupt contexts delay() won't actually sleep
while (micros() - start < us - lag)
delay(1); // in those cases, this loop allows to yield for BT/WiFi stack tasks
}
while (micros() - start < us) // fine delay the remaining usecs
;
}
void delay_us(uint32_t period, void *intfPtr) {
delay_microseconds_safe(period);
}
/**
* @brief Initializes the sensor and hardware settings
*/
void setup(void)
{
i2c0.begin(GPIO_NUM_3,GPIO_NUM_0);
/* initializes the sensor based on I2C library */
bme.begin(BME68X_I2C_INTF, read_bytes_wrapper, write_bytes_wrapper, delay_us, (void *) &i2c0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
ESP_LOGI(TAG, "Sensor error: %d", bme.status);
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
ESP_LOGI(TAG, "Sensor Warning: %d", bme.status);
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
ESP_LOGI(TAG, "TimeStamp(ms), Temperature(deg C), Pressure(Pa), Humidity(%%), Gas resistance(ohm), Status");
}
void loop(void)
{
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
delay_microseconds_safe(bme.getMeasDur());
if (bme.fetchData())
{
bme.getData(data);
ESP_LOGI(TAG, "%lu, %f, %f, %f, %f, %d", millis(), data.temperature, data.pressure, data.humidity, data.gas_resistance, data.status);
}
}
void loop_task(void *pv_params) {
setup();
while (true) {
loop();
}
}
extern "C" void app_main()
{
ESP_LOGI(TAG, "starting");
xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, NULL);
}

View file

@ -0,0 +1,89 @@
/**
* Copyright (C) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "Arduino.h"
#include "bme68xLibrary.h"
#define NEW_GAS_MEAS (BME68X_GASM_VALID_MSK | BME68X_HEAT_STAB_MSK | BME68X_NEW_DATA_MSK)
#define MEAS_DUR 140
#ifndef PIN_CS
#define PIN_CS SS
#endif
Bme68x bme;
/**
* @brief Initializes the sensor and hardware settings
*/
void setup(void)
{
SPI.begin();
Serial.begin(115200);
while (!Serial)
delay(10);
/* initializes the sensor based on SPI library */
bme.begin(PIN_CS, SPI);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Heater temperature in degree Celsius */
uint16_t tempProf[10] = { 320, 100, 100, 100, 200, 200, 200, 320, 320,
320 };
/* Multiplier to the shared heater duration */
uint16_t mulProf[10] = { 5, 2, 10, 30, 5, 5, 5, 5, 5, 5 };
/* Shared heating duration in milliseconds */
uint16_t sharedHeatrDur = MEAS_DUR - (bme.getMeasDur(BME68X_PARALLEL_MODE) / 1000);
bme.setHeaterProf(tempProf, mulProf, sharedHeatrDur, 10);
bme.setOpMode(BME68X_PARALLEL_MODE);
Serial.println("TimeStamp(ms), Temperature(deg C), Pressure(Pa), Humidity(%), Gas resistance(ohm), Status, Gas index");
}
void loop(void)
{
bme68xData data;
uint8_t nFieldsLeft = 0;
/* data being fetched for every 140ms */
delay(MEAS_DUR);
if (bme.fetchData())
{
do
{
nFieldsLeft = bme.getData(data);
if (data.status == NEW_GAS_MEAS)
{
Serial.print(String(millis()) + ", ");
Serial.print(String(data.temperature) + ", ");
Serial.print(String(data.pressure) + ", ");
Serial.print(String(data.humidity) + ", ");
Serial.print(String(data.gas_resistance) + ", ");
Serial.print(String(data.status, HEX) + ", ");
Serial.println(data.gas_index);
}
} while (nFieldsLeft);
}
}

View file

@ -0,0 +1,87 @@
/**
* Copyright (C) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "Arduino.h"
#include "bme68xLibrary.h"
#define NEW_GAS_MEAS (BME68X_GASM_VALID_MSK | BME68X_HEAT_STAB_MSK | BME68X_NEW_DATA_MSK)
#ifndef PIN_CS
#define PIN_CS SS
#endif
Bme68x bme;
/**
* @brief Initializes the sensor and hardware settings
*/
void setup(void)
{
SPI.begin();
Serial.begin(115200);
while (!Serial)
delay(10);
/* Initializes the sensor based on SPI library */
bme.begin(PIN_CS, SPI);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Heater temperature in degree Celsius */
uint16_t tempProf[10] = { 100, 200, 320 };
/* Heating duration in milliseconds */
uint16_t durProf[10] = { 150, 150, 150 };
bme.setSeqSleep(BME68X_ODR_250_MS);
bme.setHeaterProf(tempProf, durProf, 3);
bme.setOpMode(BME68X_SEQUENTIAL_MODE);
Serial.println("TimeStamp(ms), Temperature(deg C), Pressure(Pa), Humidity(%), Gas resistance(ohm), Status, Gas index");
}
void loop(void)
{
bme68xData data;
uint8_t nFieldsLeft = 0;
delay(150);
if (bme.fetchData())
{
do
{
nFieldsLeft = bme.getData(data);
//if (data.status == NEW_GAS_MEAS)
{
Serial.print(String(millis()) + ", ");
Serial.print(String(data.temperature) + ", ");
Serial.print(String(data.pressure) + ", ");
Serial.print(String(data.humidity) + ", ");
Serial.print(String(data.gas_resistance) + ", ");
Serial.print(String(data.status, HEX) + ", ");
Serial.println(data.gas_index);
if(data.gas_index == 2) /* Sequential mode sleeps after this measurement */
delay(250);
}
} while (nFieldsLeft);
}
}

View file

@ -0,0 +1,58 @@
#######################################
# BME68x Keywords #
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Bme68x KEYWORD1
bme68xScommT KEYWORD1
#######################################
# Methods and Functions (KEYWORD2) #
#######################################
Bme68x KEYWORD2
begin KEYWORD2
readReg KEYWORD2
writeReg KEYWORD2
softReset KEYWORD2
setAmbientTemp KEYWORD2
getMeasDur KEYWORD2
setOpMode KEYWORD2
getOpMode KEYWORD2
getTPH KEYWORD2
setTPH KEYWORD2
getFilter KEYWORD2
setFilter KEYWORD2
getSeqSleep KEYWORD2
setSeqSleep KEYWORD2
setHeaterProf KEYWORD2
fetchData KEYWORD2
getData KEYWORD2
getSensorData KEYWORD2
getHeaterConfiguration KEYWORD2
getUniqueId KEYWORD2
intfError KEYWORD2
checkStatus KEYWORD2
statusString KEYWORD2
#######################################
# Constants (LITERAL1) #
#######################################
BME68X_SLEEP_MODE LITERAL1
BME68X_FORCED_MODE LITERAL1
BME68X_PARALLEL_MODE LITERAL1
BME68X_SEQUENTIAL_MODE LITERAL1
#######################################
# Structures (KEYWORD3) #
#######################################
bme68xData KEYWORD3
bme68xDev KEYWORD3
bme68xConf KEYWORD3
bme68xHeatrConf KEYWORD3

View file

@ -0,0 +1,19 @@
{
"name": "BME68x Sensor library",
"description": "Bosch Sensortec BME680 and BME688 sensor library",
"homepage": "https://www.bosch-sensortec.com/software-tools/software/bme688-software/",
"repository": {
"type": "git",
"url": "https://github.com/boschsensortec/Bosch-BME68x-Library"
},
"version": "1.3.40408",
"authors": {
"name": "Bosch Sensortec",
"email": "contact@bosch-sensortec.com"
},
"frameworks": "*",
"platforms": "*",
"build": {
"includeDir": "src/bme68x"
}
}

View file

@ -0,0 +1,10 @@
name=BME68x Sensor library
version=1.3.40408
author=Bosch Sensortec
maintainer=Bosch Sensortec <contact@bosch-sensortec.com>
sentence=Bosch Sensortec BME680 and BME688 sensor library
url=https://www.bosch-sensortec.com/software-tools/software/bme688-software/
paragraph=
category=Sensors
architectures=*
includes=bme68xLibrary.h

View file

@ -0,0 +1,30 @@
Copyright (c) 2023 Bosch Sensortec GmbH. All rights reserved.
BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,30 @@
# BME68X Sensor API
> Bosch Sensortec's BME680 and BME688 sensor API
## Sensor Overview
BME680 is an integrated environmental sensor developed specifically for mobile applications and wearables where size and low power consumption are key requirements. Expanding Bosch Sensortecs existing family of environmental sensors, the BME680 integrates for the first time high-linearity and high-accuracy gas, pressure, humidity and temperature sensors. It consists of an 8-pin metal-lid 3.0 x 3.0 x 0.93 mm³ LGA package which is designed for optimized consumption depending on the specific operating mode, long term stability and high EMC robustness. The gas sensor within the BME680 can detect a broad range of gases to measure air quality for personal well being. Gases that can be detected by the BME680 include Volatile Organic Compounds (VOC) from paints (such as formaldehyde), lacquers, paint strippers, cleaning supplies, furnishings, office equipment, glues, adhesives and alcohol.
### Features
- Air quality measurement
- Personalized weather station
- Context awareness, e.g. skin moisture detection, room change detection
- Fitness monitoring / well-being
- Warning regarding dryness or high temperatures
- Measurement of volume and air flow
- Home automation control (e.g. HVAC)
- GPS enhancement (e.g. time-to-first-fix improvement, dead reckoning, slope detection)
- Indoor navigation (change of floor detection, elevator detection)
- Altitude tracking and calories expenditure for sports activities
#### Important links:
For more information, please refer to:
- [BME680 Product page](https://www.bosch-sensortec.com/bst/products/all_products/bme680)
- [BME680 & BME688 Github page](https://github.com/BoschSensortec/BME68x-Sensor-API)
- [BME680 gas sensor design guide](https://community.bosch-sensortec.com/t5/Knowledge-base/BME680-gas-sensor-series-design-guide/ta-p/5952)
- [Knowledge base page](https://community.bosch-sensortec.com/t5/Knowledge-base/tkb-p/bst_community-mems-tkb)
- [Community support page](https://community.bosch-sensortec.com)
---

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
/**
* Copyright (c) 2023 Bosch Sensortec GmbH. All rights reserved.
*
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @file bme68x.h
* @date 2023-02-07
* @version v4.4.8
*
*/
/*!
* @defgroup bme68x BME68X
* @brief <a href="https://www.bosch-sensortec.com/bst/products/all_products/bme680">Product Overview</a>
* and <a href="https://github.com/BoschSensortec/BME68x-Sensor-API">Sensor API Source Code</a>
*/
#ifndef BME68X_H_
#define BME68X_H_
#include "bme68x_defs.h"
/* CPP guard */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \ingroup bme68x
* \defgroup bme68xApiInit Initialization
* @brief Initialize the sensor and device structure
*/
/*!
* \ingroup bme68xApiInit
* \page bme68x_api_bme68x_init bme68x_init
* \code
* int8_t bme68x_init(struct bme68x_dev *dev);
* \endcode
* @details This API reads the chip-id of the sensor which is the first step to
* verify the sensor and also calibrates the sensor
* As this API is the entry point, call this API before using other APIs.
*
* @param[in,out] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_init(struct bme68x_dev *dev);
/**
* \ingroup bme68x
* \defgroup bme68xApiRegister Registers
* @brief Generic API for accessing sensor registers
*/
/*!
* \ingroup bme68xApiRegister
* \page bme68x_api_bme68x_set_regs bme68x_set_regs
* \code
* int8_t bme68x_set_regs(const uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
* \endcode
* @details This API writes the given data to the register address of the sensor
*
* @param[in] reg_addr : Register addresses to where the data is to be written
* @param[in] reg_data : Pointer to data buffer which is to be written
* in the reg_addr of sensor.
* @param[in] len : No of bytes of data to write
* @param[in,out] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiRegister
* \page bme68x_api_bme68x_get_regs bme68x_get_regs
* \code
* int8_t bme68x_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
* \endcode
* @details This API reads the data from the given register address of sensor.
*
* @param[in] reg_addr : Register address from where the data to be read
* @param[out] reg_data : Pointer to data buffer to store the read data.
* @param[in] len : No of bytes of data to be read.
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev);
/**
* \ingroup bme68x
* \defgroup bme68xApiSystem System
* @brief API that performs system-level operations
*/
/*!
* \ingroup bme68xApiSystem
* \page bme68x_api_bme68x_soft_reset bme68x_soft_reset
* \code
* int8_t bme68x_soft_reset(struct bme68x_dev *dev);
* \endcode
* @details This API soft-resets the sensor.
*
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_soft_reset(struct bme68x_dev *dev);
/**
* \ingroup bme68x
* \defgroup bme68xApiOm Operation mode
* @brief API to configure operation mode
*/
/*!
* \ingroup bme68xApiOm
* \page bme68x_api_bme68x_set_op_mode bme68x_set_op_mode
* \code
* int8_t bme68x_set_op_mode(const uint8_t op_mode, struct bme68x_dev *dev);
* \endcode
* @details This API is used to set the operation mode of the sensor
* @param[in] op_mode : Desired operation mode.
* @param[in] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_set_op_mode(const uint8_t op_mode, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiOm
* \page bme68x_api_bme68x_get_op_mode bme68x_get_op_mode
* \code
* int8_t bme68x_get_op_mode(uint8_t *op_mode, struct bme68x_dev *dev);
* \endcode
* @details This API is used to get the operation mode of the sensor.
*
* @param[out] op_mode : Desired operation mode.
* @param[in,out] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_get_op_mode(uint8_t *op_mode, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiConfig
* \page bme68x_api_bme68x_get_meas_dur bme68x_get_meas_dur
* \code
* uint32_t bme68x_get_meas_dur(const uint8_t op_mode, struct bme68x_conf *conf, struct bme68x_dev *dev);
* \endcode
* @details This API is used to get the remaining duration that can be used for heating.
*
* @param[in] op_mode : Desired operation mode.
* @param[in] conf : Desired sensor configuration.
* @param[in] dev : Structure instance of bme68x_dev
*
* @return Measurement duration calculated in microseconds
*/
uint32_t bme68x_get_meas_dur(const uint8_t op_mode, struct bme68x_conf *conf, struct bme68x_dev *dev);
/**
* \ingroup bme68x
* \defgroup bme68xApiData Data Read out
* @brief Read our data from the sensor
*/
/*!
* \ingroup bme68xApiData
* \page bme68x_api_bme68x_get_data bme68x_get_data
* \code
* int8_t bme68x_get_data(uint8_t op_mode, struct bme68x_data *data, uint8_t *n_data, struct bme68x_dev *dev);
* \endcode
* @details This API reads the pressure, temperature and humidity and gas data
* from the sensor, compensates the data and store it in the bme68x_data
* structure instance passed by the user.
*
* @param[in] op_mode : Expected operation mode.
* @param[out] data : Structure instance to hold the data.
* @param[out] n_data : Number of data instances available.
* @param[in,out] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_get_data(uint8_t op_mode, struct bme68x_data *data, uint8_t *n_data, struct bme68x_dev *dev);
/**
* \ingroup bme68x
* \defgroup bme68xApiConfig Configuration
* @brief Configuration API of sensor
*/
/*!
* \ingroup bme68xApiConfig
* \page bme68x_api_bme68x_set_conf bme68x_set_conf
* \code
* int8_t bme68x_set_conf(struct bme68x_conf *conf, struct bme68x_dev *dev);
* \endcode
* @details This API is used to set the oversampling, filter and odr configuration
*
* @param[in] conf : Desired sensor configuration.
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_set_conf(struct bme68x_conf *conf, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiConfig
* \page bme68x_api_bme68x_get_conf bme68x_get_conf
* \code
* int8_t bme68x_get_conf(struct bme68x_conf *conf, struct bme68x_dev *dev);
* \endcode
* @details This API is used to get the oversampling, filter and odr
* configuration
*
* @param[out] conf : Present sensor configuration.
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_get_conf(struct bme68x_conf *conf, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiConfig
* \page bme68x_api_bme68x_set_heatr_conf bme68x_set_heatr_conf
* \code
* int8_t bme68x_set_heatr_conf(uint8_t op_mode, const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev);
* \endcode
* @details This API is used to set the gas configuration of the sensor.
*
* @param[in] op_mode : Expected operation mode of the sensor.
* @param[in] conf : Desired heating configuration.
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_set_heatr_conf(uint8_t op_mode, const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiConfig
* \page bme68x_api_bme68x_get_heatr_conf bme68x_get_heatr_conf
* \code
* int8_t bme68x_get_heatr_conf(const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev);
* \endcode
* @details This API is used to get the gas configuration of the sensor.
*
* @param[out] conf : Current configurations of the gas sensor.
* @param[in,out] dev : Structure instance of bme68x_dev.
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_get_heatr_conf(const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev);
/*!
* \ingroup bme68xApiSystem
* \page bme68x_api_bme68x_selftest_check bme68x_selftest_check
* \code
* int8_t bme68x_selftest_check(const struct bme68x_dev *dev);
* \endcode
* @details This API performs Self-test of low gas variant of BME68X
*
* @param[in, out] dev : Structure instance of bme68x_dev
*
* @return Result of API execution status
* @retval 0 -> Success
* @retval < 0 -> Fail
*/
int8_t bme68x_selftest_check(const struct bme68x_dev *dev);
#ifdef __cplusplus
}
#endif /* End of CPP guard */
#endif /* BME68X_H_ */

View file

@ -0,0 +1,972 @@
/**
* Copyright (c) 2023 Bosch Sensortec GmbH. All rights reserved.
*
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @file bme68x_defs.h
* @date 2023-02-07
* @version v4.4.8
*
*/
/*! @cond DOXYGEN_SUPRESS */
#ifndef BME68X_DEFS_H_
#define BME68X_DEFS_H_
/********************************************************* */
/*! Header includes */
/********************************************************* */
#ifdef __KERNEL__
#include <linux/types.h>
#include <linux/kernel.h>
#else
#include <stdint.h>
#include <stddef.h>
#endif
/********************************************************* */
/*! Common Macros */
/********************************************************* */
#ifdef __KERNEL__
#if !defined(UINT8_C) && !defined(INT8_C)
#define INT8_C(x) S8_C(x)
#define UINT8_C(x) U8_C(x)
#endif
#if !defined(UINT16_C) && !defined(INT16_C)
#define INT16_C(x) S16_C(x)
#define UINT16_C(x) U16_C(x)
#endif
#if !defined(INT32_C) && !defined(UINT32_C)
#define INT32_C(x) S32_C(x)
#define UINT32_C(x) U32_C(x)
#endif
#if !defined(INT64_C) && !defined(UINT64_C)
#define INT64_C(x) S64_C(x)
#define UINT64_C(x) U64_C(x)
#endif
#endif
/*! C standard macros */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *) 0)
#endif
#endif
#ifndef BME68X_DO_NOT_USE_FPU
/* Comment or un-comment the macro to provide floating point data output */
#define BME68X_USE_FPU
#endif
/* Period between two polls (value can be given by user) */
#ifndef BME68X_PERIOD_POLL
#define BME68X_PERIOD_POLL UINT32_C(10000)
#endif
/* BME68X unique chip identifier */
#define BME68X_CHIP_ID UINT8_C(0x61)
/* Period for a soft reset */
#define BME68X_PERIOD_RESET UINT32_C(10000)
/* BME68X lower I2C address */
#define BME68X_I2C_ADDR_LOW UINT8_C(0x76)
/* BME68X higher I2C address */
#define BME68X_I2C_ADDR_HIGH UINT8_C(0x77)
/* Soft reset command */
#define BME68X_SOFT_RESET_CMD UINT8_C(0xb6)
/* Return code definitions */
/* Success */
#define BME68X_OK INT8_C(0)
/* Errors */
/* Null pointer passed */
#define BME68X_E_NULL_PTR INT8_C(-1)
/* Communication failure */
#define BME68X_E_COM_FAIL INT8_C(-2)
/* Sensor not found */
#define BME68X_E_DEV_NOT_FOUND INT8_C(-3)
/* Incorrect length parameter */
#define BME68X_E_INVALID_LENGTH INT8_C(-4)
/* Self test fail error */
#define BME68X_E_SELF_TEST INT8_C(-5)
/* Warnings */
/* Define a valid operation mode */
#define BME68X_W_DEFINE_OP_MODE INT8_C(1)
/* No new data was found */
#define BME68X_W_NO_NEW_DATA INT8_C(2)
/* Define the shared heating duration */
#define BME68X_W_DEFINE_SHD_HEATR_DUR INT8_C(3)
/* Information - only available via bme68x_dev.info_msg */
#define BME68X_I_PARAM_CORR UINT8_C(1)
/* Register map addresses in I2C */
/* Register for 3rd group of coefficients */
#define BME68X_REG_COEFF3 UINT8_C(0x00)
/* 0th Field address*/
#define BME68X_REG_FIELD0 UINT8_C(0x1d)
/* 0th Current DAC address*/
#define BME68X_REG_IDAC_HEAT0 UINT8_C(0x50)
/* 0th Res heat address */
#define BME68X_REG_RES_HEAT0 UINT8_C(0x5a)
/* 0th Gas wait address */
#define BME68X_REG_GAS_WAIT0 UINT8_C(0x64)
/* Shared heating duration address */
#define BME68X_REG_SHD_HEATR_DUR UINT8_C(0x6E)
/* CTRL_GAS_0 address */
#define BME68X_REG_CTRL_GAS_0 UINT8_C(0x70)
/* CTRL_GAS_1 address */
#define BME68X_REG_CTRL_GAS_1 UINT8_C(0x71)
/* CTRL_HUM address */
#define BME68X_REG_CTRL_HUM UINT8_C(0x72)
/* CTRL_MEAS address */
#define BME68X_REG_CTRL_MEAS UINT8_C(0x74)
/* CONFIG address */
#define BME68X_REG_CONFIG UINT8_C(0x75)
/* MEM_PAGE address */
#define BME68X_REG_MEM_PAGE UINT8_C(0xf3)
/* Unique ID address */
#define BME68X_REG_UNIQUE_ID UINT8_C(0x83)
/* Register for 1st group of coefficients */
#define BME68X_REG_COEFF1 UINT8_C(0x8a)
/* Chip ID address */
#define BME68X_REG_CHIP_ID UINT8_C(0xd0)
/* Soft reset address */
#define BME68X_REG_SOFT_RESET UINT8_C(0xe0)
/* Register for 2nd group of coefficients */
#define BME68X_REG_COEFF2 UINT8_C(0xe1)
/* Variant ID Register */
#define BME68X_REG_VARIANT_ID UINT8_C(0xF0)
/* Enable/Disable macros */
/* Enable */
#define BME68X_ENABLE UINT8_C(0x01)
/* Disable */
#define BME68X_DISABLE UINT8_C(0x00)
/* Variant ID macros */
/* Low Gas variant */
#define BME68X_VARIANT_GAS_LOW UINT8_C(0x00)
/* High Gas variant */
#define BME68X_VARIANT_GAS_HIGH UINT8_C(0x01)
/* Oversampling setting macros */
/* Switch off measurement */
#define BME68X_OS_NONE UINT8_C(0)
/* Perform 1 measurement */
#define BME68X_OS_1X UINT8_C(1)
/* Perform 2 measurements */
#define BME68X_OS_2X UINT8_C(2)
/* Perform 4 measurements */
#define BME68X_OS_4X UINT8_C(3)
/* Perform 8 measurements */
#define BME68X_OS_8X UINT8_C(4)
/* Perform 16 measurements */
#define BME68X_OS_16X UINT8_C(5)
/* IIR Filter settings */
/* Switch off the filter */
#define BME68X_FILTER_OFF UINT8_C(0)
/* Filter coefficient of 2 */
#define BME68X_FILTER_SIZE_1 UINT8_C(1)
/* Filter coefficient of 4 */
#define BME68X_FILTER_SIZE_3 UINT8_C(2)
/* Filter coefficient of 8 */
#define BME68X_FILTER_SIZE_7 UINT8_C(3)
/* Filter coefficient of 16 */
#define BME68X_FILTER_SIZE_15 UINT8_C(4)
/* Filter coefficient of 32 */
#define BME68X_FILTER_SIZE_31 UINT8_C(5)
/* Filter coefficient of 64 */
#define BME68X_FILTER_SIZE_63 UINT8_C(6)
/* Filter coefficient of 128 */
#define BME68X_FILTER_SIZE_127 UINT8_C(7)
/* ODR/Standby time macros */
/* Standby time of 0.59ms */
#define BME68X_ODR_0_59_MS UINT8_C(0)
/* Standby time of 62.5ms */
#define BME68X_ODR_62_5_MS UINT8_C(1)
/* Standby time of 125ms */
#define BME68X_ODR_125_MS UINT8_C(2)
/* Standby time of 250ms */
#define BME68X_ODR_250_MS UINT8_C(3)
/* Standby time of 500ms */
#define BME68X_ODR_500_MS UINT8_C(4)
/* Standby time of 1s */
#define BME68X_ODR_1000_MS UINT8_C(5)
/* Standby time of 10ms */
#define BME68X_ODR_10_MS UINT8_C(6)
/* Standby time of 20ms */
#define BME68X_ODR_20_MS UINT8_C(7)
/* No standby time */
#define BME68X_ODR_NONE UINT8_C(8)
/* Operating mode macros */
/* Sleep operation mode */
#define BME68X_SLEEP_MODE UINT8_C(0)
/* Forced operation mode */
#define BME68X_FORCED_MODE UINT8_C(1)
/* Parallel operation mode */
#define BME68X_PARALLEL_MODE UINT8_C(2)
/* Sequential operation mode */
#define BME68X_SEQUENTIAL_MODE UINT8_C(3)
/* SPI page macros */
/* SPI memory page 0 */
#define BME68X_MEM_PAGE0 UINT8_C(0x10)
/* SPI memory page 1 */
#define BME68X_MEM_PAGE1 UINT8_C(0x00)
/* Coefficient index macros */
/* Length for all coefficients */
#define BME68X_LEN_COEFF_ALL UINT8_C(42)
/* Length for 1st group of coefficients */
#define BME68X_LEN_COEFF1 UINT8_C(23)
/* Length for 2nd group of coefficients */
#define BME68X_LEN_COEFF2 UINT8_C(14)
/* Length for 3rd group of coefficients */
#define BME68X_LEN_COEFF3 UINT8_C(5)
/* Length of the field */
#define BME68X_LEN_FIELD UINT8_C(17)
/* Length between two fields */
#define BME68X_LEN_FIELD_OFFSET UINT8_C(17)
/* Length of the configuration register */
#define BME68X_LEN_CONFIG UINT8_C(5)
/* Length of the interleaved buffer */
#define BME68X_LEN_INTERLEAVE_BUFF UINT8_C(20)
/* Coefficient index macros */
/* Coefficient T2 LSB position */
#define BME68X_IDX_T2_LSB (0)
/* Coefficient T2 MSB position */
#define BME68X_IDX_T2_MSB (1)
/* Coefficient T3 position */
#define BME68X_IDX_T3 (2)
/* Coefficient P1 LSB position */
#define BME68X_IDX_P1_LSB (4)
/* Coefficient P1 MSB position */
#define BME68X_IDX_P1_MSB (5)
/* Coefficient P2 LSB position */
#define BME68X_IDX_P2_LSB (6)
/* Coefficient P2 MSB position */
#define BME68X_IDX_P2_MSB (7)
/* Coefficient P3 position */
#define BME68X_IDX_P3 (8)
/* Coefficient P4 LSB position */
#define BME68X_IDX_P4_LSB (10)
/* Coefficient P4 MSB position */
#define BME68X_IDX_P4_MSB (11)
/* Coefficient P5 LSB position */
#define BME68X_IDX_P5_LSB (12)
/* Coefficient P5 MSB position */
#define BME68X_IDX_P5_MSB (13)
/* Coefficient P7 position */
#define BME68X_IDX_P7 (14)
/* Coefficient P6 position */
#define BME68X_IDX_P6 (15)
/* Coefficient P8 LSB position */
#define BME68X_IDX_P8_LSB (18)
/* Coefficient P8 MSB position */
#define BME68X_IDX_P8_MSB (19)
/* Coefficient P9 LSB position */
#define BME68X_IDX_P9_LSB (20)
/* Coefficient P9 MSB position */
#define BME68X_IDX_P9_MSB (21)
/* Coefficient P10 position */
#define BME68X_IDX_P10 (22)
/* Coefficient H2 MSB position */
#define BME68X_IDX_H2_MSB (23)
/* Coefficient H2 LSB position */
#define BME68X_IDX_H2_LSB (24)
/* Coefficient H1 LSB position */
#define BME68X_IDX_H1_LSB (24)
/* Coefficient H1 MSB position */
#define BME68X_IDX_H1_MSB (25)
/* Coefficient H3 position */
#define BME68X_IDX_H3 (26)
/* Coefficient H4 position */
#define BME68X_IDX_H4 (27)
/* Coefficient H5 position */
#define BME68X_IDX_H5 (28)
/* Coefficient H6 position */
#define BME68X_IDX_H6 (29)
/* Coefficient H7 position */
#define BME68X_IDX_H7 (30)
/* Coefficient T1 LSB position */
#define BME68X_IDX_T1_LSB (31)
/* Coefficient T1 MSB position */
#define BME68X_IDX_T1_MSB (32)
/* Coefficient GH2 LSB position */
#define BME68X_IDX_GH2_LSB (33)
/* Coefficient GH2 MSB position */
#define BME68X_IDX_GH2_MSB (34)
/* Coefficient GH1 position */
#define BME68X_IDX_GH1 (35)
/* Coefficient GH3 position */
#define BME68X_IDX_GH3 (36)
/* Coefficient res heat value position */
#define BME68X_IDX_RES_HEAT_VAL (37)
/* Coefficient res heat range position */
#define BME68X_IDX_RES_HEAT_RANGE (39)
/* Coefficient range switching error position */
#define BME68X_IDX_RANGE_SW_ERR (41)
/* Gas measurement macros */
/* Disable gas measurement */
#define BME68X_DISABLE_GAS_MEAS UINT8_C(0x00)
/* Enable gas measurement low */
#define BME68X_ENABLE_GAS_MEAS_L UINT8_C(0x01)
/* Enable gas measurement high */
#define BME68X_ENABLE_GAS_MEAS_H UINT8_C(0x02)
/* Heater control macros */
/* Enable heater */
#define BME68X_ENABLE_HEATER UINT8_C(0x00)
/* Disable heater */
#define BME68X_DISABLE_HEATER UINT8_C(0x01)
#ifdef BME68X_USE_FPU
/* 0 degree Celsius */
#define BME68X_MIN_TEMPERATURE INT16_C(0)
/* 60 degree Celsius */
#define BME68X_MAX_TEMPERATURE INT16_C(60)
/* 900 hecto Pascals */
#define BME68X_MIN_PRESSURE UINT32_C(90000)
/* 1100 hecto Pascals */
#define BME68X_MAX_PRESSURE UINT32_C(110000)
/* 20% relative humidity */
#define BME68X_MIN_HUMIDITY UINT32_C(20)
/* 80% relative humidity*/
#define BME68X_MAX_HUMIDITY UINT32_C(80)
#else
/* 0 degree Celsius */
#define BME68X_MIN_TEMPERATURE INT16_C(0)
/* 60 degree Celsius */
#define BME68X_MAX_TEMPERATURE INT16_C(6000)
/* 900 hecto Pascals */
#define BME68X_MIN_PRESSURE UINT32_C(90000)
/* 1100 hecto Pascals */
#define BME68X_MAX_PRESSURE UINT32_C(110000)
/* 20% relative humidity */
#define BME68X_MIN_HUMIDITY UINT32_C(20000)
/* 80% relative humidity*/
#define BME68X_MAX_HUMIDITY UINT32_C(80000)
#endif
#define BME68X_HEATR_DUR1 UINT16_C(1000)
#define BME68X_HEATR_DUR2 UINT16_C(2000)
#define BME68X_HEATR_DUR1_DELAY UINT32_C(1000000)
#define BME68X_HEATR_DUR2_DELAY UINT32_C(2000000)
#define BME68X_N_MEAS UINT8_C(6)
#define BME68X_LOW_TEMP UINT8_C(150)
#define BME68X_HIGH_TEMP UINT16_C(350)
/* Mask macros */
/* Mask for number of conversions */
#define BME68X_NBCONV_MSK UINT8_C(0X0f)
/* Mask for IIR filter */
#define BME68X_FILTER_MSK UINT8_C(0X1c)
/* Mask for ODR[3] */
#define BME68X_ODR3_MSK UINT8_C(0x80)
/* Mask for ODR[2:0] */
#define BME68X_ODR20_MSK UINT8_C(0xe0)
/* Mask for temperature oversampling */
#define BME68X_OST_MSK UINT8_C(0Xe0)
/* Mask for pressure oversampling */
#define BME68X_OSP_MSK UINT8_C(0X1c)
/* Mask for humidity oversampling */
#define BME68X_OSH_MSK UINT8_C(0X07)
/* Mask for heater control */
#define BME68X_HCTRL_MSK UINT8_C(0x08)
/* Mask for run gas */
#define BME68X_RUN_GAS_MSK UINT8_C(0x30)
/* Mask for operation mode */
#define BME68X_MODE_MSK UINT8_C(0x03)
/* Mask for res heat range */
#define BME68X_RHRANGE_MSK UINT8_C(0x30)
/* Mask for range switching error */
#define BME68X_RSERROR_MSK UINT8_C(0xf0)
/* Mask for new data */
#define BME68X_NEW_DATA_MSK UINT8_C(0x80)
/* Mask for gas index */
#define BME68X_GAS_INDEX_MSK UINT8_C(0x0f)
/* Mask for gas range */
#define BME68X_GAS_RANGE_MSK UINT8_C(0x0f)
/* Mask for gas measurement valid */
#define BME68X_GASM_VALID_MSK UINT8_C(0x20)
/* Mask for heater stability */
#define BME68X_HEAT_STAB_MSK UINT8_C(0x10)
/* Mask for SPI memory page */
#define BME68X_MEM_PAGE_MSK UINT8_C(0x10)
/* Mask for reading a register in SPI */
#define BME68X_SPI_RD_MSK UINT8_C(0x80)
/* Mask for writing a register in SPI */
#define BME68X_SPI_WR_MSK UINT8_C(0x7f)
/* Mask for the H1 calibration coefficient */
#define BME68X_BIT_H1_DATA_MSK UINT8_C(0x0f)
/* Position macros */
/* Filter bit position */
#define BME68X_FILTER_POS UINT8_C(2)
/* Temperature oversampling bit position */
#define BME68X_OST_POS UINT8_C(5)
/* Pressure oversampling bit position */
#define BME68X_OSP_POS UINT8_C(2)
/* ODR[3] bit position */
#define BME68X_ODR3_POS UINT8_C(7)
/* ODR[2:0] bit position */
#define BME68X_ODR20_POS UINT8_C(5)
/* Run gas bit position */
#define BME68X_RUN_GAS_POS UINT8_C(4)
/* Heater control bit position */
#define BME68X_HCTRL_POS UINT8_C(3)
/* Macro to combine two 8 bit data's to form a 16 bit data */
#define BME68X_CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb)
/* Macro to set bits */
#define BME68X_SET_BITS(reg_data, bitname, data) \
((reg_data & ~(bitname##_MSK)) | \
((data << bitname##_POS) & bitname##_MSK))
/* Macro to get bits */
#define BME68X_GET_BITS(reg_data, bitname) ((reg_data & (bitname##_MSK)) >> \
(bitname##_POS))
/* Macro to set bits starting from position 0 */
#define BME68X_SET_BITS_POS_0(reg_data, bitname, data) \
((reg_data & ~(bitname##_MSK)) | \
(data & bitname##_MSK))
/* Macro to get bits starting from position 0 */
#define BME68X_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK))
/**
* BME68X_INTF_RET_TYPE is the read/write interface return type which can be overwritten by the build system.
* The default is set to int8_t.
*/
#ifndef BME68X_INTF_RET_TYPE
#define BME68X_INTF_RET_TYPE int8_t
#endif
/**
* BME68X_INTF_RET_SUCCESS is the success return value read/write interface return type which can be
* overwritten by the build system. The default is set to 0. It is used to check for a successful
* execution of the read/write functions
*/
#ifndef BME68X_INTF_RET_SUCCESS
#define BME68X_INTF_RET_SUCCESS INT8_C(0)
#endif
/********************************************************* */
/*! Function Pointers */
/********************************************************* */
/*!
* @brief Bus communication function pointer which should be mapped to
* the platform specific read functions of the user
*
* @param[in] reg_addr : 8bit register address of the sensor
* @param[out] reg_data : Data from the specified address
* @param[in] length : Length of the reg_data array
* @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors
* for interface related callbacks
* @retval 0 for Success
* @retval Non-zero for Failure
*/
typedef BME68X_INTF_RET_TYPE (*bme68x_read_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t length,
void *intf_ptr);
/*!
* @brief Bus communication function pointer which should be mapped to
* the platform specific write functions of the user
*
* @param[in] reg_addr : 8bit register address of the sensor
* @param[out] reg_data : Data to the specified address
* @param[in] length : Length of the reg_data array
* @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors
* for interface related callbacks
* @retval 0 for Success
* @retval Non-zero for Failure
*
*/
typedef BME68X_INTF_RET_TYPE (*bme68x_write_fptr_t)(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length,
void *intf_ptr);
/*!
* @brief Delay function pointer which should be mapped to
* delay function of the user
*
* @param period - The time period in microseconds
* @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors
* for interface related callbacks
*/
typedef void (*bme68x_delay_us_fptr_t)(uint32_t period, void *intf_ptr);
/*
* @brief Generic communication function pointer
* @param[in] dev_id: Place holder to store the id of the device structure
* Can be used to store the index of the Chip select or
* I2C address of the device.
* @param[in] reg_addr: Used to select the register the where data needs to
* be read from or written to.
* @param[in,out] reg_data: Data array to read/write
* @param[in] len: Length of the data array
*/
/*
* @brief Interface selection Enumerations
*/
enum bme68x_intf {
/*! SPI interface */
BME68X_SPI_INTF,
/*! I2C interface */
BME68X_I2C_INTF
};
/* Structure definitions */
/*
* @brief Sensor field data structure
*/
struct bme68x_data
{
/*! Contains new_data, gasm_valid & heat_stab */
uint8_t status;
/*! The index of the heater profile used */
uint8_t gas_index;
/*! Measurement index to track order */
uint8_t meas_index;
/*! Heater resistance */
uint8_t res_heat;
/*! Current DAC */
uint8_t idac;
/*! Gas wait period */
uint8_t gas_wait;
#ifndef BME68X_USE_FPU
/*! Temperature in degree celsius x100 */
int16_t temperature;
/*! Pressure in Pascal */
uint32_t pressure;
/*! Humidity in % relative humidity x1000 */
uint32_t humidity;
/*! Gas resistance in Ohms */
uint32_t gas_resistance;
#else
/*! Temperature in degree celsius */
float temperature;
/*! Pressure in Pascal */
float pressure;
/*! Humidity in % relative humidity x1000 */
float humidity;
/*! Gas resistance in Ohms */
float gas_resistance;
#endif
};
/*
* @brief Structure to hold the calibration coefficients
*/
struct bme68x_calib_data
{
/*! Calibration coefficient for the humidity sensor */
uint16_t par_h1;
/*! Calibration coefficient for the humidity sensor */
uint16_t par_h2;
/*! Calibration coefficient for the humidity sensor */
int8_t par_h3;
/*! Calibration coefficient for the humidity sensor */
int8_t par_h4;
/*! Calibration coefficient for the humidity sensor */
int8_t par_h5;
/*! Calibration coefficient for the humidity sensor */
uint8_t par_h6;
/*! Calibration coefficient for the humidity sensor */
int8_t par_h7;
/*! Calibration coefficient for the gas sensor */
int8_t par_gh1;
/*! Calibration coefficient for the gas sensor */
int16_t par_gh2;
/*! Calibration coefficient for the gas sensor */
int8_t par_gh3;
/*! Calibration coefficient for the temperature sensor */
uint16_t par_t1;
/*! Calibration coefficient for the temperature sensor */
int16_t par_t2;
/*! Calibration coefficient for the temperature sensor */
int8_t par_t3;
/*! Calibration coefficient for the pressure sensor */
uint16_t par_p1;
/*! Calibration coefficient for the pressure sensor */
int16_t par_p2;
/*! Calibration coefficient for the pressure sensor */
int8_t par_p3;
/*! Calibration coefficient for the pressure sensor */
int16_t par_p4;
/*! Calibration coefficient for the pressure sensor */
int16_t par_p5;
/*! Calibration coefficient for the pressure sensor */
int8_t par_p6;
/*! Calibration coefficient for the pressure sensor */
int8_t par_p7;
/*! Calibration coefficient for the pressure sensor */
int16_t par_p8;
/*! Calibration coefficient for the pressure sensor */
int16_t par_p9;
/*! Calibration coefficient for the pressure sensor */
uint8_t par_p10;
#ifndef BME68X_USE_FPU
/*! Variable to store the intermediate temperature coefficient */
int32_t t_fine;
#else
/*! Variable to store the intermediate temperature coefficient */
float t_fine;
#endif
/*! Heater resistance range coefficient */
uint8_t res_heat_range;
/*! Heater resistance value coefficient */
int8_t res_heat_val;
/*! Gas resistance range switching error coefficient */
int8_t range_sw_err;
};
/*
* @brief BME68X sensor settings structure which comprises of ODR,
* over-sampling and filter settings.
*/
struct bme68x_conf
{
/*! Humidity oversampling. Refer @ref osx*/
uint8_t os_hum;
/*! Temperature oversampling. Refer @ref osx */
uint8_t os_temp;
/*! Pressure oversampling. Refer @ref osx */
uint8_t os_pres;
/*! Filter coefficient. Refer @ref filter*/
uint8_t filter;
/*!
* Standby time between sequential mode measurement profiles.
* Refer @ref odr
*/
uint8_t odr;
};
/*
* @brief BME68X gas heater configuration
*/
struct bme68x_heatr_conf
{
/*! Enable gas measurement. Refer @ref en_dis */
uint8_t enable;
/*! Store the heater temperature for forced mode degree Celsius */
uint16_t heatr_temp;
/*! Store the heating duration for forced mode in milliseconds */
uint16_t heatr_dur;
/*! Store the heater temperature profile in degree Celsius */
uint16_t *heatr_temp_prof;
/*! Store the heating duration profile in milliseconds */
uint16_t *heatr_dur_prof;
/*! Variable to store the length of the heating profile */
uint8_t profile_len;
/*!
* Variable to store heating duration for parallel mode
* in milliseconds
*/
uint16_t shared_heatr_dur;
};
/*
* @brief BME68X device structure
*/
struct bme68x_dev
{
/*! Chip Id */
uint8_t chip_id;
/*!
* The interface pointer is used to enable the user
* to link their interface descriptors for reference during the
* implementation of the read and write interfaces to the
* hardware.
*/
void *intf_ptr;
/*!
* Variant id
* ----------------------------------------
* Value | Variant
* ----------------------------------------
* 0 | BME68X_VARIANT_GAS_LOW
* 1 | BME68X_VARIANT_GAS_HIGH
* ----------------------------------------
*/
uint32_t variant_id;
/*! SPI/I2C interface */
enum bme68x_intf intf;
/*! Memory page used */
uint8_t mem_page;
/*! Ambient temperature in Degree C*/
int8_t amb_temp;
/*! Sensor calibration data */
struct bme68x_calib_data calib;
/*! Read function pointer */
bme68x_read_fptr_t read;
/*! Write function pointer */
bme68x_write_fptr_t write;
/*! Delay function pointer */
bme68x_delay_us_fptr_t delay_us;
/*! To store interface pointer error */
BME68X_INTF_RET_TYPE intf_rslt;
/*! Store the info messages */
uint8_t info_msg;
};
#endif /* BME68X_DEFS_H_ */
/*! @endcond */

View file

@ -0,0 +1,674 @@
/**
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
*
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @file bme68xLibrary.cpp
* @date 11 Jan 2023
* @version 1.2.40408
*
*/
#include <string.h>
#include "bme68xLibrary.h"
/* Maximum transaction size. Field size 17 x 3 */
#define BME68X_MAX_READ_LENGTH 51
#ifdef ARDUINO_ARCH_ESP32
#define BME68X_I2C_BUFFER_SIZE I2C_BUFFER_LENGTH
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_MBED
/* Assuming all MBED implementations of Wire have 256 byte sized buffers */
/* The buffer size needs to be modifed by the user as per the hardware they are using */
/* Check I2C buffer length in Wire.h of used board in ardunio library folder under wire/src */
#define BME68X_I2C_BUFFER_SIZE 256
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_ESP8266
#define BME68X_I2C_BUFFER_SIZE BUFFER_LENGTH
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_AVR
#define BME68X_I2C_BUFFER_SIZE BUFFER_LENGTH
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_NRF52
#define BME68X_I2C_BUFFER_SIZE SERIAL_BUFFER_SIZE
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_SAMD
/* Assuming all Arduino's and Adafruit's SAMD
* implementations of Wire have 256 byte sized buffers */
#define BME68X_I2C_BUFFER_SIZE 256
#define BME68X_BURST_SPI_TRANSFER
#endif
#ifdef ARDUINO_ARCH_SAM
#define BME68X_I2C_BUFFER_SIZE BUFFER_LENGTH
#define BME68X_BURST_SPI_TRANSFER
#endif
/* Optimistically assume support for at least 64 byte reads */
#ifndef BME68X_I2C_BUFFER_SIZE
#define BME68X_I2C_BUFFER_SIZE 64
#endif
#if BME68X_MAX_READ_LENGTH > BME68X_I2C_BUFFER_SIZE
#warning "Wire read requires a larger buffer size. Use SPI"
#endif
Bme68x::Bme68x(void)
{
#ifdef ARDUINO
comm.i2c.wireobj = NULL;
comm.i2c.i2cAddr = 0;
comm.spi.spiobj = NULL;
comm.spi.cs = 0;
#endif
status = BME68X_OK;
memset(&bme6, 0, sizeof(bme6));
memset(&conf, 0, sizeof(conf));
memset(&heatrConf, 0, sizeof(heatrConf));
memset(sensorData, 0, sizeof(sensorData));
bme6.amb_temp = 25; /* Typical room temperature in Celsius */
nFields = 0;
iFields = 0;
lastOpMode = BME68X_SLEEP_MODE;
}
/**
* @brief Function to initialize the sensor based on custom callbacks
*/
void Bme68x::begin(bme68xIntf intf, bme68x_read_fptr_t read, bme68x_write_fptr_t write,
bme68x_delay_us_fptr_t idleTask, void *intfPtr)
{
bme6.intf = intf;
bme6.read = read;
bme6.write = write;
bme6.delay_us = idleTask;
bme6.intf_ptr = intfPtr;
bme6.amb_temp = 25;
status = bme68x_init(&bme6);
}
/**
* @brief Function to initialize the sensor based on the Wire library
*/
#ifdef ARDUINO
void Bme68x::begin(uint8_t i2cAddr, TwoWire &i2c, bme68x_delay_us_fptr_t idleTask)
{
comm.i2c.i2cAddr = i2cAddr;
comm.i2c.wireobj = &i2c;
bme6.intf = BME68X_I2C_INTF;
bme6.read = bme68xI2cRead;
bme6.write = bme68xI2cWrite;
bme6.delay_us = idleTask;
bme6.intf_ptr = &comm;
bme6.amb_temp = 25;
status = bme68x_init(&bme6);
}
/**
* @brief Function to initialize the sensor based on the SPI library
*/
void Bme68x::begin(uint8_t chipSelect, SPIClass &spi, bme68x_delay_us_fptr_t idleTask)
{
pinMode(chipSelect, OUTPUT);
digitalWrite(chipSelect, HIGH);
delay(1);
digitalWrite(chipSelect, LOW); /* Switch to SPI with a dummy transaction */
delay(1);
digitalWrite(chipSelect, HIGH);
comm.spi.cs = chipSelect;
comm.spi.spiobj = &spi;
bme6.intf = BME68X_SPI_INTF;
bme6.read = bme68xSpiRead;
bme6.write = bme68xSpiWrite;
bme6.delay_us = idleTask;
bme6.intf_ptr = &comm;
bme6.amb_temp = 25;
status = bme68x_init(&bme6);
}
#endif
/**
* @brief Function to read a register
*/
uint8_t Bme68x::readReg(uint8_t regAddr)
{
uint8_t regData;
readReg(regAddr, &regData, 1);
return regData;
}
/**
* @brief Function to read multiple registers
*/
void Bme68x::readReg(uint8_t regAddr, uint8_t *regData, uint32_t length)
{
status = bme68x_get_regs(regAddr, regData, length, &bme6);
}
/**
* @brief Function to write data to a register
*/
void Bme68x::writeReg(uint8_t regAddr, uint8_t regData)
{
status = bme68x_set_regs(&regAddr, &regData, 1, &bme6);
}
/**
* @brief Function to write multiple registers
*/
void Bme68x::writeReg(uint8_t *regAddr, const uint8_t *regData, uint32_t length)
{
status = bme68x_set_regs(regAddr, regData, length, &bme6);
}
/**
* @brief Function to trigger a soft reset
*/
void Bme68x::softReset(void)
{
status = bme68x_soft_reset(&bme6);
}
/**
* @brief Function to set the ambient temperature for better configuration
*/
void Bme68x::setAmbientTemp(int8_t temp)
{
bme6.amb_temp = temp;
}
/**
* @brief Function to get the measurement duration in microseconds
*/
uint32_t Bme68x::getMeasDur(uint8_t opMode)
{
if (opMode == BME68X_SLEEP_MODE)
opMode = lastOpMode;
return bme68x_get_meas_dur(opMode, &conf, &bme6);
}
/**
* @brief Function to set the operation mode
*/
void Bme68x::setOpMode(uint8_t opMode)
{
status = bme68x_set_op_mode(opMode, &bme6);
if ((status == BME68X_OK) && (opMode != BME68X_SLEEP_MODE))
lastOpMode = opMode;
}
/**
* @brief Function to get the operation mode
*/
uint8_t Bme68x::getOpMode(void)
{
uint8_t opMode;
status = bme68x_get_op_mode(&opMode, &bme6);
return opMode;
}
/**
* @brief Function to get the Temperature, Pressure and Humidity over-sampling
*/
void Bme68x::getTPH(uint8_t &osHum, uint8_t &osTemp, uint8_t &osPres)
{
status = bme68x_get_conf(&conf, &bme6);
if (status == BME68X_OK)
{
osHum = conf.os_hum;
osTemp = conf.os_temp;
osPres = conf.os_pres;
}
}
/**
* @brief Function to set the Temperature, Pressure and Humidity over-sampling
*/
void Bme68x::setTPH(uint8_t osTemp, uint8_t osPres, uint8_t osHum)
{
status = bme68x_get_conf(&conf, &bme6);
if (status == BME68X_OK)
{
conf.os_hum = osHum;
conf.os_temp = osTemp;
conf.os_pres = osPres;
status = bme68x_set_conf(&conf, &bme6);
}
}
/**
* @brief Function to get the filter configuration
*/
uint8_t Bme68x::getFilter(void)
{
status = bme68x_get_conf(&conf, &bme6);
return conf.filter;
}
/**
* @brief Function to set the filter configuration
*/
void Bme68x::setFilter(uint8_t filter)
{
status = bme68x_get_conf(&conf, &bme6);
if (status == BME68X_OK)
{
conf.filter = filter;
status = bme68x_set_conf(&conf, &bme6);
}
}
/**
* @brief Function to get the sleep duration during Sequential mode
*/
uint8_t Bme68x::getSeqSleep(void)
{
status = bme68x_get_conf(&conf, &bme6);
return conf.odr;
}
/**
* @brief Function to set the sleep duration during Sequential mode
*/
void Bme68x::setSeqSleep(uint8_t odr)
{
status = bme68x_get_conf(&conf, &bme6);
if (status == BME68X_OK)
{
conf.odr = odr;
status = bme68x_set_conf(&conf, &bme6);
}
}
/**
* @brief Function to set the heater profile for Forced mode
*/
void Bme68x::setHeaterProf(uint16_t temp, uint16_t dur)
{
heatrConf.enable = BME68X_ENABLE;
heatrConf.heatr_temp = temp;
heatrConf.heatr_dur = dur;
status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &heatrConf, &bme6);
}
/**
* @brief Function to set the heater profile for Sequential mode
*/
void Bme68x::setHeaterProf(uint16_t *temp, uint16_t *dur, uint8_t profileLen)
{
heatrConf.enable = BME68X_ENABLE;
heatrConf.heatr_temp_prof = temp;
heatrConf.heatr_dur_prof = dur;
heatrConf.profile_len = profileLen;
status = bme68x_set_heatr_conf(BME68X_SEQUENTIAL_MODE, &heatrConf, &bme6);
}
/**
* @brief Function to set the heater profile for Parallel mode
*/
void Bme68x::setHeaterProf(uint16_t *temp, uint16_t *mul, uint16_t sharedHeatrDur, uint8_t profileLen)
{
heatrConf.enable = BME68X_ENABLE;
heatrConf.heatr_temp_prof = temp;
heatrConf.heatr_dur_prof = mul;
heatrConf.shared_heatr_dur = sharedHeatrDur;
heatrConf.profile_len = profileLen;
status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &heatrConf, &bme6);
}
/**
* @brief Function to fetch data from the sensor into the local buffer
*/
uint8_t Bme68x::fetchData(void)
{
nFields = 0;
status = bme68x_get_data(lastOpMode, sensorData, &nFields, &bme6);
iFields = 0;
return nFields;
}
/**
* @brief Function to get a single data field
*/
uint8_t Bme68x::getData(bme68xData &data)
{
if (lastOpMode == BME68X_FORCED_MODE)
{
data = sensorData[0];
} else
{
if (nFields)
{
/* iFields spans from 0-2 while nFields spans from
* 0-3, where 0 means that there is no new data
*/
data = sensorData[iFields];
iFields++;
/* Limit reading continuously to the last fields read */
if (iFields >= nFields)
{
iFields = nFields - 1;
return 0;
}
/* Indicate if there is something left to read */
return nFields - iFields;
}
}
return 0;
}
/**
* @brief Function to get whole sensor data
*/
bme68xData* Bme68x::getAllData(void)
{
return sensorData;
}
/**
* @brief Function to get the BME68x heater configuration
*/
const bme68xHeatrConf& Bme68x::getHeaterConfiguration(void)
{
return heatrConf;
}
/**
* @brief Function to retrieve the sensor's unique ID
*/
uint32_t Bme68x::getUniqueId(void)
{
uint8_t id_regs[4];
uint32_t uid;
readReg(BME68X_REG_UNIQUE_ID, id_regs, 4);
uint32_t id1 = ((uint32_t) id_regs[3] + ((uint32_t) id_regs[2] << 8)) & 0x7fff;
uid = (id1 << 16) + (((uint32_t) id_regs[1]) << 8) + (uint32_t) id_regs[0];
return uid;
}
/**
* @brief Function to get the error code of the interface functions
*/
BME68X_INTF_RET_TYPE Bme68x::intfError(void)
{
return bme6.intf_rslt;
}
/**
* @brief Function to check if an error / warning has occurred
*/
int8_t Bme68x::checkStatus(void)
{
if (status < BME68X_OK)
{
return BME68X_ERROR;
}
else if(status > BME68X_OK)
{
return BME68X_WARNING;
}
else
{
return BME68X_OK;
}
}
#ifdef ARDUINO
/**
* @brief Function to get a brief text description of the error
*/
String Bme68x::statusString(void)
{
String ret = "";
switch (status)
{
case BME68X_OK:
/* Don't return a text for OK */
break;
case BME68X_E_NULL_PTR:
ret = "Null pointer";
break;
case BME68X_E_COM_FAIL:
ret = "Communication failure";
break;
case BME68X_E_DEV_NOT_FOUND:
ret = "Sensor not found";
break;
case BME68X_E_INVALID_LENGTH:
ret = "Invalid length";
break;
case BME68X_W_DEFINE_OP_MODE:
ret = "Set the operation mode";
break;
case BME68X_W_NO_NEW_DATA:
ret = "No new data";
break;
case BME68X_W_DEFINE_SHD_HEATR_DUR:
ret = "Set the shared heater duration";
break;
default:
ret = "Undefined error code";
}
return ret;
}
#ifdef ARDUINO
/**
* @brief Function that implements the default microsecond delay callback
*/
void bme68xDelayUs(uint32_t periodUs, void *intfPtr) {
(void) intfPtr;
delayMicroseconds(periodUs);
}
#endif
/**
* @brief Function that implements the default SPI write transaction
*/
int8_t bme68xSpiWrite(uint8_t regAddr, const uint8_t *regData,
uint32_t length, void *intfPtr) {
bme68xScommT *comm = NULL;
if (intfPtr) {
comm = (bme68xScommT *) intfPtr;
if (comm->spi.spiobj) {
digitalWrite(comm->spi.cs, LOW);
comm->spi.spiobj->transfer(regAddr);
#ifdef BME68X_BURST_SPI_TRANSFER
comm->spi.spiobj->transfer((uint8_t *)regData, length);
#else
for(uint32_t i = 0; i < length; i++) {
comm->spi.spiobj->transfer(regData[i]);
}
#endif
digitalWrite(comm->spi.cs, HIGH);
} else {
return BME68X_E_NULL_PTR;
}
} else {
return BME68X_E_NULL_PTR;
}
return BME68X_OK;
}
/**
* @brief Function that implements the default SPI read transaction
*/
int8_t bme68xSpiRead(uint8_t regAddr, uint8_t *regData, uint32_t length,
void *intfPtr) {
bme68xScommT *comm = NULL;
if (intfPtr) {
comm = (bme68xScommT *) intfPtr;
if (comm->spi.spiobj) {
digitalWrite(comm->spi.cs, LOW);
comm->spi.spiobj->transfer(regAddr);
memset(regData, 0xFF, length);
#ifdef BME68X_BURST_SPI_TRANSFER
comm->spi.spiobj->transfer(regData, length);
#else
for(uint32_t i = 0; i < length; i++) {
regData[i] = comm->spi.spiobj->transfer(0xFF);
}
#endif
digitalWrite(comm->spi.cs, HIGH);
} else {
return BME68X_E_NULL_PTR;
}
} else {
return BME68X_E_NULL_PTR;
}
return BME68X_OK;
}
/**
* @brief Function that implements the default I2C write transaction
*/
int8_t bme68xI2cWrite(uint8_t regAddr, const uint8_t *regData,
uint32_t length, void *intfPtr) {
uint32_t i;
int8_t rslt = BME68X_OK;
bme68xScommT *comm = NULL;
#ifdef BME68X_I2C_BUFFER_SIZE
if (length + 1 > BME68X_I2C_BUFFER_SIZE)
return BME68X_E_COM_FAIL;
#endif
if (intfPtr) {
comm = (bme68xScommT *) intfPtr;
if (comm->i2c.wireobj) {
comm->i2c.wireobj->beginTransmission(comm->i2c.i2cAddr);
comm->i2c.wireobj->write(regAddr);
for (i = 0; i < length; i++) {
comm->i2c.wireobj->write(regData[i]);
}
if (comm->i2c.wireobj->endTransmission()) {
rslt = BME68X_E_COM_FAIL;
}
} else {
rslt = BME68X_E_NULL_PTR;
}
} else {
rslt = BME68X_E_NULL_PTR;
}
return rslt;
}
/**
* @brief Function that implements the default I2C read transaction
*/
int8_t bme68xI2cRead(uint8_t regAddr, uint8_t *regData, uint32_t length,
void *intfPtr) {
uint32_t i;
int8_t rslt = BME68X_OK;
bme68xScommT *comm = NULL;
#ifdef BME68X_I2C_BUFFER_SIZE
if (length > BME68X_I2C_BUFFER_SIZE)
return BME68X_E_COM_FAIL;
#endif
if (intfPtr) {
comm = (bme68xScommT *) intfPtr;
if (comm->i2c.wireobj) {
comm->i2c.wireobj->beginTransmission(comm->i2c.i2cAddr);
comm->i2c.wireobj->write(regAddr);
if (comm->i2c.wireobj->endTransmission()) {
return BME68X_E_COM_FAIL;
}
comm->i2c.wireobj->requestFrom((int) comm->i2c.i2cAddr,
(int) length);
for (i = 0; (i < length) && comm->i2c.wireobj->available(); i++) {
regData[i] = comm->i2c.wireobj->read();
}
} else {
rslt = BME68X_E_NULL_PTR;
}
} else {
rslt = BME68X_E_NULL_PTR;
}
return rslt;
}
#endif

View file

@ -0,0 +1,359 @@
/**
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
*
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @file bme68xLibrary.h
* @date 11 Jan 2023
* @version 1.2.40408
*
*/
#ifndef BME68X_LIBRARY_H
#define BME68X_LIBRARY_H
#include <string.h>
#ifdef ARDUINO
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
#endif
#include "bme68x/bme68x.h"
#define BME68X_ERROR INT8_C(-1)
#define BME68X_WARNING INT8_C(1)
#ifdef ARDUINO
/**
* Datatype working as an interface descriptor
*/
typedef union
{
struct
{
TwoWire *wireobj;
uint8_t i2cAddr;
} i2c;
struct
{
SPIClass *spiobj;
uint8_t cs;
} spi;
} bme68xScommT;
#endif
/** Datatype to keep consistent with camel casing */
typedef struct bme68x_data bme68xData;
typedef struct bme68x_dev bme68xDev;
typedef enum bme68x_intf bme68xIntf;
typedef struct bme68x_conf bme68xConf;
typedef struct bme68x_heatr_conf bme68xHeatrConf;
#ifdef ARDUINO
/**
* @brief Function that implements the default microsecond delay callback
* @param periodUs : Duration of the delay in microseconds
* @param intfPtr : Pointer to the interface descriptor
*/
void bme68xDelayUs(uint32_t periodUs, void *intfPtr);
/**
* @brief Function that implements the default SPI write transaction
* @param regAddr : Register address of the sensor
* @param regData : Pointer to the data to be written to the sensor
* @param length : Length of the transfer
* @param intfPtr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t bme68xSpiWrite(uint8_t regAddr, const uint8_t *regData, uint32_t length, void *intfPtr);
/**
* @brief Function that implements the default SPI read transaction
* @param regAddr : Register address of the sensor
* @param regData : Pointer to the data to be read from the sensor
* @param length : Length of the transfer
* @param intfPtr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t bme68xSpiRead(uint8_t regAddr, uint8_t *regData, uint32_t length, void *intfPtr);
/**
* @brief Function that implements the default I2C write transaction
* @param regAddr : Register address of the sensor
* @param regData : Pointer to the data to be written to the sensor
* @param length : Length of the transfer
* @param intfPtr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t bme68xI2cWrite(uint8_t regAddr, const uint8_t *regData, uint32_t length, void *intfPtr);
/**
* @brief Function that implements the default I2C read transaction
* @param regAddr : Register address of the sensor
* @param regData : Pointer to the data to be written to the sensor
* @param length : Length of the transfer
* @param intfPtr : Pointer to the interface descriptor
* @return 0 if successful, non-zero otherwise
*/
int8_t bme68xI2cRead(uint8_t regAddr, uint8_t *regData, uint32_t length, void *intfPtr);
#endif
class Bme68x
{
public:
/** Stores the BME68x sensor APIs error code after an execution */
int8_t status;
/**
* Class constructor
*/
Bme68x(void);
/**
* @brief Function to initialize the sensor based on custom callbacks
* @param intf : BME68X_SPI_INTF or BME68X_I2C_INTF interface
* @param read : Read callback
* @param write : Write callback
* @param idleTask : Delay or Idle function
* @param intfPtr : Pointer to the interface descriptor
*/
void begin(bme68xIntf intf, bme68x_read_fptr_t read, bme68x_write_fptr_t write,
bme68x_delay_us_fptr_t idleTask, void *intfPtr);
#ifdef ARDUINO
/**
* @brief Function to initialize the sensor based on the Wire library
* @param i2cAddr : The I2C address the sensor is at
* @param i2c : The TwoWire object
* @param idleTask : Delay or Idle function
*/
void begin(uint8_t i2cAddr, TwoWire &i2c, bme68x_delay_us_fptr_t idleTask = bme68xDelayUs);
/**
* @brief Function to initialize the sensor based on the SPI library
* @param chipSelect : The chip select pin for SPI communication
* @param spi : The SPIClass object
* @param idleTask : Delay or Idle function
*/
void begin(uint8_t chipSelect, SPIClass &spi, bme68x_delay_us_fptr_t idleTask = bme68xDelayUs);
#endif
/**
* @brief Function to read a register
* @param regAddr : Register address
* @return Data at that register
*/
uint8_t readReg(uint8_t regAddr);
/**
* @brief Function to read multiple registers
* @param regAddr : Start register address
* @param regData : Pointer to store the data
* @param length : Number of registers to read
*/
void readReg(uint8_t regAddr, uint8_t *regData, uint32_t length);
/**
* @brief Function to write data to a register
* @param regAddr : Register addresses
* @param regData : Data for that register
*/
void writeReg(uint8_t regAddr, uint8_t regData);
/**
* @brief Function to write multiple registers
* @param regAddr : Pointer to the register addresses
* @param regData : Pointer to the data for those registers
* @param length : Number of register to write
*/
void writeReg(uint8_t *regAddr, const uint8_t *regData, uint32_t length);
/**
* @brief Function to trigger a soft reset
*/
void softReset(void);
/**
* @brief Function to set the ambient temperature for better configuration
* @param temp : Temperature in degree Celsius. Default is 25 deg C
*/
void setAmbientTemp(int8_t temp = 25);
/**
* @brief Function to get the measurement duration in microseconds
* @param opMode : Operation mode of the sensor. Attempts to use the last one if nothing is set
* @return Temperature, Pressure, Humidity measurement time in microseconds
*/
uint32_t getMeasDur(uint8_t opMode = BME68X_SLEEP_MODE);
/**
* @brief Function to set the operation mode
* @param opMode : BME68X_SLEEP_MODE, BME68X_FORCED_MODE, BME68X_PARALLEL_MODE, BME68X_SEQUENTIAL_MODE
*/
void setOpMode(uint8_t opMode);
/**
* @brief Function to get the operation mode
* @return Operation mode : BME68X_SLEEP_MODE, BME68X_FORCED_MODE, BME68X_PARALLEL_MODE, BME68X_SEQUENTIAL_MODE
*/
uint8_t getOpMode(void);
/**
* @brief Function to get the Temperature, Pressure and Humidity over-sampling
* @param osHum : BME68X_OS_NONE to BME68X_OS_16X
* @param osTemp : BME68X_OS_NONE to BME68X_OS_16X
* @param osPres : BME68X_OS_NONE to BME68X_OS_16X
*/
void getTPH(uint8_t &osHum, uint8_t &osTemp, uint8_t &osPres);
/**
* @brief Function to set the Temperature, Pressure and Humidity over-sampling.
* Passing no arguments sets the defaults.
* @param osTemp : BME68X_OS_NONE to BME68X_OS_16X
* @param osPres : BME68X_OS_NONE to BME68X_OS_16X
* @param osHum : BME68X_OS_NONE to BME68X_OS_16X
*/
void setTPH(uint8_t osTemp = BME68X_OS_2X, uint8_t osPres = BME68X_OS_16X, uint8_t osHum = BME68X_OS_1X);
/**
* @brief Function to get the filter configuration
* @return BME68X_FILTER_OFF to BME68X_FILTER_SIZE_127
*/
uint8_t getFilter(void);
/**
* @brief Function to set the filter configuration
* @param filter : BME68X_FILTER_OFF to BME68X_FILTER_SIZE_127
*/
void setFilter(uint8_t filter = BME68X_FILTER_OFF);
/**
* @brief Function to get the sleep duration during Sequential mode
* @return BME68X_ODR_NONE to BME68X_ODR_1000_MS
*/
uint8_t getSeqSleep(void);
/**
* @brief Function to set the sleep duration during Sequential mode
* @param odr : BME68X_ODR_NONE to BME68X_ODR_1000_MS
*/
void setSeqSleep(uint8_t odr = BME68X_ODR_0_59_MS);
/**
* @brief Function to set the heater profile for Forced mode
* @param temp : Heater temperature in degree Celsius
* @param dur : Heating duration in milliseconds
*/
void setHeaterProf(uint16_t temp, uint16_t dur);
/**
* @brief Function to set the heater profile for Sequential mode
* @param temp : Heater temperature profile in degree Celsius
* @param dur : Heating duration profile in milliseconds
* @param profileLen : Length of the profile
*/
void setHeaterProf(uint16_t *temp, uint16_t *dur, uint8_t profileLen);
/**
* @brief Function to set the heater profile for Parallel mode
* @param temp : Heater temperature profile in degree Celsius
* @param mul : Profile of number of repetitions
* @param sharedHeatrDur : Shared heating duration in milliseconds
* @param profileLen : Length of the profile
*/
void setHeaterProf(uint16_t *temp, uint16_t *mul, uint16_t sharedHeatrDur, uint8_t profileLen);
/**
* @brief Function to fetch data from the sensor into the local buffer
* @return Number of new data fields
*/
uint8_t fetchData(void);
/**
* @brief Function to get a single data field
* @param data : Structure where the data is to be stored
* @return Number of new fields remaining
*/
uint8_t getData(bme68xData &data);
/**
* @brief Function to get whole sensor data
* @return Sensor data
*/
bme68xData* getAllData(void);
/**
* @brief Function to get the BME68x heater configuration
*/
const bme68xHeatrConf& getHeaterConfiguration(void);
/**
* @brief Function to retrieve the sensor's unique ID
* @return Unique ID
*/
uint32_t getUniqueId(void);
/**
* @brief Function to get the error code of the interface functions
* @return Interface return code
*/
BME68X_INTF_RET_TYPE intfError(void);
/**
* @brief Function to check if an error / warning has occurred
* @return -1 if an error occurred, 1 if warning occured else 0
*/
int8_t checkStatus(void);
#ifdef ARDUINO
/**
* @brief Function to get a brief text description of the error
* @return Returns a string describing the error code
*/
String statusString(void);
#endif
private:
/** Datatype to keep consistent with camel casing
* Datastructure to hold sensor settings
*/
#ifdef ARDUINO
bme68xScommT comm;
#endif
bme68xDev bme6;
bme68xConf conf;
bme68xHeatrConf heatrConf;
bme68xData sensorData[3];
uint8_t nFields, iFields;
uint8_t lastOpMode;
};
#endif /* BME68X_CLASS_H */

View file

@ -0,0 +1,66 @@
# Advanced Development with FastLED
![perpetualmaniac_neo_giving_two_pills _one_of_them_is_black_and__4b145870-9ead-4976-b031-f4df3f2bfbe1](https://github.com/user-attachments/assets/9bba6113-688f-469f-8b51-bcb4fea910e5)
## GDB On Unit Tests
Yes, we have step through debugging with FastLED.
* VSCode
* Install Plugin: GDB Debugger - Beyond
* Navigate to one of the tests in `tests/` and open in
* Hit `F5`
If the Python Debugger pops up, then manually switch the VSCode debugger using `Launch(gdb)`
![image](https://github.com/user-attachments/assets/c1246803-df4b-4583-8c11-8243c084afc5)
## Enabling 3-second compile times using our `web-compiler`
* You must have `docker` installed for fastest compile times. It's free.
* `cd <FASTLED FOLDER>`
* `pip install fastled`
* `fastled examples/Blink/Blink.ino`
* Cpp changes to the fastled source can be compiled by the live fastled compiler.
## Testing your changes
Most of this is in the basic CONTRIBUTING.md guide. But as a reminder
* Unit Testing: `./test`
* Linting: `./lint`
* Compiling on platforms `./compile uno,teensy41,esp32s3 --examples Blink,Apa102HD`
## Enabling AI coding
`aider.chat` is available for advanced and high velocity coding with FastLED. To use it, have your open-ai or Anthropic api key ready. It's recommended to use Anthropic as it's performance is much better than Open-AI for coding.
At the root of the project type:
`./ai` and follow the prompts. Once the key is installed you will get a prompt that looks like this:
```bash
architect>
```
There are two modes to use this AI, a watch mode which watches your files for changes and launches automatically, and a slow way which you add target files then instruct it to make changes:
* watch mode (best for implimenting a function or two)
* Edit any file in the repo. Add a comment with `AI!` at the end. The ai will see this and start implementing what you just typed.
* Example: Edit `src/fl/vector.h` and put in a comment `// Add more comments AI!`, then say yes to the changes in the prompt.
* Slow mode (much better for bigger changes across the file or several)
* While you are in the `architect> ` prompt you will add a file to the chat
* `/add src/fl/vector.h`
* Now tell the AI what you want it to do, and it will do it.
* Making the AI fix it's own problems it introduced.
* At the AI command prompt, have it run the following
* Linux/Mac: `/run ./test`
* On Windows: `/run uv run test.py`
* After the test concludes, the AI will ask you if you want to add the output back into the chat, agree to it then let it try to correct it's mistakes.
Every single time you do a change, make sure and thoroughly check it. I recommend VSCodes built in git diff tool.
Although the AI is pretty amazing, it will inject entropy into your code and this is the source of a lot of problems. So watch all changes it makes very thoroughly. Under almost all circumstances you will have to revert unnecessary changes (like comments) line by line.

View file

@ -0,0 +1,49 @@
# Special Notes on APA102 and the 'High Definition' Mode in FastLED
The APA102 LED driver includes a 5-bit per-LED brightness component. Previously, this feature was not fully utilized, except through a workaround that defined a global brightness affecting all LEDs uniformly rather than individually.
In FastLED the APA102 chipset will have extra resolution in comparison to the WS2812 RGB8 mode.
There are two modes:
* APA102 "Regular Mode"
* Has enhanced color resolution when using the "global brightness" factor
* APA102HD Mode
* Applies automatic gamma correction at the driver level using "pseudo 13-bit" color mixing.
**APA102HD Mode**
[example: examples/APA102HD](examples/Apa102HD/)
By introducing a 5-bit gamma bit-shift algorithm, we now effectively leverage this per-LED brightness control. Faced with the decision to either rewrite the entire `CRGB` library to expose the 5-bit brightness—including adaptations for formats like RGBW—or to retain the existing RGB8 format used by FastLED and implement the enhancement at the driver level, the latter option was chosen. This approach avoids widespread changes and maintains compatibility; if RGB8 suffices for game development, it is adequate for LED development as well.
The term "Pseudo-13-bit" arises because the additional resolution becomes significant only when all color components are at low values. For example, colors like `CRGB(255, 255, 254)` or `CRGB(255, 1, 1)` do not benefit from increased resolution due to the dominance of the brighter components. However, in low-light conditions with colors such as `CRGB(8, 8, 8)`, where the maximum component value is low, the pseudo-13-bit algorithm significantly enhances resolution—precisely where increased resolution is most desired.
Gamma correction is applied to preserve the RGB8 format and because future LEDs are expected to support gamma correction inherently. In game development, the 0-255 color values are based on the gamma scale rather than the linear power scale. LEDs like the WS2812 operate on a linear power scale, which results in washed-out, undersaturated colors when displaying captured video directly. Implementing software gamma correction for RGB8 severely reduces color resolution.
To address this, an internal gamma scale mapping is applied:
```
RGB8 → RGB16 + 5-bit gamma → RGB8 + 5-bit gamma
```
During the conversion back to RGB8, the brightness from the 5-bit gamma is bit-shifted into the RGB components. Each time the 5-bit brightness is shifted right, the RGB components are shifted left. For example:
Starting with `RGB(4, 4, 4)` and a 5-bit brightness value of 31:
- Shift RGB components left, shift 5-bit brightness right:
- `RGB(8, 8, 8)`, brightness 15
- `RGB(16, 16, 16)`, brightness 7
- `RGB(32, 32, 32)`, brightness 3
- `RGB(64, 64, 64)`, brightness 1 (final state)
This simplified illustration omits that the actual processing occurs in 16-bit space rather than 8-bit, but the fundamental concept remains the same.
By truncating the gamma-corrected RGB16 values back to RGB8, the LEDs receive pre-boosted RGB components and pre-dimmed 5-bit brightness values. This method preserves minor color details over a greater range, offering a valuable trade-off and leading to the designation of this mode as "APA102HD."
In version 3.9.0, the algorithm was completely rewritten to function natively on 8-bit controllers like the `__AVR__` chipsets without significant performance loss. Previously, accumulating the numerator and denominator during the brightness bit-shifting process introduced extra bits that were ultimately truncated. Testing revealed that equivalent resolution could be achieved using straightforward bit-shifting, which also significantly reduced code size on AVR platforms with the new algorithm.
**Further Enhancements in Version 3.9.0**
Additionally, version 3.9.0 separated the color temperature from the global brightness scale. Before this update, global brightness was pre-mixed with the component scales—a method suitable for the WS2812's RGB8 format but not for the APA102's RGB8 plus 5-bit brightness. The update saw the global brightness and color scales separated for non-AVR chipsets. While the WS2812 continues to use pre-mixed values for performance reasons on AVR chipsets, the APA102 now performs component mixing within the "pseudo-13-bit space."
Although APA102HD mode offers the highest dynamic range, the standard APA102 mode also benefits from increased resolution when adjusting global brightness. In this mode, instead of pre-mixing scales and multiplying them against each `CRGB` value, the global brightness is applied to the 5-bit brightness component, and only the color scales are multiplied against the `CRGB` values. This approach is superior because each component of the color scale typically exceeds 127, providing ample high-order bits to preserve color information.

View file

@ -0,0 +1,25 @@
# FastLED
# https://github.com/FastLED/FastLED
# MIT License
cmake_minimum_required(VERSION 3.5)
# Collect all source files
file(GLOB FastLED_SRCS "src/*.cpp")
file(GLOB FastLED_FL_SRCS "src/fl/*.cpp")
file(GLOB FastLED_SENSORS_SRCS "src/sensors/*.cpp")
file(GLOB FastLED_FX_SRCS "src/fx/*.cpp" "src/fx/**/*.cpp")
file(GLOB ESP32_SRCS "src/platforms/esp/32/*.cpp" "src/platforms/esp/32/rmt_5/*.cpp")
file(GLOB ESP32_THIRD_PARTY_SRCS "src/third_party/**/src/*.c" "src/third_party/**/src/*.cpp")
file(GLOB ESP32_LED_STRIP_SRCS "src/third_party/espressif/led_strip/src/*.c")
# Combine all source files into a single list
list(APPEND FastLED_SRCS ${FastLED_FL_SRCS} ${FastLED_SENSORS_SRCS} ${FastLED_FX_SRCS} ${ESP32_SRCS} ${ESP32_THIRD_PARTY_SRCS} ${ESP32_LED_STRIP_SRCS})
# Register the component with ESP-IDF
idf_component_register(SRCS ${FastLED_SRCS}
INCLUDE_DIRS "src" "src/third_party/espressif/led_strip/src"
REQUIRES arduino-esp32 esp_driver_rmt esp_lcd driver)
project(FastLED)

View file

@ -0,0 +1,75 @@
## Contributing
The most important part about contributing to FastLED is knowing how to test your changes.
The FastLED library includes a powerful cli that can compile to any device. It will run if you have either [python](https://www.python.org/downloads/) or [uv](https://github.com/astral-sh/uv) installed on the system.
## FastLED compiler cli
[![clone and compile](https://github.com/FastLED/FastLED/actions/workflows/build_clone_and_compile.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_clone_and_compile.yml)
The FastLED compiler cli can be invoked at the project root.
```bash (MacOS/Linux, windows us git-bsh or compile.bat)
git clone https://github.com/fastled/fastled
cd fastled
./compile uno --examples Blink # linux/macos/git-bash
# compile.bat # Windows.
```
## Linting and Unit Testing
```bash
./lint
./test # runs unit tests
# Note that you do NOT need to install the C++ compiler toolchain
# for compiling + running unit tests via ./test. If `gcc` is not
# found in your system `PATH` then the `ziglang` clang compiler
# will be swapped in automatically.
````
### Testing a bunch of platforms at once.
```
./compile teensy41,teensy40 --examples Blink
./compile esp32dev,esp32s3,esp32c3,esp32c6,esp32s2 --examples Blink,Apa102HD
./compiles uno,digix,attiny85 --examples Blink,Apa102HD
```
## Unit Tests
Shared code is unit-tested on the host machine. They can be found at `tests/` at the root of the repo. Unit testing only requires either `python` or `uv` to be installed. The C++ compiler toolchain will be installed automatically.
The easiest way to run the tests is just use `./test`
Alternatively, tests can be built and run for your development machine with CMake:
```bash
cmake -S tests -B tests/.build
ctest --test-dir tests/.build --output-on-failure
# Not that this will fail if you do not have gcc installed. When in doubt
# use ./test to compile the unit tests, as a compiler is guaranteed to be
# available via this tool.
```
## VSCode
We also support VSCode and IntelliSense auto-completion when the free [platformio](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide) extension is installed. The development sketch to test library changes can be found at [dev/dev.ino](dev/dev.ino).
* Make sure you have [platformio](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide) installed.
* Click the compile button.
![image](https://github.com/user-attachments/assets/616cc35b-1736-4bb0-b53c-468580be66f4)
*Changes in non platform specific code can be tested quickly in our webcompiler by invoking the script `./wasm` at the project root*
## Once you are done
* run `./test`
* run `./lint`
* Then submit your code via a git pull request.
## Going deeper
[ADVANCED_DEVELOPMENT.md](https://github.com/FastLED/FastLED/blob/master/ADVANCED_DEVELOPMENT.md)

20
libraries/FastLED/LICENSE Normal file
View file

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 FastLED
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,56 @@
Platform Porting Guide
==========================
# Fast porting for a new board on existing hardware
Sometimes "porting" FastLED simply consists of supplying new pin definitions for the given platform. For example, platforms/avr/fastpin_avr.h contains various pin definitions for all the AVR variant chipsets/boards that FastLED supports. Defining a set of pins involves setting up a set of definitions - for example here's one full set from the avr fastpin file:
```
#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__)
_FL_IO(A); _FL_IO(B); _FL_IO(C); _FL_IO(D);
#define MAX_PIN 31
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B);
_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B); _FL_DEFPIN(6, 6, B); _FL_DEFPIN(7, 7, B);
_FL_DEFPIN(8, 0, D); _FL_DEFPIN(9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D);
_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 6, D); _FL_DEFPIN(15, 7, D);
_FL_DEFPIN(16, 0, C); _FL_DEFPIN(17, 1, C); _FL_DEFPIN(18, 2, C); _FL_DEFPIN(19, 3, C);
_FL_DEFPIN(20, 4, C); _FL_DEFPIN(21, 5, C); _FL_DEFPIN(22, 6, C); _FL_DEFPIN(23, 7, C);
_FL_DEFPIN(24, 0, A); _FL_DEFPIN(25, 1, A); _FL_DEFPIN(26, 2, A); _FL_DEFPIN(27, 3, A);
_FL_DEFPIN(28, 4, A); _FL_DEFPIN(29, 5, A); _FL_DEFPIN(30, 6, A); _FL_DEFPIN(31, 7, A);
#define HAS_HARDWARE_PIN_SUPPORT 1
```
The ```_FL_IO``` macro is used to define the port registers for the platform while the ```_FL_DEFPIN``` macro is used to define pins. The parameters to the macro are the pin number, the bit on the port that represents that pin, and the port identifier itself. On some platforms, like the AVR, ports are identified by letter. On other platforms, like arm, ports are identified by number.
The ```HAS_HARDWARE_PIN_SUPPORT``` define tells the rest of the FastLED library that there is hardware pin support available. There may be other platform specific defines for things like hardware SPI ports and such.
## Setting up the basic files/folders
* Create platform directory (e.g. platforms/arm/kl26)
* Create configuration header led_sysdefs_arm_kl26.h:
* Define platform flags (like FASTLED_ARM/FASTLED_TEENSY)
* Define configuration parameters re: interrupts, or clock doubling
* Include extar system header files if needed
* Create main platform include, fastled_arm_kl26.h
* Include the various other header files as needed
* Modify led_sysdefs.h to conditionally include platform sysdefs header file
* Modify platforms.h to conditionally include platform fastled header
## Porting fastpin.h
The heart of the FastLED library is the fast pin access. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output.
There's two low level FastPin classes. There's the base FastPIN template class, and then there is FastPinBB which is for bit-banded access on those MCUs that support bitbanding. Note that the bitband class is optional and primarily useful in the implementation of other functionality internal to the platform. This file is also where you would do the pin to port/bit mapping defines.
Explaining how the macros work and should be used is currently beyond the scope of this document.
## Porting fastspi.h
This is where you define the low level interface to the hardware SPI system (including a writePixels method that does a bunch of housekeeping for writing led data). Use the fastspi_nop.h file as a reference for the methods that need to be implemented. There are ofteh other useful methods that can help with the internals of the SPI code, I recommend taking a look at how the various platforms implement their SPI classes.
## Porting clockless.h
This is where you define the code for the clockless controllers. Across ARM platforms this will usually be fairly similar - though different arm platforms will have different clock sources that you can/should use.

499
libraries/FastLED/README.md Normal file
View file

@ -0,0 +1,499 @@
FastLED Library
===========
[![arduino-library-badge](https://www.ardu-badge.com/badge/FastLED.svg)](https://www.ardu-badge.com/FastLED)
[![build status](https://github.com/FastLED/FastLED/workflows/build/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build.yml)
[![unit tests](https://github.com/FastLED/FastLED/actions/workflows/build_unit_test.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_unit_test.yml)
[![Arduino Library Lint](https://github.com/FastLED/FastLED/actions/workflows/arduino_library_lint.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/arduino_library_lint.yml)
[![Documentation](https://img.shields.io/badge/Docs-Doxygen-blue.svg)](http://fastled.io/docs)
[![Reddit](https://img.shields.io/badge/reddit-/r/FastLED-orange.svg?logo=reddit)](https://www.reddit.com/r/FastLED/)
Want to control a strip of leds? Or control 10's of thousands? FastLED has your back.
FastLED is a robust and massively parallel-led driver for Arduino, Esp32, RaspberryPi, Atmega, Teensy, Uno, Apollo3 Arm and more. Also runs on dirt cheap sub $1 devices, due to it's incredibly small compile size. High end devices can drive upto ~30k LEDS (Teensy) and ~20k on ESP32. Supports nearly every single LED chipset in existence. Background rendering (ESP32/Teensy/RaspberriPi) means you can respond to user input while the leds render. FastLED is the third [most popular library on Arduino](https://docs.arduino.cc/libraries/).
FastLED has recently begun to evolve. While before the FastLED codebase was just a highly compatible cross platform LED driver, now it is becoming a cross platform way to generate visualizers that run on AVR, esp32, teensy, Rasperri PI etc
## Documentation
Can be found [here](https://fastled.io/docs/files.html)
## Star History
<a href="https://star-history.com/#fastled/fastled&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=fastled/fastled&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=fastled/fastled&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=fastled/fastled&type=Date" />
</picture>
</a>
## About
This is a driver library for easily & efficiently controlling a wide variety of LED chipsets, like the ones
sold by Adafruit (NeoPixel, DotStar, LPD8806), Sparkfun (WS2801), and AliExpress.
The 3.9.x series introduced:
* Massive parallel rendering to drive thousands of LEDs.
* Background rendering of LEDs so that your program/sketch can prepare the next frame and respond to user input without affect frame rate.
In addition to writing to the LEDs, this library also includes a number of functions for high-performing 8-bit math for manipulating
your RGB values, as well as low level classes for abstracting out access to pins and SPI hardware, while
still keeping things as fast as possible.
We have multiple goals with this library:
* Quick start for new developers - hook up your LEDs and go, no need to think about specifics of the LED chipsets being used
* Zero pain switching LED chipsets - you get some new LEDs that the library supports, just change the definition of LEDs you're using, et. voila! Your code is running with the new LEDs.
* High performance - with features like zero cost global brightness scaling, high performance 8-bit math for RGB manipulation, and some of the fastest bit-bang'd SPI support around, FastLED wants to keep as many CPU cycles available for your LED patterns as possible
## Example
*This is an Arduino Sketch that will run on Arduino Uno/Esp32/Raspberri Pi*
```C++
// New feature! Overclocking WS2812
// #define FASTLED_OVERCLOCK 1.2 // 20% overclock ~ 960 khz.
#include <FastLED.h>
#define NUM_LEDS 60
#define DATA_PIN 6
CRGB leds[NUM_LEDS];
void setup() { FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); }
void loop() {
leds[0] = CRGB::White; FastLED.show(); delay(30);
leds[0] = CRGB::Black; FastLED.show(); delay(30);
}
```
For more examples, see this [link](examples). Web compiled [examples](https://zackees.github.io/fastled-wasm/).
# New Feature Announcements
## New in 3.9.16: WaveFx / Multi Layer Compositing / Time-based animation control
Video:
https://github.com/user-attachments/assets/9155124b-a93e-4317-b272-8bacc1b9c3a8
#### Major release for tech-artists!
Lots of improvements in this release, read the full [change list here](https://github.com/FastLED/FastLED/releases/tag/3.9.16)
#### Links
* This demo -> [FxWave2d](https://github.com/FastLED/FastLED/blob/master/examples/FxWave2d/FxWave2d.ino)
* [Wave Simulation Library](https://github.com/FastLED/FastLED/blob/master/src/fl/wave_simulation.h)
* [FireCylinder](https://github.com/FastLED/FastLED/blob/master/examples/FireCylinder/FireCylinder.ino)
* Wraps around so that (0,y) ~= (width-1,y)
* [TimeAlpha](https://github.com/FastLED/FastLED/blob/master/src/fl/time_alpha.h)
* Precision control of animations with time-based alpha transition.
## New in 3.9.13: HD107 "Turbo" 40Mhz LED Support
![image](https://github.com/user-attachments/assets/9684ab7d-2eaa-40df-a00d-0dff18098917)
## New in 3.9.12: WS2816 "HD" LED support
![image](https://github.com/user-attachments/assets/258ec44c-af82-44b7-ad7b-fac08daa9bcb)
## New in 3.9.10: Super Stable WS2812 SPI driver for ESP32
![image (2)](https://github.com/user-attachments/assets/b3c5801c-66df-40af-a6b8-bbd1520fbb36)
## New in 3.9.9: 16-way Yves I2S parallel driver for the ESP32-S3
![perpetualmaniac_an_led_display_in_a_room_lots_of_refaction_of_t_eb7c170a-7b2c-404a-b114-d33794b4954b](https://github.com/user-attachments/assets/982571fc-9b8d-4e58-93be-5bed76a0c53d)
*Note some users find that newer versions of the ESP32 Arduino core (3.10) don't work very well, but older versions do, see [issue 1903](https://github.com/FastLED/FastLED/issues/1903)
## New in 3.9.8 - Massive Teensy 4.1 & 4.0 WS2812 LED output
* *Teensy 4.1: 50 parallel pins*
* *Teensy 4.0: 42 parallel pins*
![New Project](https://github.com/user-attachments/assets/79dc2801-5161-4d5a-90a2-0126403e215f)
## New in 3.9.2 - Overclocking of WS2812
![image](https://github.com/user-attachments/assets/be98fbe6-0ec7-492d-8ed1-b7eb6c627e86)
Update: max overclock has been reported at +70%: https://www.reddit.com/r/FastLED/comments/1gkcb6m/fastled_FASTLED_OVERCLOCK_17/
## New in 3.7.7 - RGBW LED Strip Support
![image (1)](https://github.com/user-attachments/assets/d4892626-3dc6-4d6d-a740-49ddad495fa5)
## Supported Platforms
### Arduino
[![uno](https://github.com/FastLED/FastLED/actions/workflows/build_uno.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_uno.yml)
[![attiny85](https://github.com/FastLED/FastLED/actions/workflows/build_attiny85.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny85.yml)
[![attiny88](https://github.com/FastLED/FastLED/actions/workflows/build_attiny88.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny88.yml)
[![attiny1604](https://github.com/FastLED/FastLED/actions/workflows/build_attiny1604.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny1604.yml)
[![attiny1616](https://github.com/FastLED/FastLED/actions/workflows/build_attiny1616.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny1616.yml)
[![attiny4313](https://github.com/FastLED/FastLED/actions/workflows/build_attiny4313.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny4313.yml)
*New FastLED 3.9.14! - Very memory limited, so only tested against examples WS2812 Blink and APA102*
[![yun](https://github.com/FastLED/FastLED/actions/workflows/build_yun.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_yun.yml)
[![digix](https://github.com/FastLED/FastLED/actions/workflows/build_digix.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_digix.yml)
[![due](https://github.com/FastLED/FastLED/actions/workflows/build_due.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_due.yml)
[![uno_r4_wifi](https://github.com/FastLED/FastLED/actions/workflows/build_uno_r4_wifif.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_uno_r4_wifif.yml)
[![nano_every](https://github.com/FastLED/FastLED/actions/workflows/build_nano_every.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_nano_every.yml)
[![Arduino Giga-R1](https://github.com/FastLED/FastLED/actions/workflows/build_giga_r1.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_giga_r1.yml)
*Now works in 3.9.14!*
### Teensy
[![teensy30](https://github.com/FastLED/FastLED/actions/workflows/build_teensy30.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy30.yml)
[![teensy31](https://github.com/FastLED/FastLED/actions/workflows/build_teensy31.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy31.yml)
[![teensyLC](https://github.com/FastLED/FastLED/actions/workflows/build_teensyLC.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensyLC.yml)
[![teensy40](https://github.com/FastLED/FastLED/actions/workflows/build_teensy40.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy40.yml)
[![teensy41](https://github.com/FastLED/FastLED/actions/workflows/build_teensy41.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy41.yml)
*Specific Features*
[![teensy_octoWS2811](https://github.com/FastLED/FastLED/actions/workflows/build_teensy_octo.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy_octo.yml)
[![teensy41 ObjectFLED](https://github.com/FastLED/FastLED/actions/workflows/build_teensy41_ofled.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_teensy41_ofled.yml)
### NRF
[![nrf52840_sense](https://github.com/FastLED/FastLED/actions/workflows/build_adafruit_feather_nrf52840_sense.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_adafruit_feather_nrf52840_sense.yml)
[![nordicnrf52_dk](https://github.com/FastLED/FastLED/actions/workflows/build_nrf52840_dk.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_nrf52840_dk.yml)
[![adafruit_xiaoblesense](https://github.com/FastLED/FastLED/actions/workflows/build_adafruit_xiaoblesense.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_adafruit_xiaoblesense.yml)
[![nrf52_xiaoblesense](https://github.com/FastLED/FastLED/actions/workflows/build_nrf52_xiaoblesense.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_nrf52_xiaoblesense.yml)
(This board has mbed engine but doesn't compile against Arduino.h right now for some unknown reason.)
### Apollo3
[![apollo3_red](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_red.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_red.yml) *Board needs pin definitions.*
[![apollo3_thing_explorable](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml)
### STM
[![bluepill](https://github.com/FastLED/FastLED/actions/workflows/build_bluepill.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_bluepill.yml)
[![maple_mini](https://github.com/FastLED/FastLED/actions/workflows/build_maple_map.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_maple_map.yml)
[![stm103tb](https://github.com/FastLED/FastLED/actions/workflows/build_stm103tb.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_stm103tb.yml)
(PlatformIO doesn't support this board yet and we don't know what the build info is to support this is yet)
### Raspberry Pi
[![rp2040](https://github.com/FastLED/FastLED/actions/workflows/build_rp2040.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_rp2040.yml)
[![rp2350](https://github.com/FastLED/FastLED/actions/workflows/build_rp2350.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_rp2350.yml)
[![rp2350B SparkfunXRP](https://github.com/FastLED/FastLED/actions/workflows/build_rp2350B.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_rp2350B.yml)
### Esp
[![esp32-8266](https://github.com/FastLED/FastLED/actions/workflows/build_esp8622.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp8622.yml)
[![esp32dev](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev.yml)
[![esp32wroom](https://github.com/FastLED/FastLED/actions/workflows/build_esp32wroom.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32wroom.yml)
[![esp32c2](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c2.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c2.yml)
*Now supported as of FastLED 3.9.10!*
[![esp32c3](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c3.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c3.yml)
[![esp32s2](https://github.com/FastLED/FastLED/actions/workflows/build_esp32s2.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32s2.yml)
[![esp32s3](https://github.com/FastLED/FastLED/actions/workflows/build_esp32s3.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32s3.yml)
[![esp32c6](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c6.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32c6.yml)
[![esp32h2](https://github.com/FastLED/FastLED/actions/workflows/build_esp32h2.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32h2.yml)
*Specific features*
[![esp32_i2s_ws2812](https://github.com/FastLED/FastLED/actions/workflows/build_esp32_i2s_ws2812.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32_i2s_ws2812.yml)
[![esp32 extra libs](https://github.com/FastLED/FastLED/actions/workflows/build_esp_extra_libs.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp_extra_libs.yml)
[![esp32dev_namespace](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_namespace.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_namespace.yml)
*Legacy Toolchains*
[![esp32dev-idf3.3-lts](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_idf3.3.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_idf3.3.yml)
Espressif's current evaluation of FastLED's compatibility with their product sheet can be found [here](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md)
### x86
[![linux_native](https://github.com/FastLED/FastLED/actions/workflows/build_linux.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_linux.yml)
### Wasm
[![wasm](https://github.com/FastLED/FastLED/actions/workflows/build_wasm.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_wasm.yml)
[![wasm_compile_test](https://github.com/FastLED/FastLED/actions/workflows/build_wasm_compilers.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_wasm_compilers.yml)
## Compiled Library Size Check
[![attiny85_binary_size](https://github.com/FastLED/FastLED/actions/workflows/check_attiny85.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/check_attiny85.yml)
[![uno_binary_size](https://github.com/FastLED/FastLED/actions/workflows/check_uno_size.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/check_uno_size.yml)
[![esp32dev_binary_size](https://github.com/FastLED/FastLED/actions/workflows/check_esp32_size.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/check_esp32_size.yml)
[![teensy41_binary_size](https://github.com/FastLED/FastLED/actions/workflows/check_teensy41_size.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/check_teensy41_size.yml)
# Install
## Arduino IDE
After the ArduinoIDE is installed then add the library to your IDE
![image](https://github.com/user-attachments/assets/b1c02cf9-aba6-4f80-851e-78df914e2501)
![image](https://github.com/user-attachments/assets/508eb700-7dd4-4901-a901-68c56cc4d0e1)
## PlatformIO
PlatformIO offers an incredible IDE experience. Setup is easier than you think. Follow our guide here. Our template will allow your project to be compiled by both PlatformIO and ArduinoIDE
https://github.com/FastLED/PlatformIO-Starter
# How to maximize the number of parallel WS2812 outputs
Some of the new processors can drive many many WS2812 strips in parallel.
# Leader Boards: Stock Setups
### Teensy 4.0/4.1
This chipset holds the current record for parallel output in a stock configuration. The theoretical output is 50 strips at a time with Teensy 4.1 and 42 strips with Teensy 4.2.
See this [example](https://github.com/FastLED/FastLED/blob/master/examples/TeensyMassiveParallel/TeensyMassiveParallel.ino) on how to enable.
### ESP32DEV
Surprisingly it's the good old ESP32Dev and not the ESP32S3, which holds the esp record for the amount of parallel outputs at 24 through I2S, and 8 via RMT.
I2S needs special setup as of 3.9.11 and earlier (current version of this writing is 3.9.11) see the [example](https://github.com/FastLED/FastLED/blob/master/examples/EspI2SDemo/EspI2SDemo.ino) here.
### ESP32-S3
The S3 is a CPU beast, but has half the RMT tx channels (4) and 2/3rds the I2S channels (16) of ESPDev. The S3 requires a special driver for I2S which you can find in this [example](https://github.com/FastLED/FastLED/blob/master/examples/Esp32S3I2SDemo/Esp32S3I2SDemo.ino)
*Note some users find that newer versions of the ESP32 arduino core (3.10) don't work very well, but older versions do, see [issue 1903](https://github.com/FastLED/FastLED/issues/1903)
### RaspberriPi
I (Zach Vorhies) don't use this platform. Help wanted on what the limits of this chip is.
## Exotic Setups
If you are willing to make a custom board with shift registers, then the ESp32S3 and ESP32Dev have special "virtual pin" libraries. These libraries will allow you to drive 120 parallel WS2812 outputs. However these are not included in FastLED but are compatible with it.
* Esp32DEV: https://github.com/hpwit/I2SClocklessVirtualLedDriver
* Esp32-S3: https://github.com/hpwit/I2SClockLessLedVirtualDriveresp32s3
## Development
[![clone and compile](https://github.com/FastLED/FastLED/actions/workflows/build_clone_and_compile.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_clone_and_compile.yml)
Zero pain setup and install/test/run. Can be done from the command line in seconds if `uv` or `python` are installed. See our [contributing guide](https://github.com/FastLED/FastLED/blob/master/CONTRIBUTING.md) guide for more information.
![image](https://github.com/user-attachments/assets/f409120e-be6f-4158-81b5-d4bf935833b2)
When changes are made then push to your fork to your repo and git will give you a url to trigger a pull request into the master repo.
### Testing other devices
* run [compile](compile) and then select your board
```bash
Available boards:
[0]: ATtiny1616
[1]: adafruit_feather_nrf52840_sense
[2]: attiny85
[3]: bluepill
[4]: digix
[5]: esp01
[6]: esp32c2
[7]: esp32c3
[8]: esp32c6
[9]: esp32s3
[10]: esp32dev
[11]: esp32dev_i2s
[12]: esp32dev_idf44
[13]: esp32rmt_51
[14]: nano_every
[15]: rpipico
[16]: rpipico2
[17]: teensy30
[18]: teensy41
[19]: uno
[20]: uno_r4_wifi
[21]: xiaoblesense_adafruit
[22]: yun
[all]: All boards
Enter the number of the board you want to use: 0
```
## Help and Support
If you need help with using the library, please consider visiting the Reddit community at https://reddit.com/r/FastLED. There are thousands of knowledgeable FastLED users in that group and a plethora of solutions in the post history.
If you are looking for documentation on how something in the library works, please see the Doxygen documentation online at http://fastled.io/docs.
If you run into bugs with the library, or if you'd like to request support for a particular platform or LED chipset, please submit an issue at http://fastled.io/issues.
## Supported LED Chipsets
Here's a list of all the LED chipsets are supported. More details on the LED chipsets are included [on our wiki page](https://github.com/FastLED/FastLED/wiki/Chipset-reference)
* WS281x Clockless family
* WS2811 (Old style 400khz & 800khz)
* WS2812 (NeoPixel)
* WS2812-V5B (250 uS reset)
* WS2815
* APA102 / SK9822 / HD107s (turbo->40mhz) / Adafruit DotStars (SPI)
* HD107s, same thing as the APA102, but runs at turbo 40 Mhz
* SmartMatrix panels - needs the SmartMatrix library (https://github.com/pixelmatix/SmartMatrix)
* TM1809/4 - 3 wire chipset, cheaply available on aliexpress.com
* TM1803 - 3 wire chipset, sold by RadioShack
* UCS1903 - another 3-wire LED chipset, cheap
* GW6205 - another 3-wire LED chipset
* LPD8806 - SPI-based chipset, very high speed
* WS2801 - SPI-based chipset, cheap and widely available
* SM16716 - SPI-based chipset
* APA102 - SPI-based chipset
* APA102HD - Same as APA102 but with a high-definition gamma correction function applied at the driver level.
* P9813 - aka Cool Neon's Total Control Lighting
* DMX - send rgb data out over DMX using Arduino DMX libraries
* LPD6803 - SPI-based chipset, chip CMODE pin must be set to 1 (inside oscillator mode)
## APA102 and the 'High Definition' Mode in FastLED
FastLED features driver-level gamma correction for the APA102 and SK9822 chipsets, using our "pseudo-13-bit mixing" algorithm.
Read about it here: https://github.com/FastLED/FastLED/blob/master/APA102.md
Enable it like by using the `APA102HD` type. Example:
```C++
#define LED_TYPE APA102HD // "HD" suffix for APA102 family enables hardware gamma correction
void setup() {
FastLED.addLeds<LED_TYPE, DATA_PIN, CLOCK_PIN, RGB>(leds_hd, NUM_LEDS);
}
```
![image](https://github.com/user-attachments/assets/999e68ce-454f-4f15-9590-a8d2e8d47a22)
Check out thr rust port of this algorithm:
https://docs.rs/apa102-spi/latest/apa102_spi/
# Getting Started
### Arduino IDE / PlatformIO Dual Repo
We've created a custom repo you can try to start your projects. This repo is designed to be used with VSCode + PlatformIO but is also *backwards compatible with the Arduino IDE*.
PlatformIO is an extension to VSCode and is generally viewed as a much better experience than the Arduino IDE. You get auto completion tools like intellisense and CoPilot and the ability to install tools like crash decoding. Anything you can do in Arduino IDE you can do with PlatformIO.
Get started here:
https://github.com/FastLED/PlatformIO-Starter
### ArduinoIDE
When running the Arduino IDE you need to do the additional installation step of installing FastLED in the global Arduino IDE package manager.
Install the library using either [the .zip file from the latest release](https://github.com/FastLED/FastLED/releases/latest/) or by searching for "FastLED" in the libraries manager of the Arduino IDE. [See the Arduino documentation on how to install libraries for more information.](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries)
## Porting FastLED to a new platform
Information on porting FastLED can be found in the file [PORTING.md](PORTING.md).
## What about that name?
Wait, what happened to FastSPI_LED and FastSPI_LED2? The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access. However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board. We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs.
## For more information
Check out the official site http://fastled.io for links to documentation, issues, and news.
## Daniel Garcia, Founder of FastLED
In Memory of Daniel Garcia
Daniel Garcia, the brilliant founder of FastLED, tragically passed away in September 2019 in the Conception dive boat fire alongside his partner, Yulia. This heartbreaking loss was felt deeply by the maker and developer community, where Daniel's contributions had left an indelible mark.
Daniel was more than just a talented programmer; he was a passionate innovator who transformed the way creators interacted with LED technology. His work on FastLED brought high-performance LED control to countless projects, empowering developers to craft breathtaking installations.
In his personal life, Daniel was known for his kindness and creativity. His pride in FastLED and the vibrant community it fostered was a testament to his dedication to open-source development and his commitment to helping others bring light into the world.
While Daniel is no longer with us, his legacy continues through the FastLED library and the countless makers who use it. The community he built serves as a living tribute to his ingenuity, generosity, and the joy he found in sharing his work with the world.
## About the Current Contributor
Zach Vorhies, the current main contributor to FastLED, briefly worked with Dan in 2014 in San Francisco and was an avid user of the FastLED library for over 13 years. After Daniel Garcias untimely passing, Zach stepped up to ensure FastLEDs continued growth and development.
Zach has this to say about FastLED:
*"The true power of FastLED lies in its ability to transform programmers into LED artists. Free space becomes their canvas; bending light is their medium. FastLED is a collective effort by programmers who want to manifest the world that science fiction writers promised us. -- To contribute code to FastLED is to leave behind a piece of something immortal."*
## Contributing
See our easy to use guide here:
https://github.com/FastLED/FastLED/blob/master/CONTRIBUTING.md
*To stay updated on the latest feature releases, please click the `Watch` button in the upper right*

View file

@ -0,0 +1,39 @@
# FastLED Release howto
*Pushing a fastled release, the short version, last updated May 2024*
## Example
https://github.com/FastLED/FastLED/commit/4444758ffaf853ba4f8deb973532548c9c1ee231
## How to
Edit these files to update the version number
* library.json
* library.properties
* src/FastLED.h
* docs/Doxyfile
* RELEASE.md
* This file: update instructions with the current release.
Edit this file with release notes and version number.
* release_notes.md
Release notes should list highlight changes (not necessarily all minor bug fixes) and thank people for their help.
Git commands to commit and tag release'
```bash
$ git commit -am "Rev 3.9.20 - Misc fixes"
$ git tag 3.9.20 master
$ git push
$ git push origin 3.9.20
```
Then use the GitHub UI to make a new “Release”:
https://github.com/FastLED/FastLED/releases/new
Announce new version on subreddit, highlighting major changes and thanking people for helping.
Thats it.

37
libraries/FastLED/TODO.md Normal file
View file

@ -0,0 +1,37 @@
# Testing
* Esp32 testing
* https://github.com/marketplace/actions/esp32-qemu-runner will run a sketch for X seconds and see's if it crashes
* There's specific tests we'd like to run with this including the WS2812 and APA102 tests to test the clockless and clocked drivers
# Feature Enhancements
* I2S driver for ESP32 WS2812
* https://github.com/hpwit/I2SClocklessLedDriver
* Our copy is here: https://github.com/FastLED/FastLED/blob/master/src/platforms/esp/32/clockless_i2s_esp32.h
* S3:
* https://github.com/hpwit/I2SClockLessLedDriveresp32s3
* Apparently, this driver allows MASSIVE parallelization for WS2812
* Timing guide for reducing RMT frequency https://github.com/Makuna/NeoPixelBus/pull/795
* ESp32 LED guide
* web: https://components.espressif.com/components/espressif/led_strip
* repo: https://github.com/espressif/idf-extra-components/tree/60c14263f3b69ac6e98ecae79beecbe5c18d5596/led_strip
* adafruit conversation on RMT progress: https://github.com/adafruit/Adafruit_NeoPixel/issues/375
* MIT Licensed SdFat library
* https://github.com/greiman/SdFat
* YVes LittleFS implementation for ESP
* https://github.com/hpwit/ledOS/blob/main/src/fileSystem.h
* NimBLE for Arduino
* https://github.com/h2zero/NimBLE-Arduino?tab=readme-ov-file
* Arduino test compile
* https://github.com/hpwit/arduino-test-compile/blob/master/arduino-test-compile.sh
# Misc:
* sutaburosu's guide to playing around with FastLED 4
* https://github.com/sutaburosu/FastLED4-ESP32-playpen

3
libraries/FastLED/ai Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
uv run aicode

View file

@ -0,0 +1,11 @@
This is the Continuous Integration tool that builds the product in various configurations.
Running
Install the python `uv` tool.
`pip install uv`
Now run the python in this directory
`uv run ci-compile.py`

View file

@ -0,0 +1,30 @@
{
"build": {
"core": "esp32",
"f_cpu": "120000000L",
"f_flash": "60000000L",
"flash_mode": "qio",
"mcu": "esp32c2",
"variant": "esp32c2"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32c2.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Espressif ESP32-C2-DevKitM-1",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 278528,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp8684/esp8684-devkitm-1/user_guide.html",
"vendor": "Espressif"
}

View file

@ -0,0 +1,31 @@
{
"build": {
"core": "esp32",
"f_cpu": "160000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"mcu": "esp32c6",
"variant": "esp32c6"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32c6.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Espressif ESP32-C6-DevKitC-1",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32c6/esp32-c6-devkitc-1/index.html",
"vendor": "Espressif"
}

View file

@ -0,0 +1,31 @@
{
"build": {
"core": "esp32",
"f_cpu": "96000000L",
"f_flash": "64000000L",
"f_image": "48000000L",
"flash_mode": "qio",
"mcu": "esp32h2",
"variant": "esp32h2"
},
"connectivity": [
"bluetooth"
],
"debug": {
"openocd_target": "esp32h2.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Espressif ESP32-H2-DevKit",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32h2/esp32-h2-devkitm-1/index.html",
"vendor": "Espressif"
}

View file

@ -0,0 +1,61 @@
{
"build": {
"arduino": {
"ldscript": "linker_script.ld",
"flash_layout": "100_0"
},
"extra_flags": "-DARDUINO_GIGA -DARDUINO_GIGA_PINS -DGIGA_PINS -DGIGA -DCM4_BINARY_START=0x60000000 -DCM4_BINARY_END=0x60040000 -DCM4_RAM_END=0x60080000",
"core": "arduino",
"cpu": "cortex-m7",
"f_cpu": "480000000L",
"mcu": "stm32h747xih6",
"variant": "GIGA",
"product_line": "STM32H747xx",
"hwids": [
[
"0x2341",
"0x0266"
],
[
"0x2341",
"0x0366"
],
[
"0x2341",
"0x0466"
]
]
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"jlink_device": "STM32H747XI_M7",
"openocd_target": "stm32h7x_dual_bank"
},
"frameworks": [
"arduino"
],
"name": "Arduino Giga R1 Wifi",
"upload": {
"_maximum_ram_size": 294248,
"_maximum_size": 1048576,
"maximum_ram_size": 523624,
"maximum_size": 1966080,
"protocol": "dfu",
"protocols": [
"cmsis-dap",
"dfu",
"jlink",
"stlink",
"mbed"
],
"require_upload_port": true,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"offset_address": "0x08100000"
},
"url": "https://docs.arduino.cc/hardware/giga-r1-wifi",
"vendor": "Arduino"
}

View file

@ -0,0 +1,51 @@
{
"build": {
"arduino":{
"ldscript": "nrf52_xxaa.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52_DK",
"f_cpu": "64000000L",
"mcu": "nrf52840",
"variant": "nRF52DK",
"zephyr": {
"variant": "nrf52840dk_nrf52840"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"default_tools": [
"jlink"
],
"jlink_device": "nRF52840_xxAA",
"onboard_tools": [
"cmsis-dap",
"jlink"
],
"svd_path": "nrf52840.svd"
},
"frameworks": [
"arduino",
"mbed",
"zephyr"
],
"name": "Nordic nRF52840-DK",
"upload": {
"maximum_ram_size": 262144,
"maximum_size": 1048576,
"protocol": "jlink",
"protocols": [
"jlink",
"nrfjprog",
"stlink",
"blackmagic",
"cmsis-dap",
"mbed"
]
},
"url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/",
"vendor": "Nordic"
}

View file

@ -0,0 +1,56 @@
{
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
"usb_vid": "0x2E8A",
"usb_pid": "0x000A"
}
},
"core": "earlephilhower",
"cpu": "cortex-m0plus",
"extra_flags": "-DARDUINO_RASPBERRY_PI_PICO -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
"f_cpu": "133000000L",
"hwids": [
[
"0x2E8A",
"0x00C0"
],
[
"0x2E8A",
"0x000A"
]
],
"mcu": "rp2040",
"variant": "rpipico"
},
"debug": {
"jlink_device": "RP2040_M0_0",
"openocd_target": "rp2040.cfg",
"svd_path": "rp2040.svd"
},
"frameworks": [
"arduino"
],
"name": "Pico",
"upload": {
"maximum_ram_size": 262144,
"maximum_size": 2097152,
"require_upload_port": true,
"native_usb": true,
"use_1200bps_touch": true,
"wait_for_upload_port": false,
"protocol": "picotool",
"protocols": [
"blackmagic",
"cmsis-dap",
"jlink",
"raspberrypi-swd",
"picotool",
"picoprobe",
"pico-debug"
]
},
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
"vendor": "Raspberry Pi"
}

View file

@ -0,0 +1,56 @@
{
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "boot2_generic_03h_2_padded_checksum.S",
"usb_vid": "0x2E8A",
"usb_pid": "0x000B"
}
},
"core": "earlephilhower",
"cpu": "cortex-m33",
"extra_flags": "-D ARDUINO_RASPBERRY_PI_PICO_2 -DARDUINO_ARCH_RP2350 -DUSBD_MAX_POWER_MA=250",
"f_cpu": "133000000L",
"hwids": [
[
"0x2E8A",
"0x00C0"
],
[
"0x2E8A",
"0x000B"
]
],
"mcu": "rp2350",
"variant": "rpipico2"
},
"debug": {
"jlink_device": "RP2350_0",
"openocd_target": "rp2350.cfg",
"svd_path": "rp2350.svd"
},
"frameworks": [
"arduino"
],
"name": "Pico 2",
"upload": {
"maximum_ram_size": 524288,
"maximum_size": 2097152,
"require_upload_port": true,
"native_usb": true,
"use_1200bps_touch": true,
"wait_for_upload_port": false,
"protocol": "picotool",
"protocols": [
"blackmagic",
"cmsis-dap",
"jlink",
"raspberrypi-swd",
"picotool",
"picoprobe",
"pico-debug"
]
},
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
"vendor": "Raspberry Pi"
}

View file

@ -0,0 +1,56 @@
{
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
"usb_vid": "0x2E8A",
"usb_pid": "0xF00A"
}
},
"core": "earlephilhower",
"cpu": "cortex-m0plus",
"extra_flags": "-DARDUINO_RASPBERRY_PI_PICO_W -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
"f_cpu": "133000000L",
"hwids": [
[
"0x2E8A",
"0x00C0"
],
[
"0x2E8A",
"0xF00A"
]
],
"mcu": "rp2040",
"variant": "rpipicow"
},
"debug": {
"jlink_device": "RP2040_M0_0",
"openocd_target": "rp2040.cfg",
"svd_path": "rp2040.svd"
},
"frameworks": [
"arduino"
],
"name": "Pico W",
"upload": {
"maximum_ram_size": 262144,
"maximum_size": 2097152,
"require_upload_port": true,
"native_usb": true,
"use_1200bps_touch": true,
"wait_for_upload_port": false,
"protocol": "picotool",
"protocols": [
"blackmagic",
"cmsis-dap",
"jlink",
"raspberrypi-swd",
"picotool",
"picoprobe",
"pico-debug"
]
},
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
"vendor": "Raspberry Pi"
}

View file

@ -0,0 +1,33 @@
{
"build": {
"core": "silabs",
"f_cpu": "39000000L",
"mcu": "cortex-m33",
"variant": "thingplusmatter"
},
"connectivity": [
"bluetooth",
"thread",
"wifi",
"zigbee"
],
"debug": {
"jlink_device": "EFR32MG24B020F1536IM40",
"onboard_tools": [
"jlink"
],
"svd_path": "EFR32MG24B020F1536IM40.svd"
},
"frameworks": [
"arduino"
],
"name": "Sparkfun Thing Plus Matter",
"upload": {
"flash_size": "1536kB",
"maximum_ram_size": 262144,
"maximum_size": 1572864,
"protocol": "jlink"
},
"url": "https://www.sparkfun.com/products/20270",
"vendor": "Sparkfun"
}

View file

@ -0,0 +1,108 @@
import argparse
import os
import re
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
PROJECT_ROOT = HERE.parent
IS_GITHUB = "GITHUB_ACTIONS" in os.environ
def run_command(
cmd_list: list[str], shell: bool = False, check=False, capture_output: bool = False
) -> str | None:
check = check if check is not None else check
cmd = cmd_list if not shell else subprocess.list2cmdline(cmd_list)
result: subprocess.CompletedProcess = subprocess.run(
cmd, capture_output=capture_output, text=True, shell=shell, check=check
)
if not capture_output:
return None
stdout: str = result.stdout
stdout = stdout.strip()
return stdout
def parse_args():
parser = argparse.ArgumentParser(
description="Check compiled program size for a board"
)
parser.add_argument("board", help="Board name")
parser.add_argument(
"--max-size", type=int, required=False, help="Maximum allowed size"
)
parser.add_argument(
"--no-build",
action="store_true",
help="Skip compilation and check existing build",
)
parser.add_argument(
"--example",
default="Blink",
help="Example to compile (default: Blink)",
)
# Parse known args first
args, unknown = parser.parse_known_args()
# Add remaining arguments as extra_args
args.extra_args = unknown
return args
def main():
os.chdir(str(PROJECT_ROOT))
args = parse_args()
if not args.no_build:
cmd_list = [
"uv",
"run",
"ci/ci-compile.py",
args.board,
"--examples",
args.example,
] + args.extra_args
try:
run_command(cmd_list, shell=True, capture_output=IS_GITHUB, check=True)
except subprocess.CalledProcessError:
run_command(cmd_list, shell=True, capture_output=False, check=True)
output = run_command(
["uv", "run", "ci/compiled_size.py", "--board", args.board],
capture_output=True,
)
size_match = re.search(r": *(\d+)", output) # type: ignore
if not size_match:
print("Error: Unable to extract size from output")
print(f"Output: {output}")
sys.exit(1)
size = int(size_match.group(1))
if args.max_size is not None and args.max_size > 0:
max_size = args.max_size
if size > max_size:
print(f"{args.board} size {size} is greater than max size {max_size}")
print("::error::Compiled size exceeds maximum allowed size")
sys.exit(1)
else:
print(f"{args.board} size {size} is within the limit of {max_size}")
else:
if not args.max_size:
print("Warning: No max size specified")
elif args.max_size <= 0:
print("Warning: max size was <= 0 so no check was performed")
print(f"{args.board} size: {size}")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,5 @@
#!/bin/bash
# cd to the directory of the script
cd "$(dirname "$0")"
uv run ci-compile.py "$@"

View file

@ -0,0 +1,21 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
def main():
# Change to the directory of the script
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Change to the 'native' directory and run 'pio run'
os.chdir("native")
result = subprocess.run(["pio", "run"], check=True)
# Exit with the same status as the pio run command
sys.exit(result.returncode)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,317 @@
"""
Runs the compilation process for all examples on all boards in parallel.
Build artifacts are recycled within a board group so that subsequent ino
files are built faster.
"""
import argparse
import sys
import time
import warnings
from pathlib import Path
from ci.boards import Board, get_board # type: ignore
from ci.concurrent_run import ConcurrentRunArgs, concurrent_run
from ci.locked_print import locked_print
HERE = Path(__file__).parent.resolve()
LIBS = ["src", "ci"]
EXTRA_LIBS = [
"https://github.com/me-no-dev/ESPAsyncWebServer.git",
"ArduinoOTA",
"SD",
"FS",
"ESPmDNS",
"WiFi",
"WebSockets",
]
BUILD_FLAGS = ["-Wl,-Map,firmware.map", "-fopt-info-all=optimization_report.txt"]
# Default boards to compile for. You can use boards not defined here but
# if the board isn't part of the officially supported platformio boards then
# you will need to add the board to the ~/.platformio/platforms directory.
# prior to running this script. This happens automatically as of 2024-08-20
# with the github workflow scripts.
DEFAULT_BOARDS_NAMES = [
"apollo3_red",
"apollo3_thing_explorable",
"web", # work in progress
"uno", # Build is faster if this is first, because it's used for global init.
"esp32dev",
"esp01", # ESP8266
"esp32c3",
"attiny85",
"ATtiny1616",
"esp32c6",
"esp32s3",
"yun",
"digix",
"teensy30",
"teensy41",
"adafruit_feather_nrf52840_sense",
"xiaoblesense_adafruit",
"rpipico",
"rpipico2",
"uno_r4_wifi",
"esp32rmt_51",
"esp32dev_idf44",
"bluepill",
"esp32rmt_51",
"giga_r1",
"sparkfun_xrp_controller",
]
OTHER_BOARDS_NAMES = [
"nano_every",
"esp32-c2-devkitm-1",
]
# Examples to compile.
DEFAULT_EXAMPLES = [
"Apa102",
"Apa102HD",
"Apa102HDOverride",
"Blink",
"Blur",
"Chromancer",
"ColorPalette",
"ColorTemperature",
"Cylon",
"DemoReel100",
"FirstLight",
"Fire2012",
"Multiple/MultipleStripsInOneArray",
"Multiple/ArrayOfLedArrays",
"Noise",
"NoisePlayground",
"NoisePlusPalette",
"LuminescentGrand",
"Pacifica",
"Pride2015",
"RGBCalibrate",
"RGBSetDemo",
"RGBW",
"Overclock",
"RGBWEmulated",
"TwinkleFox",
"XYMatrix",
"FxGfx2Video",
"FxSdCard",
"FxCylon",
"FxDemoReel100",
"FxTwinkleFox",
"FxFire2012",
"FxNoisePlusPalette",
"FxPacifica",
"FxEngine",
"WS2816",
]
EXTRA_EXAMPLES: dict[Board, list[str]] = {
# ESP32DEV: ["EspI2SDemo"],
# ESP32_S3_DEVKITC_1: ["EspS3I2SDemo"],
}
def parse_args():
parser = argparse.ArgumentParser(
description="Compile FastLED examples for various boards."
)
# parser.add_argument(
# "--boards", type=str, help="Comma-separated list of boards to compile for"
# )
# needs to be a positional argument instead
parser.add_argument(
"boards",
type=str,
help="Comma-separated list of boards to compile for",
nargs="?",
)
parser.add_argument(
"--examples", type=str, help="Comma-separated list of examples to compile"
)
parser.add_argument(
"--exclude-examples", type=str, help="Examples that should be excluded"
)
parser.add_argument(
"--skip-init", action="store_true", help="Skip the initialization step"
)
parser.add_argument(
"--defines", type=str, help="Comma-separated list of compiler definitions"
)
parser.add_argument(
"--extra-packages",
type=str,
help="Comma-separated list of extra packages to install",
)
parser.add_argument(
"--add-extra-esp32-libs",
action="store_true",
help="Add extra libraries to the libraries list to check against compiler errors.",
)
parser.add_argument(
"--build-dir", type=str, help="Override the default build directory"
)
parser.add_argument(
"--no-project-options",
action="store_true",
help="Don't use custom project options",
)
parser.add_argument(
"--interactive",
action="store_true",
help="Enable interactive mode to choose a board",
)
# Passed by the github action to disable interactive mode.
parser.add_argument(
"--no-interactive", action="store_true", help="Disable interactive mode"
)
parser.add_argument(
"-v", "--verbose", action="store_true", help="Enable verbose output"
)
parser.add_argument(
"--supported-boards",
action="store_true",
help="Print the list of supported boards and exit",
)
args, unknown = parser.parse_known_args()
if unknown:
warnings.warn(f"Unknown arguments: {unknown}")
# if --interactive and --no-interative are both passed, --no-interactive takes precedence.
if args.interactive and args.no_interactive:
warnings.warn(
"Both --interactive and --no-interactive were passed, --no-interactive takes precedence."
)
args.interactive = False
return args
def remove_duplicates(items: list[str]) -> list[str]:
seen = set()
out = []
for item in items:
if item not in seen:
seen.add(item)
out.append(item)
return out
def choose_board_interactively(boards: list[str]) -> list[str]:
print("Available boards:")
boards = remove_duplicates(sorted(boards))
for i, board in enumerate(boards):
print(f"[{i}]: {board}")
print("[all]: All boards")
out: list[str] = []
while True:
try:
# choice = int(input("Enter the number of the board(s) you want to compile to: "))
input_str = input(
"Enter the number of the board(s) you want to compile to, or it's name(s): "
)
if "all" in input_str:
return boards
for board in input_str.split(","):
if board == "":
continue
if not board.isdigit():
out.append(board) # Assume it's a board name.
else:
index = int(board) # Find the board from the index.
if 0 <= index < len(boards):
out.append(boards[index])
else:
warnings.warn(f"invalid board index: {index}, skipping")
if not out:
print("Please try again.")
continue
return out
except ValueError:
print("Invalid input. Please enter a number.")
def resolve_example_path(example: str) -> Path:
example_path = HERE.parent / "examples" / example
if not example_path.exists():
raise FileNotFoundError(f"Example '{example}' not found at '{example_path}'")
return example_path
def create_concurrent_run_args(args: argparse.Namespace) -> ConcurrentRunArgs:
skip_init = args.skip_init
if args.interactive:
boards = choose_board_interactively(DEFAULT_BOARDS_NAMES + OTHER_BOARDS_NAMES)
else:
boards = args.boards.split(",") if args.boards else DEFAULT_BOARDS_NAMES
projects: list[Board] = []
for board in boards:
projects.append(get_board(board, no_project_options=args.no_project_options))
extra_examples: dict[Board, list[Path]] = {}
if args.examples is None:
for b, _examples in EXTRA_EXAMPLES.items():
resolved_examples = [resolve_example_path(example) for example in _examples]
extra_examples[b] = resolved_examples
examples = args.examples.split(",") if args.examples else DEFAULT_EXAMPLES
examples_paths = [resolve_example_path(example) for example in examples]
# now process example exclusions.
if args.exclude_examples:
exclude_examples = args.exclude_examples.split(",")
examples_paths = [
example
for example in examples_paths
if example.name not in exclude_examples
]
for exclude in exclude_examples:
examples.remove(exclude)
defines: list[str] = []
if args.defines:
defines.extend(args.defines.split(","))
extra_packages: list[str] = []
if args.extra_packages:
extra_packages.extend(args.extra_packages.split(","))
build_dir = args.build_dir
extra_scripts = "pre:lib/ci/ci-flags.py"
verbose = args.verbose
out: ConcurrentRunArgs = ConcurrentRunArgs(
projects=projects,
examples=examples_paths,
skip_init=skip_init,
defines=defines,
extra_packages=extra_packages,
libs=LIBS,
build_dir=build_dir,
extra_scripts=extra_scripts,
cwd=str(HERE.parent),
board_dir=(HERE / "boards").absolute().as_posix(),
build_flags=BUILD_FLAGS,
verbose=verbose,
extra_examples=extra_examples,
)
return out
def main() -> int:
"""Main function."""
args = parse_args()
if args.supported_boards:
print(",".join(DEFAULT_BOARDS_NAMES))
return 0
if args.add_extra_esp32_libs:
LIBS.extend(EXTRA_LIBS)
# Set the working directory to the script's parent directory.
run_args = create_concurrent_run_args(args)
start_time = time.time()
rtn = concurrent_run(args=run_args)
time_taken = time.strftime("%Mm:%Ss", time.gmtime(time.time() - start_time))
locked_print(f"Compilation finished in {time_taken}.")
return rtn
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
sys.exit(1)

View file

@ -0,0 +1,56 @@
import argparse
import os
import subprocess
import sys
from pathlib import Path
MINIMUM_REPORT_SEVERTIY = "medium"
MINIMUM_FAIL_SEVERTIY = "high"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Run cppcheck on the project")
parser.add_argument("board", nargs="?", help="Board to check, optional")
return parser.parse_args()
def main() -> int:
args = parse_args()
here = Path(__file__).parent
project_root = here.parent
build = project_root / ".build"
os.chdir(str(build))
if args.board:
build = build / args.board
if not build.exists():
print(f"Board {args.board} not found")
return 1
os.chdir(str(build))
else:
# Change to the first subdirectory in .build
subdirs = [d for d in os.listdir() if os.path.isdir(d)]
assert (
len(subdirs) == 1
), f"Expected exactly one subdirectory in {build}, instead got {subdirs}"
if subdirs:
os.chdir(subdirs[0])
# Run pio check command
cp = subprocess.run(
[
"pio",
"check",
"--skip-packages",
"--src-filters=+<lib/src/>",
f"--severity={MINIMUM_REPORT_SEVERTIY}",
f"--fail-on-defect={MINIMUM_FAIL_SEVERTIY}",
"--flags",
"--inline-suppr",
],
)
return cp.returncode
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,7 @@
# mypy: ignore-errors
# flake8: noqa
# ruff: skip
Import("env") # type: ignore
env.Append(CXXFLAGS=["-Wno-register"]) # type: ignore

View file

View file

@ -0,0 +1,175 @@
import subprocess
from pathlib import Path
def _run_command(command: list, show_output=False):
"""
Run a command using subprocess and capture the output.
Args:
command (list): Command to run.
show_output (bool): Print command and its output if True.
Returns:
str: Standard output of the command.
Raises:
RuntimeError: If the command fails.
"""
if show_output:
print(f"Running command: {' '.join(command)}")
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"Command failed: {' '.join(command)}\n{result.stderr}")
if show_output and result.stdout:
print(f"Command output: {result.stdout}")
return result.stdout
def _generate_linker_script(map_file: Path) -> Path:
"""
Generate a linker script based on map file information.
Args:
map_file (Path): Path to the map file.
Returns:
Path: Path to the generated linker script.
"""
linker_script_content = """
SECTIONS
{
.text 0x00000000 :
{
*(.text)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}
"""
linker_script_path = map_file.with_suffix(".ld")
linker_script_path.write_text(linker_script_content)
print(f"Generated linker script at: {linker_script_path}")
return linker_script_path
def _create_dummy_object_file(as_path: Path, dummy_obj_path: Path):
"""
Create a minimal dummy object file using the specified assembler.
Args:
as_path (Path): Path to the assembler executable.
dummy_obj_path (Path): Path to the dummy object file to be created.
"""
assembly_code = """
.section .text
.global _start
_start:
nop
"""
asm_file = dummy_obj_path.with_suffix(".s")
asm_file.write_text(assembly_code)
command = [str(as_path), "-o", str(dummy_obj_path), str(asm_file)]
print(f"Creating dummy object file: {dummy_obj_path}")
_run_command(command, show_output=True)
asm_file.unlink() # Clean up the temporary assembly file
def _create_dummy_elf(
ld_path: Path, linker_script: Path, dummy_obj: Path, output_elf: Path
):
"""
Create a dummy ELF file using the specified linker script and dummy object file.
Args:
ld_path (Path): Path to the ld executable.
linker_script (Path): Path to the linker script.
dummy_obj (Path): Path to the dummy object file.
output_elf (Path): Path to the output ELF file.
"""
command = [
str(ld_path),
str(dummy_obj),
"-T",
str(linker_script),
"-o",
str(output_elf),
]
print(f"Creating dummy ELF file: {output_elf}")
_run_command(command, show_output=True)
def _update_elf_sections(
objcopy_path: Path, bin_file: Path, elf_file: Path, section_name: str
):
"""
Update the ELF file sections with binary data.
Args:
objcopy_path (Path): Path to the objcopy executable.
bin_file (Path): Path to the binary file.
elf_file (Path): Path to the ELF file.
section_name (str): Name of the section to update.
"""
command = [
str(objcopy_path),
"--update-section",
f"{section_name}={bin_file}",
str(elf_file),
]
print(
f"Updating ELF file '{elf_file}' section '{section_name}' with binary file '{bin_file}'"
)
_run_command(command, show_output=True)
def bin_to_elf(
bin_file: Path,
map_file: Path,
as_path: Path,
ld_path: Path,
objcopy_path: Path,
output_elf: Path,
):
"""
Convert a binary file to ELF format.
Args:
bin_file (Path): Path to the input binary file.
map_file (Path): Path to the map file.
as_path (Path): Path to the assembler executable.
ld_path (Path): Path to the linker executable.
objcopy_path (Path): Path to the objcopy executable.
output_elf (Path): Path to the output ELF file.
Returns:
Path: Path to the generated ELF file.
"""
# Generate a linker script based on the map file
linker_script = _generate_linker_script(map_file)
# Create a minimal dummy object file
dummy_obj_path = bin_file.with_name("dummy.o")
_create_dummy_object_file(as_path, dummy_obj_path)
# Create a dummy ELF file using the generated linker script
_create_dummy_elf(ld_path, linker_script, dummy_obj_path, output_elf)
# Update the ELF sections with binary data
_update_elf_sections(objcopy_path, bin_file, output_elf, ".text")
# Clean up dummy object file
if dummy_obj_path.exists():
dummy_obj_path.unlink()
if linker_script.exists():
linker_script.unlink()
return output_elf

View file

@ -0,0 +1,318 @@
# dataclasses
import json
from dataclasses import dataclass
# An open source version of the esp-idf 5.1 platform for the ESP32 that
# gives esp32 boards the same build environment as the Arduino 2.3.1+.
# Set to a specific release, we may want to update this in the future.
ESP32_IDF_5_1_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip"
# TODO: Upgrade toolkit to 5.3
ESP32_IDF_5_3_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip"
ESP32_IDF_5_1_PIOARDUINO_LATEST = (
"https://github.com/pioarduino/platform-espressif32.git#develop"
)
ESP32_IDF_4_4_LATEST = "platformio/espressif32"
APOLLO3_2_2_0 = "https://github.com/nigelb/platform-apollo3blue"
# Top of trunk.
# ESP32_IDF_5_1_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32"
# Old fork that we were using
# ESP32_IDF_5_1_PIOARDUINO = "https://github.com/zackees/platform-espressif32#Arduino/IDF5"
ALL: list["Board"] = []
@dataclass
class Board:
board_name: str
real_board_name: str | None = None
platform: str | None = None
platform_needs_install: bool = False
use_pio_run: bool = (
False # some platforms like esp32-c2-devkitm-1 will only work with pio run
)
platform_packages: str | None = None
framework: str | None = None
board_build_mcu: str | None = None
board_build_core: str | None = None
board_build_filesystem_size: str | None = None
build_flags: list[str] | None = None # Reserved for future use.
defines: list[str] | None = None
board_partitions: str | None = None # Reserved for future use.
def __post_init__(self) -> None:
ALL.append(self)
def get_real_board_name(self) -> str:
return self.real_board_name if self.real_board_name else self.board_name
def to_dictionary(self) -> dict[str, list[str]]:
out: dict[str, list[str]] = {}
if self.real_board_name:
out[self.board_name] = [f"board={self.real_board_name}"]
options = out.setdefault(self.board_name, [])
if self.platform:
options.append(f"platform={self.platform}")
if self.platform_needs_install:
options.append("platform_needs_install=true")
if self.platform_packages:
options.append(f"platform_packages={self.platform_packages}")
if self.framework:
options.append(f"framework={self.framework}")
if self.board_build_core:
options.append(f"board_build.core={self.board_build_core}")
if self.board_build_mcu:
options.append(f"board_build.mcu={self.board_build_mcu}")
if self.board_build_filesystem_size:
options.append(
f"board_build.filesystem_size={self.board_build_filesystem_size}"
)
if self.defines:
for define in self.defines:
options.append(f"build_flags=-D{define}")
return out
def __repr__(self) -> str:
json_str = json.dumps(self.to_dictionary(), indent=4, sort_keys=True)
return json_str
def __hash__(self) -> int:
data_str = self.__repr__()
return hash(data_str)
# [env:sparkfun_xrp_controller]
# platform = https://github.com/maxgerhardt/platform-raspberrypi
# board = sparkfun_xrp_controller
# framework = arduino
# lib_deps = fastled/FastLED @ ^3.9.16
WEBTARGET = Board(
board_name="web",
)
DUE = Board(
board_name="due",
platform="atmelsam",
)
SPARKFUN_XRP_CONTROLLER_2350B = Board(
board_name="sparkfun_xrp_controller",
platform="https://github.com/maxgerhardt/platform-raspberrypi",
platform_needs_install=True,
)
APOLLO3_RED_BOARD = Board(
board_name="apollo3_red",
real_board_name="SparkFun_RedBoard_Artemis_ATP",
platform=APOLLO3_2_2_0,
platform_packages="framework-arduinoapollo3@https://github.com/sparkfun/Arduino_Apollo3#v2.2.0",
platform_needs_install=True,
)
APOLLO3_SPARKFUN_THING_PLUS_EXPLORERABLE = Board(
board_name="apollo3_thing_explorable",
real_board_name="SparkFun_Thing_Plus_expLoRaBLE",
platform=APOLLO3_2_2_0,
platform_packages="framework-arduinoapollo3@https://github.com/sparkfun/Arduino_Apollo3#v2.2.0",
platform_needs_install=True,
)
ESP32DEV = Board(
board_name="esp32dev",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32DEV_IDF3_3 = Board(
board_name="esp32dev_idf33",
real_board_name="esp32dev",
platform="espressif32@1.11.2",
)
ESP32DEV_IDF4_4 = Board(
board_name="esp32dev_idf44",
real_board_name="esp32dev",
platform=ESP32_IDF_4_4_LATEST,
)
GIGA_R1 = Board(
board_name="giga_r1",
platform="ststm32",
framework="arduino",
real_board_name="giga_r1_m7",
)
# ESP01 = Board(
# board_name="esp01",
# platform=ESP32_IDF_5_1_PIOARDUINO,
# )
ESP32_C2_DEVKITM_1 = Board(
board_name="esp32c2",
real_board_name="esp32-c2-devkitm-1",
use_pio_run=True,
platform="https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF5",
defines=["CONFIG_IDF_TARGET_ESP32C2=1"],
)
ESP32_C3_DEVKITM_1 = Board(
board_name="esp32c3",
real_board_name="esp32-c3-devkitm-1",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_C6_DEVKITC_1 = Board(
board_name="esp32c6",
real_board_name="esp32-c6-devkitc-1",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_S3_DEVKITC_1 = Board(
board_name="esp32s3",
real_board_name="seeed_xiao_esp32s3", # Seeed Xiao ESP32-S3 has psram.
platform=ESP32_IDF_5_3_PIOARDUINO,
defines=[
"BOARD_HAS_PSRAM",
],
build_flags=[ # Reserved for future use.
"-mfix-esp32-psram-cache-issue",
"-mfix-esp32-psram-cache-strategy=memw",
],
board_partitions="huge_app.csv", # Reserved for future use.
)
ESP32_S2_DEVKITM_1 = Board(
board_name="esp32s2",
real_board_name="esp32dev",
board_build_mcu="esp32s2",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_H2_DEVKITM_1 = Board(
board_name="esp32-h2-devkitm-1",
platform_needs_install=True, # Install platform package to get the boards
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ADA_FEATHER_NRF52840_SENSE = Board(
board_name="adafruit_feather_nrf52840_sense",
platform="nordicnrf52",
)
XIAOBLESENSE_ADAFRUIT_NRF52 = Board(
board_name="xiaoblesense_adafruit",
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True, # Install platform package to get the boards
)
XIAOBLESENSE_NRF52 = Board(
board_name="xiaoblesense",
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True,
)
NRF52840 = Board(
board_name="nrf52840_dk",
real_board_name="xiaoble_adafruit",
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True,
)
RPI_PICO = Board(
board_name="rpipico",
platform="https://github.com/maxgerhardt/platform-raspberrypi.git",
platform_needs_install=True, # Install platform package to get the boards
platform_packages="framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git",
framework="arduino",
board_build_core="earlephilhower",
board_build_filesystem_size="0.5m",
)
RPI_PICO2 = Board(
board_name="rpipico2",
platform="https://github.com/maxgerhardt/platform-raspberrypi.git",
platform_needs_install=True, # Install platform package to get the boards
platform_packages="framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git",
framework="arduino",
board_build_core="earlephilhower",
board_build_filesystem_size="0.5m",
)
BLUEPILL = Board(
board_name="bluepill",
real_board_name="bluepill_f103c8",
platform="ststm32",
)
# maple_mini_b20
MAPLE_MINI = Board(
board_name="maple_mini",
real_board_name="maple_mini_b20",
platform="ststm32",
)
ATTINY88 = Board(
board_name="attiny88",
platform="atmelavr",
)
# ATtiny1604
ATTINY1616 = Board(
board_name="ATtiny1616",
platform="atmelmegaavr",
)
UNO_R4_WIFI = Board(
board_name="uno_r4_wifi",
platform="renesas-ra",
)
NANO_EVERY = Board(
board_name="nano_every",
platform="atmelmegaavr",
)
ESP32DEV_I2S = Board(
board_name="esp32dev_i2s",
real_board_name="esp32dev",
platform=ESP32_IDF_4_4_LATEST,
)
ESP32S3_RMT51 = Board(
board_name="esp32rmt_51",
real_board_name="esp32-s3-devkitc-1",
platform_needs_install=True,
platform=ESP32_IDF_5_3_PIOARDUINO,
defines=[
"FASTLED_RMT5=1",
],
)
def _make_board_map(boards: list[Board]) -> dict[str, Board]:
# make board map, but assert on duplicate board names
board_map: dict[str, Board] = {}
for board in boards:
assert (
board.board_name not in board_map
), f"Duplicate board name: {board.board_name}"
board_map[board.board_name] = board
return board_map
_BOARD_MAP: dict[str, Board] = _make_board_map(ALL)
def get_board(board_name: str, no_project_options: bool = False) -> Board:
if no_project_options:
return Board(board_name=board_name)
if board_name not in _BOARD_MAP:
# empty board without any special overrides, assume platformio will know what to do with it.
return Board(board_name=board_name)
else:
return _BOARD_MAP[board_name]

View file

@ -0,0 +1,212 @@
import os
import shutil
import subprocess
from pathlib import Path
from threading import Lock
from ci.boards import Board # type: ignore
from ci.locked_print import locked_print
ERROR_HAPPENED = False
IS_GITHUB = "GITHUB_ACTIONS" in os.environ
FIRST_BUILD_LOCK = Lock()
USE_FIRST_BUILD_LOCK = IS_GITHUB
def errors_happened() -> bool:
"""Return whether any errors happened during the build."""
return ERROR_HAPPENED
def _fastled_js_is_parent_directory(p: Path) -> bool:
"""Check if fastled_js is a parent directory of the given path."""
# Check if fastled_js is a parent directory of p
return "fastled_js" in str(p.absolute())
def compile_for_board_and_example(
board: Board,
example: Path,
build_dir: str | None,
verbose_on_failure: bool,
libs: list[str] | None,
) -> tuple[bool, str]:
"""Compile the given example for the given board."""
global ERROR_HAPPENED # pylint: disable=global-statement
if board.board_name == "web":
locked_print(f"Skipping web target for example {example}")
return True, ""
board_name = board.board_name
use_pio_run = board.use_pio_run
real_board_name = board.get_real_board_name()
libs = libs or []
builddir = (
Path(build_dir) / board_name if build_dir else Path(".build") / board_name
)
builddir.mkdir(parents=True, exist_ok=True)
srcdir = builddir / "src"
# Remove the previous *.ino file if it exists, everything else is recycled
# to speed up the next build.
if srcdir.exists():
shutil.rmtree(srcdir, ignore_errors=False)
locked_print(f"*** Building example {example} for board {board_name} ***")
cwd: str | None = None
shell: bool = False
# Copy all files from the example directory to the "src" directory
for src_file in example.rglob("*"):
if src_file.is_file():
if _fastled_js_is_parent_directory(src_file):
# Skip the fastled_js folder, it's not needed for the build.
continue
src_dir = src_file.parent
path = src_dir.relative_to(example)
dst_dir = srcdir / path
os.makedirs(dst_dir, exist_ok=True)
locked_print(f"Copying {src_file} to {dst_dir / src_file.name}")
os.makedirs(srcdir, exist_ok=True)
shutil.copy(src_file, dst_dir / src_file.name)
# libs = ["src", "ci"]
if use_pio_run:
# we have to copy a few folders of pio ci in order to get this to work.
for lib in libs:
project_libdir = Path(lib)
assert project_libdir.exists()
build_lib = builddir / "lib" / lib
shutil.rmtree(build_lib, ignore_errors=True)
shutil.copytree(project_libdir, build_lib)
cwd = str(builddir)
cmd_list = [
"pio",
"run",
]
# in this case we need to manually copy the example to the src directory
# because platformio doesn't support building a single file.
# ino_file = example / f"{example.name}.ino"
else:
cmd_list = [
"pio",
"ci",
"--board",
real_board_name,
*[f"--lib={lib}" for lib in libs],
"--keep-build-dir",
f"--build-dir={builddir.as_posix()}",
]
cmd_list.append(f"{example.as_posix()}/*ino")
cmd_str = subprocess.list2cmdline(cmd_list)
msg_lsit = [
"\n\n******************************",
f"* Running command in cwd: {cwd if cwd else os.getcwd()}",
f"* {cmd_str}",
"******************************\n",
]
msg = "\n".join(msg_lsit)
locked_print(msg)
result = subprocess.run(
cmd_list,
cwd=cwd,
shell=shell,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
stdout = result.stdout
# replace all instances of "lib/src" => "src" so intellisense can find the files
# with one click.
stdout = stdout.replace("lib/src", "src").replace("lib\\src", "src")
locked_print(stdout)
if result.returncode != 0:
if not verbose_on_failure:
ERROR_HAPPENED = True
return False, stdout
if ERROR_HAPPENED:
return False, ""
ERROR_HAPPENED = True
locked_print(
f"*** Error compiling example {example} for board {board_name} ***"
)
# re-running command with verbose output to see what the defines are.
cmd_list.append("-v")
cmd_str = subprocess.list2cmdline(cmd_list)
msg_lsit = [
"\n\n******************************",
"* Re-running failed command but with verbose output:",
f"* {cmd_str}",
"******************************\n",
]
msg = "\n".join(msg_lsit)
locked_print(msg)
result = subprocess.run(
cmd_list,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
stdout = result.stdout
# replace all instances of "lib/src" => "src" so intellisense can find the files
# with one click.
stdout = stdout.replace("lib/src", "src").replace("lib\\src", "src")
stdout = (
stdout
+ "\n\nThis is a second attempt, but with verbose output, look above for compiler errors.\n"
)
locked_print(stdout)
return False, stdout
locked_print(f"*** Finished building example {example} for board {board_name} ***")
return True, stdout
# Function to process task queues for each board
def compile_examples(
board: Board,
examples: list[Path],
build_dir: str | None,
verbose_on_failure: bool,
libs: list[str] | None,
) -> tuple[bool, str]:
"""Process the task queue for the given board."""
global ERROR_HAPPENED # pylint: disable=global-statement
board_name = board.board_name
is_first = True
for example in examples:
example = example.relative_to(Path(".").resolve())
if ERROR_HAPPENED:
return True, ""
locked_print(f"\n*** Building {example} for board {board_name} ***")
if is_first:
locked_print(
f"*** Building for first example {example} board {board_name} ***"
)
if is_first and USE_FIRST_BUILD_LOCK:
with FIRST_BUILD_LOCK:
# Github runners are memory limited and the first job is the most
# memory intensive since all the artifacts are being generated in parallel.
success, message = compile_for_board_and_example(
board=board,
example=example,
build_dir=build_dir,
verbose_on_failure=verbose_on_failure,
libs=libs,
)
else:
success, message = compile_for_board_and_example(
board=board,
example=example,
build_dir=build_dir,
verbose_on_failure=verbose_on_failure,
libs=libs,
)
is_first = False
if not success:
ERROR_HAPPENED = True
return (
False,
f"Error building {example} for board {board_name}. stdout:\n{message}",
)
return True, ""

View file

@ -0,0 +1,157 @@
import os
import time
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from pathlib import Path
from ci.boards import Board # type: ignore
from ci.compile_for_board import compile_examples, errors_happened
from ci.cpu_count import cpu_count
from ci.create_build_dir import create_build_dir
from ci.locked_print import locked_print
# Board initialization doesn't take a lot of memory or cpu so it's safe to run in parallel
PARRALLEL_PROJECT_INITIALIZATION = (
os.environ.get("PARRALLEL_PROJECT_INITIALIZATION", "1") == "1"
)
def _banner_print(msg: str) -> None:
"""Print a banner message."""
# will produce
#######
# msg #
#######
lines = msg.splitlines()
for line in lines:
print("#" * (len(line) + 4))
print(f"# {line} #")
print("#" * (len(line) + 4))
@dataclass
class ConcurrentRunArgs:
projects: list[Board]
examples: list[Path]
skip_init: bool
defines: list[str]
extra_packages: list[str]
libs: list[str] | None
build_dir: str | None
extra_scripts: str | None
cwd: str | None
board_dir: str | None
build_flags: list[str] | None
verbose: bool = False
extra_examples: dict[Board, list[Path]] | None = None
def concurrent_run(
args: ConcurrentRunArgs,
) -> int:
projects = args.projects
examples = args.examples
skip_init = args.skip_init
defines = args.defines
extra_packages = args.extra_packages
build_dir = args.build_dir
extra_scripts = args.extra_scripts
cwd = args.cwd
start_time = time.time()
first_project = projects[0]
prev_cwd: str | None = None
board_dir = args.board_dir
libs = args.libs
extra_examples: dict[Board, list[Path]] = args.extra_examples or {}
if cwd:
prev_cwd = os.getcwd()
locked_print(f"Changing to directory {cwd}")
os.chdir(cwd)
start_time = time.time()
create_build_dir(
board=first_project,
defines=defines,
no_install_deps=skip_init,
extra_packages=extra_packages,
build_dir=build_dir,
board_dir=board_dir,
build_flags=args.build_flags,
extra_scripts=extra_scripts,
)
diff = time.time() - start_time
msg = f"Build directory created in {diff:.2f} seconds for board"
locked_print(msg)
verbose = args.verbose
# This is not memory/cpu bound but is instead network bound so we can run one thread
# per board to speed up the process.
parallel_init_workers = 1 if not PARRALLEL_PROJECT_INITIALIZATION else len(projects)
# Initialize the build directories for all boards
with ThreadPoolExecutor(max_workers=parallel_init_workers) as executor:
future_to_board: dict[Future, Board] = {}
for board in projects:
future = executor.submit(
create_build_dir,
board,
defines,
skip_init,
extra_packages,
build_dir,
board_dir,
args.build_flags,
extra_scripts,
)
future_to_board[future] = board
for future in as_completed(future_to_board):
board = future_to_board[future]
success, msg = future.result()
if not success:
locked_print(
f"Error initializing build_dir for board {board.board_name}:\n{msg}"
)
# cancel all other tasks
for f in future_to_board:
f.cancel()
return 1
else:
locked_print(
f"Finished initializing build_dir for board {board.board_name}"
)
init_end_time = time.time()
init_time = (init_end_time - start_time) / 60
locked_print(f"\nAll build directories initialized in {init_time:.2f} minutes.")
errors: list[str] = []
# Run the compilation process
num_cpus = max(1, min(cpu_count(), len(projects)))
with ThreadPoolExecutor(max_workers=num_cpus) as executor:
future_to_board = {
executor.submit(
compile_examples,
board,
examples + extra_examples.get(board, []),
build_dir,
verbose,
libs=libs,
): board
for board in projects
}
for future in as_completed(future_to_board):
board = future_to_board[future]
success, msg = future.result()
if not success:
msg = f"Compilation failed for board {board}: {msg}"
errors.append(msg)
locked_print(f"Compilation failed for board {board}: {msg}.\nStopping.")
for f in future_to_board:
f.cancel()
break
if prev_cwd:
locked_print(f"Changing back to directory {prev_cwd}")
os.chdir(prev_cwd)
if errors_happened():
locked_print("\nDone. Errors happened during compilation.")
locked_print("\n".join(errors))
return 1
return 0

View file

@ -0,0 +1,8 @@
import os
def cpu_count() -> int:
"""Get the number of CPUs."""
if "GITHUB_ACTIONS" in os.environ:
return 4
return os.cpu_count() or 1

View file

@ -0,0 +1,223 @@
import json
import os
import shutil
import subprocess
import warnings
from pathlib import Path
from ci.boards import Board # type: ignore
from ci.locked_print import locked_print
def _install_global_package(package: str) -> None:
# example pio pkg -g -p "https://github.com/maxgerhardt/platform-raspberrypi.git".
locked_print(f"*** Installing {package} ***")
cmd_list = [
"pio",
"pkg",
"install",
"-g",
"-p",
package,
]
cmd_str = subprocess.list2cmdline(cmd_list)
locked_print(f"Running command:\n\n{cmd_str}\n\n")
result = subprocess.run(
cmd_str,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=True,
)
locked_print(result.stdout)
locked_print(f"*** Finished installing {package} ***")
def insert_tool_aliases(meta_json: dict[str, dict]) -> None:
for board in meta_json.keys():
aliases: dict[str, str | None] = {}
cc_path = meta_json[board].get("cc_path")
cc_path = Path(cc_path) if cc_path else None
if cc_path:
# get the prefix of the base name of the compiler.
cc_base = cc_path.name
parent = cc_path.parent
prefix = cc_base.split("gcc")[0]
suffix = cc_path.suffix
# create the aliases
for tool in [
"gcc",
"g++",
"ar",
"objcopy",
"objdump",
"size",
"nm",
"ld",
"as",
"ranlib",
"strip",
"c++filt",
"readelf",
"addr2line",
]:
name = f"{prefix}{tool}" + suffix
tool_path = Path(str(parent / name))
if tool_path.exists():
aliases[tool] = str(tool_path)
else:
aliases[tool] = None
meta_json[board]["aliases"] = aliases
def remove_readonly(func, path, _):
"Clear the readonly bit and reattempt the removal"
if os.name == "nt":
os.system(f"attrib -r {path}")
else:
try:
os.chmod(path, 0o777)
except Exception:
print(f"Error removing readonly attribute from {path}")
func(path)
def create_build_dir(
board: Board,
defines: list[str],
no_install_deps: bool,
extra_packages: list[str],
build_dir: str | None,
board_dir: str | None,
build_flags: list[str] | None,
extra_scripts: str | None,
) -> tuple[bool, str]:
"""Create the build directory for the given board."""
# filter out "web" board because it's not a real board.
if board.board_name == "web":
locked_print(f"Skipping web target for board {board.board_name}")
return True, ""
if board.defines:
defines.extend(board.defines)
# remove duplicates
defines = list(set(defines))
board_name = board.board_name
real_board_name = board.get_real_board_name()
locked_print(f"*** Initializing environment for {board_name} ***")
# builddir = Path(build_dir) / board if build_dir else Path(".build") / board
build_dir = build_dir or ".build"
builddir = Path(build_dir) / board_name
builddir.mkdir(parents=True, exist_ok=True)
# if lib directory (where FastLED lives) exists, remove it. This is necessary to run on
# recycled build directories for fastled to update. This is a fast operation.
srcdir = builddir / "lib"
if srcdir.exists():
shutil.rmtree(srcdir, onerror=remove_readonly)
platformio_ini = builddir / "platformio.ini"
if platformio_ini.exists():
try:
platformio_ini.unlink()
except OSError as e:
locked_print(f"Error removing {platformio_ini}: {e}")
if board_dir:
dst_dir = builddir / "boards"
if dst_dir.exists():
shutil.rmtree(dst_dir)
shutil.copytree(str(board_dir), str(builddir / "boards"))
if board.platform_needs_install:
if board.platform:
try:
_install_global_package(board.platform)
except subprocess.CalledProcessError as e:
stdout = e.stdout
return False, stdout
else:
warnings.warn("Platform install was specified but no platform was given.")
cmd_list = [
"pio",
"project",
"init",
"--project-dir",
builddir.as_posix(),
"--board",
real_board_name,
]
if board.platform:
cmd_list.append(f"--project-option=platform={board.platform}")
if board.platform_packages:
cmd_list.append(f"--project-option=platform_packages={board.platform_packages}")
if board.framework:
cmd_list.append(f"--project-option=framework={board.framework}")
if board.board_build_core:
cmd_list.append(f"--project-option=board_build.core={board.board_build_core}")
if board.board_build_filesystem_size:
cmd_list.append(
f"--project-option=board_build.filesystem_size={board.board_build_filesystem_size}"
)
if build_flags is not None:
for build_flag in build_flags:
cmd_list.append(f"--project-option=build_flags={build_flag}")
if defines:
build_flags_str = " ".join(f"-D{define}" for define in defines)
cmd_list.append(f"--project-option=build_flags={build_flags_str}")
if extra_packages:
cmd_list.append(f'--project-option=lib_deps={",".join(extra_packages)}')
if no_install_deps:
cmd_list.append("--no-install-dependencies")
if extra_scripts:
p = Path(extra_scripts)
cmd_list.append(f"--project-option=extra_scripts={p.resolve()}")
cmd_str = subprocess.list2cmdline(cmd_list)
locked_print(f"\n\nRunning command:\n {cmd_str}\n")
result = subprocess.run(
cmd_str,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
stdout = result.stdout
locked_print(result.stdout)
if result.returncode != 0:
locked_print(f"*** Error setting up board {board_name} ***")
return False, stdout
locked_print(f"*** Finished initializing environment for board {board_name} ***")
# dumping enviorment variables to help debug.
# this is the command: pio run --target envdump
cwd = str(builddir.resolve())
cmd_list = [
"pio",
"project",
"metadata",
"--json-output",
]
cmd_str = subprocess.list2cmdline(cmd_list)
stdout = subprocess.run(
cmd_list,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
).stdout
try:
data = json.loads(stdout)
# now dump the values to the file at the root of the build directory.
matadata_json = builddir / "build_info.json"
try:
insert_tool_aliases(data)
formatted = json.dumps(data, indent=4, sort_keys=True)
with open(matadata_json, "w") as f:
f.write(formatted)
except Exception:
with open(matadata_json, "w") as f:
f.write(stdout)
except json.JSONDecodeError:
msg = f"build_info.json will not be generated because of error because stdout does not look like a json file:\n#### STDOUT ####\n{stdout}\n#### END STDOUT ####\n"
locked_print(msg)
return True, stdout

View file

@ -0,0 +1,205 @@
import subprocess
from dataclasses import dataclass
from pathlib import Path
def run_command(command: list, show_output=False):
"""
Run a command using subprocess and capture the output.
Args:
command (list): Command to run.
show_output (bool): Print command and its output if True.
Returns:
str: Standard output of the command.
Raises:
RuntimeError: If the command fails.
"""
if show_output:
print(f"Running command: {' '.join(command)}")
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"Command failed: {' '.join(command)}\n{result.stderr}")
if show_output and result.stdout:
print(f"Command output: {result.stdout}")
return result.stdout
def analyze_elf_file(objdump_path: Path, cppfilt_path: Path, elf_file: Path):
"""
Analyze the ELF file using objdump to display its contents.
Args:
objdump_path (Path): Path to the objdump executable.
cppfilt_path (Path): Path to the c++filt executable.
elf_file (Path): Path to the ELF file.
"""
command = [str(objdump_path), "-h", str(elf_file)] # "-h" option shows headers.
print(f"Analyzing ELF file: {elf_file}")
output = run_command(command, show_output=True)
print("\nELF File Analysis:")
print(output)
list_symbols_and_sizes(objdump_path, cppfilt_path, elf_file)
def cpp_filt(cppfilt_path: Path, input_text: str) -> str:
"""
Demangle C++ symbols using c++filt.
Args:
cppfilt_path (Path): Path to c++filt executable.
input_text (str): Text to demangle.
Returns:
str: Demangled text.
"""
command = [str(cppfilt_path), "-t", "-n"]
print(f"Running c++filt on input text with {cppfilt_path}")
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
stdout, stderr = process.communicate(input=input_text)
if process.returncode != 0:
raise RuntimeError(f"Error running c++filt: {stderr}")
return stdout
def dump_symbol_sizes(nm_path: Path, cpp_filt_path: Path, elf_file: Path) -> str:
nm_command = [
str(nm_path),
"-S",
"--size-sort",
str(elf_file),
]
print(f"Listing symbols and sizes in ELF file: {elf_file}")
print("Running command: ", " ".join(nm_command))
nm_result = subprocess.run(
nm_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if nm_result.returncode != 0:
raise RuntimeError(f"Error running nm command: {nm_result.stderr}")
cpp_filt_command = [str(cpp_filt_path), "--no-strip-underscore"]
print("Running c++filt command: ", " ".join(cpp_filt_command))
cpp_filt_result = subprocess.run(
cpp_filt_command,
input=nm_result.stdout,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if cpp_filt_result.returncode != 0:
raise RuntimeError(f"Error running c++filt command: {cpp_filt_result.stderr}")
# now reverse sort the lines
lines = cpp_filt_result.stdout.splitlines()
@dataclass
class Entry:
address: str
size: int
everything_else: str
def parse_line(line: str) -> Entry:
address, size, *rest = line.split()
return Entry(address, int(size, 16), " ".join(rest))
data: list[Entry] = [parse_line(line) for line in lines]
data.sort(key=lambda x: x.size, reverse=True)
lines = [f"{d.size:6d} {d.everything_else}" for d in data]
return "\n".join(lines)
def demangle_symbol(cppfilt_path: Path, symbol: str) -> str:
"""
Demangle a C++ symbol using c++filt.
Args:
cppfilt_path (Path): Path to the c++filt executable.
symbol (str): The symbol to demangle.
Returns:
str: The demangled symbol.
"""
command = [str(cppfilt_path), symbol]
return run_command(command, show_output=False).strip()
def list_symbols_and_sizes(objdump_path: Path, cppfilt_path: Path, elf_file: Path):
"""
List all symbols and their sizes from the ELF file using objdump.
Args:
objdump_path (Path): Path to the objdump executable.
cppfilt_path (Path): Path to the c++filt executable.
elf_file (Path): Path to the ELF file.
"""
command = [
str(objdump_path),
"-t",
str(elf_file),
] # "-t" option lists symbols with sizes.
print(f"Listing symbols and sizes in ELF file: {elf_file}")
output = run_command(command, show_output=False)
symbols = []
for line in output.splitlines():
parts = line.split()
# Expected parts length can vary, check if size and section index (parts[2] & parts[4]) are valid
if len(parts) > 5 and parts[2].isdigit() and parts[4].startswith("."):
symbol = {
"name": parts[-1],
"size": int(parts[2], 16), # size is in hex format
"section": parts[4],
"type": parts[3],
}
symbols.append(symbol)
if symbols:
print("\nSymbols and Sizes in ELF File:")
for symbol in symbols:
demangled_name = demangle_symbol(cppfilt_path, symbol["name"])
print(
f"Symbol: {demangled_name}, Size: {symbol['size']} bytes, Type: {symbol['type']}, Section: {symbol['section']}"
)
else:
print("No symbols found or unable to parse symbols correctly.")
def check_elf_format(objdump_path: Path, elf_file: Path):
"""
Check the format of the ELF file using objdump to confirm it's being read correctly.
Args:
objdump_path (Path): Path to the objdump executable.
elf_file (Path): Path to the ELF file.
"""
command = [str(objdump_path), "-f", str(elf_file)]
print(f"Checking ELF file format: {elf_file}")
output = run_command(command, show_output=True)
print("\nELF File Format Information:")
print(output)
def check_section_contents(objdump_path: Path, elf_file: Path):
"""
Dump the contents of all sections in the ELF file using objdump.
Args:
objdump_path (Path): Path to the objdump executable.
elf_file (Path): Path to the ELF file.
"""
command = [str(objdump_path), "-s", str(elf_file)]
print(f"Dumping all sections of ELF file: {elf_file}")
output = run_command(command, show_output=True)
print("\nELF File Sections Content:")
print(output)

View file

@ -0,0 +1,11 @@
from threading import Lock
PRINT_LOCK = Lock()
def locked_print(string: str):
"""Print with a lock to prevent garbled output for multiple threads."""
with PRINT_LOCK:
# print only prints so much, break up the string into lines
for line in string.splitlines():
print(line)

View file

@ -0,0 +1,19 @@
import os
from pathlib import Path
def map_dump(map_file: Path) -> None:
# os.system("uv run fpvgcc ci/tests/uno/firmware.map --lmap root")
cmds = [
f"uv run fpvgcc {map_file} --sar",
f"uv run fpvgcc {map_file} --lmap root",
f"uv run fpvgcc {map_file} --uf",
f"uv run fpvgcc {map_file} --uregions",
# --usections
f"uv run fpvgcc {map_file} --usections",
f"uv run fpvgcc {map_file} --la",
]
for cmd in cmds:
print("\nRunning command: ", cmd)
os.system(cmd)

View file

@ -0,0 +1,5 @@
from pathlib import Path
_HERE = Path(__file__).resolve().parent
PROJECT_ROOT = _HERE.parent.parent
BUILD = PROJECT_ROOT / ".build"

View file

@ -0,0 +1,139 @@
import subprocess
import threading
from pathlib import Path
class RunningProcess:
"""
A class to manage and stream output from a running subprocess.
This class provides functionality to execute shell commands, stream their output
in real-time, and control the subprocess execution.
"""
def __init__(
self,
command: str | list[str],
cwd: Path | None = None,
check: bool = False,
auto_run: bool = True,
echo: bool = True,
):
"""
Initialize the RunningProcess instance. Note that stderr is merged into stdout!!
Args:
command (str): The command to execute.
cwd (Path | None): The working directory to execute the command in.
check (bool): If True, raise an exception if the command returns a non-zero exit code.
auto_run (bool): If True, automatically run the command when the instance is created.
echo (bool): If True, print the output of the command to the console in real-time.
"""
if isinstance(command, list):
command = subprocess.list2cmdline(command)
self.command = command
self.cwd = str(cwd) if cwd is not None else None
self.buffer: list[str] = []
self.proc: subprocess.Popen | None = None
self.check = check
self.auto_run = auto_run
self.echo = echo
self.reader_thread: threading.Thread | None = None
self.shutdown: threading.Event = threading.Event()
if auto_run:
self.run()
def run(self) -> None:
"""
Execute the command and stream its output in real-time.
Returns:
str: The full output of the command.
Raises:
subprocess.CalledProcessError: If the command returns a non-zero exit code.
"""
self.proc = subprocess.Popen(
self.command,
shell=True,
cwd=self.cwd,
bufsize=256,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Merge stderr into stdout
text=True, # Automatically decode bytes to str
)
def output_reader():
try:
assert self.proc is not None
assert self.proc.stdout is not None
for line in iter(self.proc.stdout.readline, ""):
if self.shutdown.is_set():
break
line = line.rstrip()
if self.echo:
print(line) # Print to console in real time
self.buffer.append(line)
finally:
if self.proc and self.proc.stdout:
self.proc.stdout.close()
# Start output reader thread
self.reader_thread = threading.Thread(target=output_reader, daemon=True)
self.reader_thread.start()
def wait(self) -> int:
"""
Wait for the process to complete.
Raises:
ValueError: If the process hasn't been started.
"""
if self.proc is None:
raise ValueError("Process is not running.")
rtn = self.proc.wait()
assert self.reader_thread is not None
self.reader_thread.join(timeout=1)
return rtn
def kill(self) -> None:
"""
Immediately terminate the process with SIGKILL.
Raises:
ValueError: If the process hasn't been started.
"""
if self.proc is None:
return
self.shutdown.set()
self.proc.kill()
def terminate(self) -> None:
"""
Gracefully terminate the process with SIGTERM.
Raises:
ValueError: If the process hasn't been started.
"""
if self.proc is None:
raise ValueError("Process is not running.")
self.shutdown.set()
self.proc.terminate()
@property
def returncode(self) -> int | None:
if self.proc is None:
return None
return self.proc.returncode
@property
def stdout(self) -> str:
"""
Get the complete stdout output of the process.
Returns:
str: The complete stdout output as a string, or None if process hasn't completed.
"""
self.wait()
return "\n".join(self.buffer)

View file

@ -0,0 +1,205 @@
import json
import os
import sys
from dataclasses import dataclass
from pathlib import Path
from ci.paths import BUILD
@dataclass
class Tools:
as_path: Path
ld_path: Path
objcopy_path: Path
objdump_path: Path
cpp_filt_path: Path
nm_path: Path
def load_tools(build_info_path: Path) -> Tools:
build_info = json.loads(build_info_path.read_text())
board_info = build_info[next(iter(build_info))]
aliases = board_info["aliases"]
as_path = Path(aliases["as"])
ld_path = Path(aliases["ld"])
objcopy_path = Path(aliases["objcopy"])
objdump_path = Path(aliases["objdump"])
cpp_filt_path = Path(aliases["c++filt"])
nm_path = Path(aliases["nm"])
if sys.platform == "win32":
as_path = as_path.with_suffix(".exe")
ld_path = ld_path.with_suffix(".exe")
objcopy_path = objcopy_path.with_suffix(".exe")
objdump_path = objdump_path.with_suffix(".exe")
cpp_filt_path = cpp_filt_path.with_suffix(".exe")
nm_path = nm_path.with_suffix(".exe")
out = Tools(as_path, ld_path, objcopy_path, objdump_path, cpp_filt_path, nm_path)
tools = [as_path, ld_path, objcopy_path, objdump_path, cpp_filt_path, nm_path]
for tool in tools:
if not tool.exists():
raise FileNotFoundError(f"Tool not found: {tool}")
return out
def _list_builds() -> list[Path]:
str_paths = os.listdir(BUILD)
paths = [BUILD / p for p in str_paths]
dirs = [p for p in paths if p.is_dir()]
return dirs
def _check_build(build: Path) -> bool:
# 1. should contain a build_info.json file
# 2. should contain a .pio/build directory
has_build_info = (build / "build_info.json").exists()
has_pio_build = (build / ".pio" / "build").exists()
return has_build_info and has_pio_build
def _prompt_build() -> Path:
builds = _list_builds()
if not builds:
print("Error: No builds found", file=sys.stderr)
sys.exit(1)
print("Select a build:")
for i, build in enumerate(builds):
print(f" [{i}]: {build}")
while True:
try:
which = int(input("Enter the number of the build to use: "))
if 0 <= which < len(builds):
valid = _check_build(BUILD / builds[which])
if valid:
return BUILD / builds[which]
print("Error: Invalid build", file=sys.stderr)
else:
print("Error: Invalid selection", file=sys.stderr)
continue
except ValueError:
print("Error: Invalid input", file=sys.stderr)
continue
def _prompt_object_file(build: Path) -> Path:
# Look for object files in .pio/build directory
build_dir = build / ".pio" / "build"
object_files = []
# Walk through build directory to find .o files
for root, _, files in os.walk(build_dir):
for file in files:
if file.endswith(".o") and "FrameworkArduino" not in file:
full_path = Path(root) / file
if "FrameworkArduino" not in full_path.parts:
object_files.append(full_path)
if not object_files:
print("Error: No object files found", file=sys.stderr)
sys.exit(1)
print("\nSelect an object file:")
for i, obj_file in enumerate(object_files):
print(f" [{i}]: {obj_file.relative_to(build_dir)}")
while True:
try:
which = int(input("Enter the number of the object file to use: "))
if 0 <= which < len(object_files):
return object_files[which]
print("Error: Invalid selection", file=sys.stderr)
except ValueError:
print("Error: Invalid input", file=sys.stderr)
continue
def cli() -> None:
import argparse
parser = argparse.ArgumentParser(
description="Dump object file information using build tools"
)
parser.add_argument(
"build_path",
type=Path,
nargs="?",
help="Path to build directory containing build info JSON file",
)
parser.add_argument(
"--symbols", action="store_true", help="Dump symbol table using nm"
)
parser.add_argument(
"--disassemble", action="store_true", help="Dump disassembly using objdump"
)
args = parser.parse_args()
build_path = args.build_path
symbols = args.symbols
disassemble = args.disassemble
# Check if object file was provided and exists
if build_path is None:
build_path = _prompt_build()
else:
if not _check_build(build_path):
print("Error: Invalid build directory", file=sys.stderr)
sys.exit(1)
assert build_path is not None
assert build_path
build_info_path = build_path / "build_info.json"
assert build_info_path.exists(), f"File not found: {build_info_path}"
tools = load_tools(build_info_path)
if not symbols and not disassemble:
while True:
print(
"Error: Please specify at least one action to perform", file=sys.stderr
)
action = input(
"Enter 's' to dump symbols, 'd' to disassemble, or 'q' to quit: "
)
if action == "s":
symbols = True
break
elif action == "d":
disassemble = True
break
elif action == "q":
sys.exit(0)
else:
print("Error: Invalid action", file=sys.stderr)
object_file = _prompt_object_file(build_path)
if symbols:
import subprocess
cmd_str = subprocess.list2cmdline(
[str(tools.objdump_path), str(object_file), "--syms"]
)
print(f"Running command: {cmd_str}")
subprocess.run([str(tools.objdump_path), str(object_file)])
if disassemble:
import subprocess
cmd_str = subprocess.list2cmdline(
[str(tools.objdump_path), "-d", str(object_file)]
)
print(f"Running command: {cmd_str}")
subprocess.run([str(tools.objdump_path), "-d", str(object_file)])
if not (symbols or disassemble):
parser.print_help()
if __name__ == "__main__":
try:
cli()
except KeyboardInterrupt:
print("Exiting...")
sys.exit(1)

View file

@ -0,0 +1,58 @@
import argparse
import json
from pathlib import Path
def _get_board_info(path: Path) -> dict:
build_info = json.loads(path.read_text())
assert build_info.keys(), f"No boards found in {build_info}"
assert (
len(build_info.keys()) == 1
), f"Multiple boards found in {build_info}, so correct board should be specified"
return build_info[next(iter(build_info))]
def check_firmware_size(board: str) -> int:
root_build_dir = Path(".build") / board
build_info_json = root_build_dir / "build_info.json"
board_info = _get_board_info(build_info_json)
assert board_info, f"Board {board} not found in {build_info_json}"
prog_path = Path(board_info["prog_path"])
base_path = prog_path.parent
suffixes = [".bin", ".hex", ".uf2"]
firmware: Path
for suffix in suffixes:
firmware = base_path / f"firmware{suffix}"
if firmware.exists():
break
else:
msg = (
", ".join([f"firmware{suffix}" for suffix in suffixes])
+ f" not found in {base_path}"
)
raise FileNotFoundError(msg)
return firmware.stat().st_size
def main(board: str):
try:
size = check_firmware_size(board)
print(f"Firmware size for {board}: {size} bytes")
except FileNotFoundError as e:
print(f"Error: {e}")
except json.JSONDecodeError:
print(f"Error: Unable to parse build_info.json for {board}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Check FastLED firmware size for the specified board."
)
parser.add_argument(
"--board", type=str, required=True, help="Board to check firmware size for"
)
args = parser.parse_args()
main(args.board)

View file

@ -0,0 +1,233 @@
import argparse
import csv
import json
import os
import shutil
import subprocess
from pathlib import Path
import dateutil.parser # type: ignore
HERE = Path(__file__).resolve().parent
def run_command(command):
process = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
)
output, error = process.communicate()
return output.decode("utf-8"), error.decode("utf-8")
def step_back_commits(steps):
step_back_command = f"git reset --hard HEAD~{steps}"
output, error = run_command(step_back_command)
if error:
print(f"Error stepping back {steps} commit(s): {error}")
return False
return True
def check_firmware_size(board: str) -> int:
root_build_dir = Path(".build") / board
build_info_json = root_build_dir / "build_info.json"
build_info = json.loads(build_info_json.read_text())
board_info = build_info.get(board)
assert board_info, f"Board {board} not found in {build_info_json}"
prog_path = Path(board_info["prog_path"])
base_path = prog_path.parent
suffixes = [".bin", ".hex", ".uf2"]
firmware: Path
for suffix in suffixes:
firmware = base_path / f"firmware{suffix}"
if firmware.exists():
break
else:
msg = (
", ".join([f"firmware{suffix}" for suffix in suffixes])
+ f" not found in {base_path}"
)
raise FileNotFoundError(msg)
size_command = f"du -b {firmware}"
output, error = run_command(size_command)
if error:
print(f"Error checking firmware size: {error}")
return -1
size_in_bytes = output.strip().split()[0]
return int(size_in_bytes)
def get_commit_hash():
hash_command = "git rev-parse HEAD"
output, error = run_command(hash_command)
if error:
print(f"Error getting commit hash: {error}")
return None
return output.strip()
def get_commit_date(commit_hash):
date_command = f"git show -s --format=%ci {commit_hash}"
output, error = run_command(date_command)
if error:
print(f"Error getting commit date: {error}")
return None
return dateutil.parser.parse(output.strip()).isoformat()
def main(
board: str,
num_commits: int,
skip_step: int,
start_commit: str | None = None,
end_commit: str | None = None,
):
# change to the script dir
os.chdir(str(HERE))
# Create tmp directory if it doesn't exist
if os.path.exists("tmp"):
shutil.rmtree("tmp")
os.makedirs("tmp", exist_ok=True)
# Change to the tmp directory
os.chdir("tmp")
# 1. Git clone FastLED repository
print("Cloning FastLED repository...")
clone_command = "git clone https://github.com/FastLED/FastLED.git"
output, error = run_command(clone_command)
# if error:
# print(f"Error cloning repository: {error}")
# os.chdir("..")
# return
# Change to the FastLED directory
os.chdir("FastLED")
# Checkout the latest commit
run_command("git checkout master")
# If end_commit is specified, checkout that commit
# if end_commit:
# print(f"Checking out end commit: {end_commit}")
# checkout_command = f"git checkout {end_commit}"
# output, error = run_command(checkout_command)
# #if error:
# # print(f"Error checking out end commit: {error}")
# # return
# Prepare CSV file
csv_filename = "../../firmware_sizes.csv"
with open(csv_filename, "w", newline="") as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(["datetime", "commit_hash", "binary_size"])
commits_checked = 0
first_iteration = True
while True:
current_commit = get_commit_hash()
if first_iteration and start_commit:
first_iteration = False
while True:
if current_commit == start_commit:
break
if not step_back_commits(1):
break
current_commit = get_commit_hash()
if num_commits and commits_checked >= num_commits:
print(f"Checked {num_commits} commits")
break
if end_commit and current_commit == end_commit:
print(f"Checked until end commit: {end_commit}")
break
# 2. Run ci-compile.py for current commit
print(f"\nChecking commit {commits_checked + 1}")
# remove .build/esp32dev/pio/build/esp32dev/ directory
board_files = Path(".build") / board / ".pio" / "build" / board
if board_files.exists():
shutil.rmtree(str(board_files), ignore_errors=True)
compile_command = f"python3 ci/ci-compile.py {board} --examples Blink"
output, error = run_command(compile_command)
if error:
print(f"Error running ci-compile.py: {error}")
if not step_back_commits(skip_step):
break
continue
# 3. Check firmware size and get commit hash
print("Checking firmware size...")
try:
size = check_firmware_size(board)
except FileNotFoundError as e:
print(f"Error checking firmware size: {e}")
if not step_back_commits(skip_step):
break
continue
except AssertionError as e:
print(f"Error: {e}")
if not step_back_commits(skip_step):
break
continue
commit_hash = get_commit_hash()
if size and commit_hash:
commit_date = get_commit_date(commit_hash)
print(f"Firmware size: {size} bytes")
# Write to CSV incrementally
with open(csv_filename, "a", newline="") as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow([commit_date, commit_hash, size])
print(f"Result appended to {csv_filename}")
commits_checked += 1
# 4. Step back one commit
print("Stepping back 1 commit...")
if not step_back_commits(1):
break
# Don't remove the tmp directory
print("\nTemporary directory 'tmp' has been left intact for inspection.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Check FastLED firmware size for multiple commits."
)
parser.add_argument(
"--num-commits", type=int, default=1, help="Number of commits to check"
)
parser.add_argument(
"--skip-step",
type=int,
default=1,
help="Number of commits to skip between checks",
)
parser.add_argument("--start-commit", type=str, help="Starting commit hash")
parser.add_argument("--end-commit", type=str, help="Ending commit hash")
parser.add_argument(
"--board", type=str, required=True, help="Board to check firmware size for"
)
args = parser.parse_args()
if args.start_commit or args.end_commit:
if not (args.start_commit and not args.end_commit):
print("Both start commit and end commit must be specified.")
exit(1)
# if start_commit is specified, end_commit must be specified
num_commits = args.num_commits
if args.start_commit and args.end_commit:
if args.start_commit == args.end_commit:
print("Start commit and end commit are the same.")
exit(1)
num_commits = 999999
main(args.board, num_commits, args.skip_step, args.start_commit, args.end_commit)

View file

@ -0,0 +1,293 @@
import argparse
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Tuple
from ci.paths import PROJECT_ROOT
from ci.running_process import RunningProcess
BUILD_DIR = PROJECT_ROOT / "tests" / ".build"
BUILD_DIR.mkdir(parents=True, exist_ok=True)
def clean_build_directory():
print("Cleaning build directory...")
shutil.rmtree(BUILD_DIR, ignore_errors=True)
BUILD_DIR.mkdir(parents=True, exist_ok=True)
print("Build directory cleaned.")
HERE = Path(__file__).resolve().parent
WASM_BUILD = False
USE_ZIG = False
USE_CLANG = False
def _has_system_clang_compiler() -> bool:
CLANG = shutil.which("clang")
CLANGPP = shutil.which("clang++")
LLVM_AR = shutil.which("llvm-ar")
return CLANG is not None and CLANGPP is not None and LLVM_AR is not None
def use_clang_compiler() -> Tuple[Path, Path, Path]:
assert _has_system_clang_compiler(), "Clang system compiler not found"
CLANG = shutil.which("clang")
CLANGPP = shutil.which("clang++")
LLVM_AR = shutil.which("llvm-ar")
assert CLANG is not None, "clang compiler not found"
assert CLANGPP is not None, "clang++ compiler not found"
assert LLVM_AR is not None, "llvm-ar not found"
# Set environment variables for C and C++ compilers
os.environ["CC"] = CLANG
os.environ["CXX"] = CLANGPP
os.environ["AR"] = LLVM_AR
os.environ["CXXFLAGS"] = os.environ.get("CXXFLAGS", "") + " -ferror-limit=1"
os.environ["CFLAGS"] = os.environ.get("CFLAGS", "") + " -ferror-limit=1"
if WASM_BUILD:
wasm_flags = [
"--target=wasm32",
"-O3",
"-flto",
# "-nostdlib",
# "-Wl,--no-entry",
# "-Wl,--export-all",
# "-Wl,--lto-O3",
# "-Wl,-z,stack-size=8388608", # 8 * 1024 * 1024 (8MiB)
]
os.environ["CFLAGS"] = " ".join(wasm_flags)
os.environ["CXXFLAGS"] = " ".join(wasm_flags)
print(f"CC: {CLANG}")
print(f"CXX: {CLANGPP}")
print(f"AR: {LLVM_AR}")
return Path(CLANG), Path(CLANGPP), Path(LLVM_AR)
def use_zig_compiler() -> Tuple[Path, Path, Path]:
assert 0 == os.system(
"uv run python -m ziglang version"
), "Zig-clang compiler not found"
uv_path_str: str | None = shutil.which("uv")
assert uv_path_str is not None, "uv not found in PATH"
uv_path = Path(uv_path_str).resolve()
zig_command = f'"{uv_path}" run python -m ziglang'
# We are going to build up shell scripts that look like cc, c++, and ar. It will contain the actual build command.
CC_PATH = BUILD_DIR / "cc"
CXX_PATH = BUILD_DIR / "c++"
AR_PATH = BUILD_DIR / "ar"
if sys.platform == "win32":
CC_PATH = CC_PATH.with_suffix(".cmd")
CXX_PATH = CXX_PATH.with_suffix(".cmd")
AR_PATH = AR_PATH.with_suffix(".cmd")
CC_PATH.write_text(f"@echo off\n{zig_command} cc %* 2>&1\n")
CXX_PATH.write_text(f"@echo off\n{zig_command} c++ %* 2>&1\n")
AR_PATH.write_text(f"@echo off\n{zig_command} ar %* 2>&1\n")
else:
cc_cmd = f'#!/bin/bash\n{zig_command} cc "$@"\n'
cxx_cmd = f'#!/bin/bash\n{zig_command} c++ "$@"\n'
ar_cmd = f'#!/bin/bash\n{zig_command} ar "$@"\n'
CC_PATH.write_text(cc_cmd)
CXX_PATH.write_text(cxx_cmd)
AR_PATH.write_text(ar_cmd)
CC_PATH.chmod(0o755)
CXX_PATH.chmod(0o755)
AR_PATH.chmod(0o755)
# if WASM_BUILD:
# wasm_flags = [
# # "--target=wasm32",
# # "-O3",
# # "-flto",
# # "-nostdlib",
# "-Wl,--no-entry",
# # "-Wl,--export-all",
# # "-Wl,--lto-O3",
# "-Wl,-z,stack-size=8388608", # 8 * 1024 * 1024 (8MiB)
# ]
# os.environ["CFLAGS"] = " ".join(wasm_flags)
# os.environ["CXXFLAGS"] = " ".join(wasm_flags)
cc, cxx = CC_PATH, CXX_PATH
# use the system path, so on windows this looks like "C:\Program Files\Zig\zig.exe"
cc_path: Path | str = cc.resolve()
cxx_path: Path | str = cxx.resolve()
if sys.platform == "win32":
cc_path = str(cc_path).replace("/", "\\")
cxx_path = str(cxx_path).replace("/", "\\")
# print out the paths
print(f"CC: {cc_path}")
print(f"CXX: {cxx_path}")
print(f"AR: {AR_PATH}")
# sys.exit(1)
# Set environment variables for C and C++ compilers
os.environ["CC"] = str(cc_path)
os.environ["CXX"] = str(cxx_path)
os.environ["AR"] = str(AR_PATH)
return CC_PATH, CXX_PATH, AR_PATH
def run_command(command: str, cwd: Path | None = None) -> None:
process = RunningProcess(command, cwd=cwd)
process.wait()
if process.returncode != 0:
print(f"{Path(__file__).name}: Error executing command: {command}")
sys.exit(1)
def compile_fastled(specific_test: str | None = None) -> None:
if USE_ZIG:
print("USING ZIG COMPILER")
rtn = subprocess.run(
"python -m ziglang version", shell=True, capture_output=True
).returncode
zig_is_installed = rtn == 0
assert (
zig_is_installed
), 'Zig compiler not when using "python -m ziglang version" command'
use_zig_compiler()
elif USE_CLANG:
print("USING CLANG COMPILER")
use_clang_compiler()
cmake_configure_command_list: list[str] = [
"cmake",
"-S",
str(PROJECT_ROOT / "tests"),
"-B",
str(BUILD_DIR),
"-G",
"Ninja",
"-DCMAKE_VERBOSE_MAKEFILE=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
]
if WASM_BUILD:
cmake_configure_command_list.extend(
[
"-DCMAKE_C_COMPILER_TARGET=wasm32-wasi",
"-DCMAKE_CXX_COMPILER_TARGET=wasm32-wasi",
"-DCMAKE_C_COMPILER_WORKS=TRUE",
"-DCMAKE_CXX_COMPILER_WORKS=TRUE",
"-DCMAKE_SYSTEM_NAME=Generic",
"-DCMAKE_CROSSCOMPILING=TRUE",
"-DCMAKE_EXE_LINKER_FLAGS=-Wl,--no-entry -Wl,--export-all -Wl,--lto-O3 -Wl,-z,stack-size=8388608",
]
)
cmake_configure_command = subprocess.list2cmdline(cmake_configure_command_list)
run_command(cmake_configure_command, cwd=BUILD_DIR)
# Build the project
if specific_test:
cmake_build_command = f"cmake --build {BUILD_DIR} --target test_{specific_test}"
else:
cmake_build_command = f"cmake --build {BUILD_DIR}"
run_command(cmake_build_command)
print("FastLED library compiled successfully.")
def parse_arguments():
parser = argparse.ArgumentParser(
description="Compile FastLED library with different compiler options."
)
parser.add_argument("--use-zig", action="store_true", help="Use Zig compiler")
parser.add_argument("--use-clang", action="store_true", help="Use Clang compiler")
parser.add_argument("--wasm", action="store_true", help="Build for WebAssembly")
parser.add_argument(
"--clean",
action="store_true",
help="Clean the build directory before compiling",
)
parser.add_argument(
"--test",
help="Specific test to compile (without test_ prefix)",
)
return parser.parse_args()
def get_build_info(args: argparse.Namespace) -> dict[str, str | dict[str, str]]:
return {
"USE_ZIG": str(USE_ZIG),
"USE_CLANG": str(USE_CLANG),
"WASM_BUILD": str(WASM_BUILD),
"CC": os.environ.get("CC", ""),
"CXX": os.environ.get("CXX", ""),
"AR": os.environ.get("AR", ""),
"CFLAGS": os.environ.get("CFLAGS", ""),
"CXXFLAGS": os.environ.get("CXXFLAGS", ""),
"ARGS": {
"use_zig": str(args.use_zig),
"use_clang": str(args.use_clang),
"wasm": str(args.wasm),
},
}
def should_clean_build(build_info: dict[str, str | dict[str, str]]) -> bool:
build_info_file = BUILD_DIR / "build_info.json"
if not build_info_file.exists():
return True
with open(build_info_file, "r") as f:
old_build_info = json.load(f)
return old_build_info != build_info
def update_build_info(build_info: dict[str, str | dict[str, str]]):
build_info_file = BUILD_DIR / "build_info.json"
with open(build_info_file, "w") as f:
json.dump(build_info, f, indent=2)
def main() -> None:
global USE_ZIG, USE_CLANG, WASM_BUILD
args = parse_arguments()
USE_ZIG = args.use_zig # use Zig's clang compiler
USE_CLANG = args.use_clang # Use pure Clang for WASM builds
WASM_BUILD = args.wasm
using_gcc = not USE_ZIG and not USE_CLANG and not WASM_BUILD
if using_gcc:
if not shutil.which("g++"):
print(
"gcc compiler not found in PATH, falling back zig's built in clang compiler"
)
USE_ZIG = True
USE_CLANG = False
if USE_CLANG:
if not _has_system_clang_compiler():
print(
"Clang compiler not found in PATH, falling back to Zig-clang compiler"
)
USE_ZIG = True
USE_CLANG = False
os.chdir(str(HERE))
print(f"Current directory: {Path('.').absolute()}")
build_info = get_build_info(args)
if args.clean or should_clean_build(build_info):
clean_build_directory()
compile_fastled(args.test)
update_build_info(build_info)
print("FastLED library compiled successfully.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,269 @@
import argparse
import os
import re
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from ci.paths import PROJECT_ROOT
@dataclass
class FailedTest:
name: str
return_code: int
stdout: str
def run_command(command, use_gdb=False) -> tuple[int, str]:
captured_lines = []
if use_gdb:
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as gdb_script:
gdb_script.write("set pagination off\n")
gdb_script.write("run\n")
gdb_script.write("bt full\n")
gdb_script.write("info registers\n")
gdb_script.write("x/16i $pc\n")
gdb_script.write("thread apply all bt full\n")
gdb_script.write("quit\n")
gdb_command = (
f"gdb -return-child-result -batch -x {gdb_script.name} --args {command}"
)
process = subprocess.Popen(
gdb_command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Merge stderr into stdout
shell=True,
text=True,
bufsize=1, # Line buffered
)
assert process.stdout is not None
# Stream and capture output
while True:
line = process.stdout.readline()
if not line and process.poll() is not None:
break
if line:
captured_lines.append(line.rstrip())
print(line, end="") # Print in real-time
os.unlink(gdb_script.name)
output = "\n".join(captured_lines)
return process.returncode, output
else:
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Merge stderr into stdout
shell=True,
text=True,
bufsize=1, # Line buffered
)
assert process.stdout is not None
# Stream and capture output
while True:
line = process.stdout.readline()
if not line and process.poll() is not None:
break
if line:
captured_lines.append(line.rstrip())
print(line, end="") # Print in real-time
output = "\n".join(captured_lines)
return process.returncode, output
def compile_tests(clean: bool = False, unknown_args: list[str] = []) -> None:
os.chdir(str(PROJECT_ROOT))
print("Compiling tests...")
command = ["uv", "run", "ci/cpp_test_compile.py"]
if clean:
command.append("--clean")
command.extend(unknown_args)
return_code, _ = run_command(" ".join(command))
if return_code != 0:
print("Compilation failed:")
sys.exit(1)
print("Compilation successful.")
def run_tests(specific_test: str | None = None) -> None:
test_dir = os.path.join("tests", ".build", "bin")
if not os.path.exists(test_dir):
print(f"Test directory not found: {test_dir}")
sys.exit(1)
print("Running tests...")
failed_tests: list[FailedTest] = []
files = os.listdir(test_dir)
# filter out all pdb files (windows) and only keep test_ executables
files = [f for f in files if not f.endswith(".pdb") and f.startswith("test_")]
# If specific test is specified, filter for just that test
if specific_test:
test_name = f"test_{specific_test}"
if sys.platform == "win32":
test_name += ".exe"
files = [f for f in files if f == test_name]
if not files:
print(f"Test {test_name} not found in {test_dir}")
sys.exit(1)
for test_file in files:
test_path = os.path.join(test_dir, test_file)
if os.path.isfile(test_path) and os.access(test_path, os.X_OK):
print(f"Running test: {test_file}")
return_code, stdout = run_command(test_path)
output = stdout
failure_pattern = re.compile(r"Test .+ failed with return code (\d+)")
failure_match = failure_pattern.search(output)
is_crash = failure_match is not None
if is_crash:
print("Test crashed. Re-running with GDB to get stack trace...")
_, gdb_stdout = run_command(test_path, use_gdb=True)
stdout += "\n--- GDB Output ---\n" + gdb_stdout
# Extract crash information
crash_info = extract_crash_info(gdb_stdout)
print(f"Crash occurred at: {crash_info.file}:{crash_info.line}")
print(f"Cause: {crash_info.cause}")
print(f"Stack: {crash_info.stack}")
print("Test output:")
print(stdout)
if return_code == 0:
print("Test passed")
elif is_crash:
if failure_match:
print(f"Test crashed with return code {failure_match.group(1)}")
else:
print(f"Test crashed with return code {return_code}")
else:
print(f"Test failed with return code {return_code}")
print("-" * 40)
if return_code != 0:
failed_tests.append(FailedTest(test_file, return_code, stdout))
if failed_tests:
for failed_test in failed_tests:
print(
f"Test {failed_test.name} failed with return code {failed_test.return_code}\n{failed_test.stdout}"
)
tests_failed = len(failed_tests)
failed_test_names = [test.name for test in failed_tests]
print(
f"{tests_failed} test{'s' if tests_failed != 1 else ''} failed: {', '.join(failed_test_names)}"
)
sys.exit(1)
print("All tests passed.")
@dataclass
class CrashInfo:
cause: str = "Unknown"
stack: str = "Unknown"
file: str = "Unknown"
line: str = "Unknown"
def extract_crash_info(gdb_output: str) -> CrashInfo:
lines = gdb_output.split("\n")
crash_info = CrashInfo()
try:
for i, line in enumerate(lines):
if line.startswith("Program received signal"):
try:
crash_info.cause = line.split(":", 1)[1].strip()
except IndexError:
crash_info.cause = line.strip()
elif line.startswith("#0"):
crash_info.stack = line
for j in range(i, len(lines)):
if "at" in lines[j]:
try:
_, location = lines[j].split("at", 1)
location = location.strip()
if ":" in location:
crash_info.file, crash_info.line = location.rsplit(
":", 1
)
else:
crash_info.file = location
except ValueError:
pass # If split fails, we keep the default values
break
break
except Exception as e:
print(f"Error parsing GDB output: {e}")
return crash_info
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Compile and run C++ tests")
parser.add_argument(
"--compile-only",
action="store_true",
help="Only compile the tests without running them",
)
parser.add_argument(
"--run-only",
action="store_true",
help="Only run the tests without compiling them",
)
parser.add_argument(
"--only-run-failed-test",
action="store_true",
help="Only run the tests that failed in the previous run",
)
parser.add_argument(
"--clean", action="store_true", help="Clean build before compiling"
)
parser.add_argument(
"--test",
help="Specific test to run (without test_ prefix)",
)
parser.add_argument(
"--clang",
help="Use Clang compiler",
action="store_true",
)
args, unknown = parser.parse_known_args()
args.unknown = unknown
return args
def main() -> None:
args = parse_args()
run_only = args.run_only
compile_only = args.compile_only
specific_test = args.test
only_run_failed_test = args.only_run_failed_test
use_clang = args.clang
if not run_only:
passthrough_args = args.unknown
if use_clang:
passthrough_args.append("--use-clang")
compile_tests(clean=args.clean, unknown_args=passthrough_args)
if not compile_only:
if specific_test:
run_tests(specific_test)
else:
cmd = "ctest --test-dir tests/.build --output-on-failure"
if only_run_failed_test:
cmd += " --rerun-failed"
rtn, stdout = run_command(cmd)
if rtn != 0:
print("Failed tests:")
print(stdout)
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,186 @@
"""
Work in progress to generate doxygen via a script instead of a GitHub action.
"""
import os
import platform
import shutil
import subprocess
import warnings
from pathlib import Path
from typing import Optional, Tuple
from download import download # type: ignore
# Configs
DOXYGEN_VERSION = (
"1.13.2" # DOXYGEN_AWESOME styler is has certain restrictions with doxygen version
)
DOXYGEN_AWESOME_VERSION = "2.3.4" # deprecating
DOXYFILE_PATH = Path("docs/Doxyfile")
HTML_OUTPUT_DIR = Path("docs/html")
DOXYGEN_CSS_REPO = "https://github.com/jothepro/doxygen-awesome-css" # deprecating
HERE = Path(__file__).parent.resolve()
PROJECT_ROOT = HERE.parent
DOCS_ROOT = PROJECT_ROOT / "docs"
DOCS_TOOL_PATH = PROJECT_ROOT / ".tools_cache"
DOCS_OUTPUT_PATH = DOCS_ROOT / "html"
def run(
cmd: str,
cwd: Optional[str] = None,
shell: bool = True,
check: bool = True,
capture: bool = True,
) -> str:
print(f"Running: {cmd}")
result = subprocess.run(
cmd, shell=shell, cwd=cwd, check=False, capture_output=capture, text=False
)
if capture:
stdout = result.stdout.decode("utf-8") if result.stdout else ""
stderr = result.stderr.decode("utf-8") if result.stderr else ""
else:
stdout = ""
stderr = ""
if result.returncode != 0:
msg = f"Command failed with exit code {result.returncode}:\nstdout:\n{stdout}\n\nstderr:\n{stderr}"
warnings.warn(msg)
if check:
raise subprocess.CalledProcessError(
result.returncode, cmd, output=result.stdout
)
return stdout.strip()
def get_git_info() -> Tuple[str, str]:
release_tag = os.environ.get("RELEASE_TAG", "")
try:
latest_tag = run("git tag | grep -E '^[0-9]' | sort -V | tail -1")
latest_tag = latest_tag if latest_tag else ""
except subprocess.CalledProcessError:
latest_tag = ""
git_sha_short = run("git rev-parse --short HEAD")
full_sha = run("git rev-parse HEAD")
project_number = release_tag or latest_tag or git_sha_short
commit_message = (
f"{project_number} ({full_sha})"
if project_number != git_sha_short
else project_number
)
print(f"Project number: {project_number}")
print(f"Commit message: {commit_message}")
return project_number, commit_message
def install_doxygen_windows() -> Path:
print("Installing Doxygen...")
doxygen_url = (
f"https://www.doxygen.nl/files/doxygen-{DOXYGEN_VERSION}.windows.x64.bin.zip"
)
zip_path = DOCS_TOOL_PATH / f"doxygen-{DOXYGEN_VERSION}.zip"
extract_dir = DOCS_TOOL_PATH / f"doxygen-{DOXYGEN_VERSION}"
# Create tool path if it doesn't exist
DOCS_TOOL_PATH.mkdir(exist_ok=True, parents=True)
download(doxygen_url, zip_path)
shutil.unpack_archive(str(zip_path), extract_dir)
bin_path = next(extract_dir.glob("**/doxygen.exe"), None)
if not bin_path:
raise FileNotFoundError("Doxygen executable not found after extraction.")
print(f"Doxygen installed at: {bin_path}")
return bin_path
def install_doxygen_unix() -> Path:
print("Installing Doxygen...")
archive = f"doxygen-{DOXYGEN_VERSION}.linux.bin.tar.gz"
url = f"https://www.doxygen.nl/files/{archive}"
# Create tool path if it doesn't exist
DOCS_TOOL_PATH.mkdir(exist_ok=True, parents=True)
# Change to tool directory for download and extraction
original_dir = os.getcwd()
os.chdir(str(DOCS_TOOL_PATH))
try:
run(f"wget -q {url}")
run(f"tar -xf {archive}")
bin_dir = DOCS_TOOL_PATH / f"doxygen-{DOXYGEN_VERSION}"
return bin_dir / "bin" / "doxygen"
finally:
os.chdir(original_dir)
def install_theme() -> Path:
print("Installing Doxygen Awesome Theme...")
theme_path = DOCS_ROOT / "doxygen-awesome-css"
if theme_path.exists():
return theme_path
run(
f"git clone --depth 1 -b v{DOXYGEN_AWESOME_VERSION} {DOXYGEN_CSS_REPO}",
cwd=str(DOCS_ROOT),
)
return theme_path
def generate_docs(doxygen_bin: Path) -> None:
print("Generating documentation...")
cmd_str = f'"{doxygen_bin}" {DOXYFILE_PATH.name}'
run(cmd_str, cwd=str(DOCS_ROOT), capture=False)
# def install_graphviz() -> None:
# url: str = get_latest_release_for_platform()
# print(url)
def main() -> None:
is_windows = platform.system() == "Windows"
# is_macos = platform.system() == "Darwin"
_, commit_msg = get_git_info()
if is_windows:
doxygen_bin = install_doxygen_windows()
# add to path C:\Program Files\Graphviz\bin\
os.environ["PATH"] += os.pathsep + r"C:\Program Files\Graphviz\bin"
else:
doxygen_bin = install_doxygen_unix()
# install_theme()
# install_graphviz() # Work in progress
# Verify Graphviz installation
try:
dot_version = run("dot -V", check=False)
print(f"Graphviz detected: {dot_version}")
except Exception:
warnings.warn(
"Graphviz (dot) not found in PATH. Diagrams may not be generated."
)
# Check it graphviz is installed
# if linux
if not is_windows:
run("dot -Tsvg -Kneato -Grankdir=LR", check=True)
generate_docs(doxygen_bin=doxygen_bin)
print(f"\n✅ Docs generated in: {HTML_OUTPUT_DIR}")
print(f"📄 Commit message: {commit_msg}")
print("✨ You can now manually deploy to GitHub Pages or automate this step.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,197 @@
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path
from tempfile import TemporaryDirectory
from ci.bin_2_elf import bin_to_elf
from ci.elf import dump_symbol_sizes
from ci.map_dump import map_dump
def cpp_filt(cpp_filt_path: Path, input_text: str) -> str:
"""
Demangle C++ symbols using c++filt.
Args:
cpp_filt_path (Path): Path to c++filt executable.
input_text (str): Text to demangle.
Returns:
str: Demangled text.
"""
if not cpp_filt_path.exists():
raise FileNotFoundError(f"cppfilt not found at '{cpp_filt_path}'")
command = [str(cpp_filt_path), "-t", "-n"]
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
stdout, stderr = process.communicate(input=input_text)
if process.returncode != 0:
raise RuntimeError(f"Error running c++filt: {stderr}")
return stdout
def demangle_gnu_linkonce_symbols(cpp_filt_path: Path, map_text: str) -> str:
"""
Demangle .gnu.linkonce.t symbols in the map file.
Args:
cpp_filt_path (Path): Path to c++filt executable.
map_text (str): Content of the map file.
Returns:
str: Map file content with demangled symbols.
"""
# Extract all .gnu.linkonce.t symbols
pattern = r"\.gnu\.linkonce\.t\.(.+?)\s"
matches = re.findall(pattern, map_text)
if not matches:
return map_text
# Create a block of text with the extracted symbols
symbols_block = "\n".join(matches)
# Demangle the symbols
demangled_block = cpp_filt(cpp_filt_path, symbols_block)
# Create a dictionary of mangled to demangled symbols
demangled_dict = dict(zip(matches, demangled_block.strip().split("\n")))
# Replace the mangled symbols with demangled ones in the original text
for mangled, demangled in demangled_dict.items():
map_text = map_text.replace(
f".gnu.linkonce.t.{mangled}", f".gnu.linkonce.t.{demangled}"
)
return map_text
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Convert a binary file to ELF using map file."
)
parser.add_argument("--first", action="store_true", help="Inspect the first board")
parser.add_argument("--cwd", type=Path, help="Custom working directory")
return parser.parse_args()
def load_build_info(build_info_path: Path) -> dict:
"""
Load build information from a JSON file.
Args:
build_info_path (Path): Path to the build_info.json file.
Returns:
dict: Parsed JSON data.
"""
if not build_info_path.exists():
raise FileNotFoundError(f"Build info JSON not found at '{build_info_path}'")
return json.loads(build_info_path.read_text())
def main() -> int:
args = parse_args()
if args.cwd:
root_build_dir = args.cwd / ".build"
else:
root_build_dir = Path(".build")
board_dirs = [d for d in root_build_dir.iterdir() if d.is_dir()]
if not board_dirs:
print(f"No board directories found in {root_build_dir.absolute()}")
return 1
print("Available boards:")
for i, board_dir in enumerate(board_dirs):
print(f"[{i}]: {board_dir.name}")
which = (
0
if args.first
else int(input("Enter the number of the board you want to inspect: "))
)
board_dir = board_dirs[which]
build_info_json = board_dir / "build_info.json"
build_info = load_build_info(build_info_json)
board = board_dir.name
board_info = build_info.get(board) or build_info[next(iter(build_info))]
# Validate paths from build_info.json
elf_path = Path(board_info.get("prog_path", ""))
if not elf_path.exists():
print(
f"Error: ELF path '{elf_path}' does not exist. Check the 'prog_path' in build_info.json."
)
return 1
bin_file = elf_path.with_suffix(".bin")
if not bin_file.exists():
# use .hex or .uf2 if .bin doesn't exist
bin_file = elf_path.with_suffix(".hex")
if not bin_file.exists():
bin_file = elf_path.with_suffix(".uf2")
if not bin_file.exists():
print(f"Error: Binary file not found for '{elf_path}'")
return 1
cpp_filt_path = Path(board_info["aliases"]["c++filt"])
ld_path = Path(board_info["aliases"]["ld"])
as_path = Path(board_info["aliases"]["as"])
nm_path = Path(board_info["aliases"]["nm"])
objcopy_path = Path(board_info["aliases"]["objcopy"])
nm_path = Path(board_info["aliases"]["nm"])
map_file = board_dir / "firmware.map"
if not map_file.exists():
# Search for the map file
map_file = bin_file.with_suffix(".map")
if not map_file.exists():
possible_map_files = list(board_dir.glob("**/firmware.map"))
if possible_map_files:
map_file = possible_map_files[0]
else:
print("Error: firmware.map file not found")
return 1
try:
with TemporaryDirectory() as temp_dir:
temp_dir_path = Path(temp_dir)
output_elf = bin_to_elf(
bin_file,
map_file,
as_path,
ld_path,
objcopy_path,
temp_dir_path / "output.elf",
)
out = dump_symbol_sizes(nm_path, cpp_filt_path, output_elf)
print(out)
except Exception as e:
print(
f"Error while converting binary to ELF, binary analysis will not work on this build: {e}"
)
map_dump(map_file)
# Demangle .gnu.linkonce.t symbols and print map file
print("\n##################################################")
print("# Map file dump:")
print("##################################################\n")
map_text = map_file.read_text()
demangled_map_text = demangle_gnu_linkonce_symbols(cpp_filt_path, map_text)
print(demangled_map_text)
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,72 @@
import argparse
import json
from pathlib import Path
from ci.elf import dump_symbol_sizes
HERE = Path(__file__).resolve().parent
PROJECT_ROOT = HERE.parent
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Inspect a compiled binary")
parser.add_argument("--first", action="store_true", help="Inspect the first board")
parser.add_argument("--cwd", type=str, help="Custom working directory")
parser.add_argument("--elf", type=str, help="Path to the ELF file to inspect")
return parser.parse_args()
def main() -> int:
args = parse_args()
if args.elf:
firmware_path = Path(args.elf)
if not firmware_path.exists():
print(f"ELF file not found: {firmware_path}")
return 1
if args.cwd:
# os.chdir(args.cwd)
root_build_dir = Path(args.cwd) / ".build"
else:
root_build_dir = Path(".build")
# Find the first board directory
board_dirs = [d for d in root_build_dir.iterdir() if d.is_dir()]
if not board_dirs:
# print("No board directories found in .build")
print(f"No board directories found in {root_build_dir.absolute()}")
return 1
# display all the boards to the user and ask them to select which one they want by number
print("Available boards:")
for i, board_dir in enumerate(board_dirs):
print(f"[{i}]: {board_dir.name}")
if args.first:
which = 0
else:
which = int(input("Enter the number of the board you want to inspect: "))
board_dir = board_dirs[which]
board = board_dir.name
build_info_json = board_dir / "build_info.json"
build_info = json.loads(build_info_json.read_text())
board_info = build_info.get(board) or build_info[next(iter(build_info))]
firmware_path = Path(board_info["prog_path"])
cpp_filt_path = Path(board_info["aliases"]["c++filt"])
print(f"Dumping symbol sizes for {board} firmware: {firmware_path}")
try:
nm_path = Path(board_info["aliases"]["nm"])
symbol_sizes = dump_symbol_sizes(nm_path, cpp_filt_path, firmware_path)
print(symbol_sizes)
except Exception as e:
print(f"Error while dumping symbol sizes: {e}")
return 0
if __name__ == "__main__":
main()

View file

@ -0,0 +1,129 @@
import os
import subprocess
import sys
from pathlib import Path
from ci.paths import BUILD
from ci.tools import load_tools
def _list_builds() -> list[Path]:
str_paths = os.listdir(BUILD)
paths = [BUILD / p for p in str_paths]
dirs = [p for p in paths if p.is_dir()]
return dirs
def _check_build(build: Path) -> bool:
# 1. should contain a build_info.json file
# 2. should contain a .pio/build directory
has_build_info = (build / "build_info.json").exists()
has_pio_build = (build / ".pio" / "build").exists()
return has_build_info and has_pio_build
def _prompt_build() -> Path:
builds = _list_builds()
if not builds:
print("Error: No builds found", file=sys.stderr)
sys.exit(1)
print("Select a build:")
for i, build in enumerate(builds):
print(f" [{i}]: {build}")
while True:
try:
which = int(input("Enter the number of the build to use: "))
if 0 <= which < len(builds):
valid = _check_build(BUILD / builds[which])
if valid:
return BUILD / builds[which]
print("Error: Invalid build", file=sys.stderr)
else:
print("Error: Invalid selection", file=sys.stderr)
continue
except ValueError:
print("Error: Invalid input", file=sys.stderr)
continue
def _prompt_object_file(build: Path) -> Path:
# Look for object files in .pio/build directory
build_dir = build / ".pio" / "build"
object_files = []
# Walk through build directory to find .o files
for root, _, files in os.walk(build_dir):
for file in files:
if file.endswith(".o") and "FrameworkArduino" not in file:
full_path = Path(root) / file
if "FrameworkArduino" not in full_path.parts:
object_files.append(full_path)
if not object_files:
print("Error: No object files found", file=sys.stderr)
sys.exit(1)
print("\nSelect an object file:")
for i, obj_file in enumerate(object_files):
print(f" [{i}]: {obj_file.relative_to(build_dir)}")
while True:
try:
which = int(input("Enter the number of the object file to use: "))
if 0 <= which < len(object_files):
return object_files[which]
print("Error: Invalid selection", file=sys.stderr)
except ValueError:
print("Error: Invalid input", file=sys.stderr)
continue
def cli() -> None:
import argparse
parser = argparse.ArgumentParser(
description="Dump object file information using build tools"
)
parser.add_argument(
"build_path",
type=Path,
nargs="?",
help="Path to build directory containing build info JSON file",
)
args = parser.parse_args()
build_path = args.build_path
# Check if object file was provided and exists
if build_path is None:
build_path = _prompt_build()
else:
if not _check_build(build_path):
print("Error: Invalid build directory", file=sys.stderr)
sys.exit(1)
assert build_path is not None
assert build_path
build_info_path = build_path / "build_info.json"
assert build_info_path.exists(), f"File not found: {build_info_path}"
tools = load_tools(build_info_path)
object_file = _prompt_object_file(build_path)
cmd = [str(tools.objdump_path), "--syms", str(object_file)]
if sys.platform == "win32":
cmd = ["cmd", "/c"] + cmd
cmd_str = subprocess.list2cmdline(cmd)
subprocess.run(cmd, check=True)
print("\nDone. Command used:", cmd_str)
if __name__ == "__main__":
try:
cli()
except KeyboardInterrupt:
print("Exiting...")
sys.exit(1)

View file

@ -0,0 +1,16 @@
#include "FastLED.h"
// Let's include a bunch of stuff and see if it breaks the build.
#include <WiFi.h>
#include <ESPmDNS.h>
#include <NetworkUdp.h>
#include <ArduinoOTA.h>
#include <ArduinoJson.h>
void setup() {
}
void loop() {
}

View file

@ -0,0 +1,17 @@
[platformio]
src_dir = symlink://../../../src
[env:dev]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
board = esp32-s3-devkitc-1
framework = arduino
lib_deps =
https://github.com/dvarrel/AsyncTCP
https://github.com/mathieucarbou/ESPAsyncWebServer
ArduinoJson
FS
ArduinoOTA
ESPmDNS
lib_ldf_mode=deep
build_flags=-DFASTLED_STUB_MAIN_INCLUDE_INO="../ci/kitchensink/kitchensink.ino.cpp"

View file

@ -0,0 +1,11 @@
[platformio]
src_dir = symlink://../../../src
[env:dev]
platform = platformio/native
build_flags =
-DFASTLED_STUB_IMPL
-DFASTLED_STUB_MAIN_INCLUDE_INO="../examples/Blink/Blink.ino"
-std=c++17

View file

@ -0,0 +1,42 @@
import argparse
import sys
from pathlib import Path
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Convert a binary file to ELF using map file."
)
parser.add_argument("--first", action="store_true", help="Inspect the first board")
parser.add_argument("--cwd", type=Path, help="Custom working directory")
return parser.parse_args()
def main() -> int:
args = parse_args()
if args.cwd:
root_build_dir = args.cwd / ".build"
else:
root_build_dir = Path(".build")
board_dirs = [d for d in root_build_dir.iterdir() if d.is_dir()]
if not board_dirs:
print(f"No board directories found in {root_build_dir.absolute()}")
return 1
print("Available boards:")
for i, board_dir in enumerate(board_dirs):
print(f"[{i}]: {board_dir.name}")
which = (
0
if args.first
else int(input("Enter the number of the board you want to inspect: "))
)
board_dir = board_dirs[which]
# build_info_json = board_dir / "build_info.json"
optimization_report = board_dir / "optimization_report.txt"
text = optimization_report.read_text(encoding="utf-8")
print(text)
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,419 @@
#!/usr/bin/env python
"""A wrapper script around clang-format, suitable for linting multiple files
and to use for continuous integration.
This is an alternative API for the clang-format command line.
It runs over multiple files and directories in parallel.
A diff output is produced and a sensible exit code is returned.
"""
from __future__ import print_function, unicode_literals
import argparse
import codecs
import difflib
import errno
import fnmatch
import io
import multiprocessing
import os
import signal
import subprocess
import sys
import traceback
from functools import partial
try:
from subprocess import DEVNULL # py3k
except ImportError:
DEVNULL = open(os.devnull, "wb") # type: ignore
DEFAULT_EXTENSIONS = "c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx"
DEFAULT_CLANG_FORMAT_IGNORE = ".clang-format-ignore"
class ExitStatus:
SUCCESS = 0
DIFF = 1
TROUBLE = 2
def excludes_from_file(ignore_file):
excludes = []
try:
with io.open(ignore_file, "r", encoding="utf-8") as f:
for line in f:
if line.startswith("#"):
# ignore comments
continue
pattern = line.rstrip()
if not pattern:
# allow empty lines
continue
excludes.append(pattern)
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
return excludes
def list_files(files, recursive=False, extensions=None, exclude=None):
if extensions is None:
extensions = []
if exclude is None:
exclude = []
out = []
for file in files:
if recursive and os.path.isdir(file):
for dirpath, dnames, fnames in os.walk(file):
fpaths = [os.path.join(dirpath, fname) for fname in fnames]
for pattern in exclude:
# os.walk() supports trimming down the dnames list
# by modifying it in-place,
# to avoid unnecessary directory listings.
dnames[:] = [
x
for x in dnames
if not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)
]
fpaths = [x for x in fpaths if not fnmatch.fnmatch(x, pattern)]
for f in fpaths:
ext = os.path.splitext(f)[1][1:]
if ext in extensions:
out.append(f)
else:
out.append(file)
return out
def make_diff(file, original, reformatted):
return list(
difflib.unified_diff(
original,
reformatted,
fromfile="{}\t(original)".format(file),
tofile="{}\t(reformatted)".format(file),
n=3,
)
)
class DiffError(Exception):
def __init__(self, message, errs=None):
super(DiffError, self).__init__(message)
self.errs = errs or []
class UnexpectedError(Exception):
def __init__(self, message, exc=None):
super(UnexpectedError, self).__init__(message)
self.formatted_traceback = traceback.format_exc()
self.exc = exc
def run_clang_format_diff_wrapper(args, file):
try:
ret = run_clang_format_diff(args, file)
return ret
except DiffError:
raise
except Exception as e:
raise UnexpectedError("{}: {}: {}".format(file, e.__class__.__name__, e), e)
def run_clang_format_diff(args, file):
try:
with io.open(file, "r", encoding="utf-8") as f:
original = f.readlines()
except IOError as exc:
raise DiffError(str(exc))
if args.in_place:
invocation = [args.clang_format_executable, "-i", file]
else:
invocation = [args.clang_format_executable, file]
if args.style:
invocation.extend(["--style", args.style])
if args.dry_run:
print(" ".join(invocation))
return [], []
# Use of utf-8 to decode the process output.
#
# Hopefully, this is the correct thing to do.
#
# It's done due to the following assumptions (which may be incorrect):
# - clang-format will returns the bytes read from the files as-is,
# without conversion, and it is already assumed that the files use utf-8.
# - if the diagnostics were internationalized, they would use utf-8:
# > Adding Translations to Clang
# >
# > Not possible yet!
# > Diagnostic strings should be written in UTF-8,
# > the client can translate to the relevant code page if needed.
# > Each translation completely replaces the format string
# > for the diagnostic.
# > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
#
# It's not pretty, due to Python 2 & 3 compatibility.
encoding_py3 = {}
if sys.version_info[0] >= 3:
encoding_py3["encoding"] = "utf-8"
try:
proc = subprocess.Popen(
invocation,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
**encoding_py3,
)
except OSError as exc:
raise DiffError(
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(invocation), exc
)
)
proc_stdout = proc.stdout
proc_stderr = proc.stderr
assert proc_stdout is not None
assert proc_stderr is not None
if sys.version_info[0] < 3:
# make the pipes compatible with Python 3,
# reading lines should output unicode
encoding = "utf-8"
proc_stdout = codecs.getreader(encoding)(proc_stdout)
proc_stderr = codecs.getreader(encoding)(proc_stderr)
# hopefully the stderr pipe won't get full and block the process
outs = list(proc_stdout.readlines())
errs = list(proc_stderr.readlines())
proc.wait()
if proc.returncode:
raise DiffError(
"Command '{}' returned non-zero exit status {}".format(
subprocess.list2cmdline(invocation), proc.returncode
),
errs,
)
if args.in_place:
return [], errs
return make_diff(file, original, outs), errs
def bold_red(s):
return "\x1b[1m\x1b[31m" + s + "\x1b[0m"
def colorize(diff_lines):
def bold(s):
return "\x1b[1m" + s + "\x1b[0m"
def cyan(s):
return "\x1b[36m" + s + "\x1b[0m"
def green(s):
return "\x1b[32m" + s + "\x1b[0m"
def red(s):
return "\x1b[31m" + s + "\x1b[0m"
for line in diff_lines:
if line[:4] in ["--- ", "+++ "]:
yield bold(line)
elif line.startswith("@@ "):
yield cyan(line)
elif line.startswith("+"):
yield green(line)
elif line.startswith("-"):
yield red(line)
else:
yield line
def print_diff(diff_lines, use_color):
if use_color:
diff_lines = colorize(diff_lines)
if sys.version_info[0] < 3:
sys.stdout.writelines((line.encode("utf-8") for line in diff_lines))
else:
sys.stdout.writelines(diff_lines)
def print_trouble(prog, message, use_colors):
error_text = "error:"
if use_colors:
error_text = bold_red(error_text)
print("{}: {} {}".format(prog, error_text, message), file=sys.stderr)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--clang-format-executable",
metavar="EXECUTABLE",
help="path to the clang-format executable",
default="clang-format",
)
parser.add_argument(
"--extensions",
help="comma separated list of file extensions (default: {})".format(
DEFAULT_EXTENSIONS
),
default=DEFAULT_EXTENSIONS,
)
parser.add_argument(
"-r",
"--recursive",
action="store_true",
help="run recursively over directories",
)
parser.add_argument(
"-d", "--dry-run", action="store_true", help="just print the list of files"
)
parser.add_argument(
"-i",
"--in-place",
action="store_true",
help="format file instead of printing differences",
)
parser.add_argument("files", metavar="file", nargs="+")
parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="disable output, useful for the exit code",
)
parser.add_argument(
"-j",
metavar="N",
type=int,
default=0,
help="run N clang-format jobs in parallel" " (default number of cpus + 1)",
)
parser.add_argument(
"--color",
default="auto",
choices=["auto", "always", "never"],
help="show colored diff (default: auto)",
)
parser.add_argument(
"-e",
"--exclude",
metavar="PATTERN",
action="append",
default=[],
help="exclude paths matching the given glob-like pattern(s)"
" from recursive search",
)
parser.add_argument(
"--style",
help="formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)",
)
args = parser.parse_args()
# use default signal handling, like diff return SIGINT value on ^C
# https://bugs.python.org/issue14229#msg156446
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
signal.SIGPIPE # type: ignore
except AttributeError:
# compatibility, SIGPIPE does not exist on Windows
pass
else:
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # type: ignore
colored_stdout = False
colored_stderr = False
if args.color == "always":
colored_stdout = True
colored_stderr = True
elif args.color == "auto":
colored_stdout = sys.stdout.isatty()
colored_stderr = sys.stderr.isatty()
version_invocation = [args.clang_format_executable, str("--version")]
try:
subprocess.check_call(version_invocation, stdout=DEVNULL)
except subprocess.CalledProcessError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
return ExitStatus.TROUBLE
except OSError as e:
print_trouble(
parser.prog,
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(version_invocation), e
),
use_colors=colored_stderr,
)
return ExitStatus.TROUBLE
retcode = ExitStatus.SUCCESS
excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE)
excludes.extend(args.exclude)
files = list_files(
args.files,
recursive=args.recursive,
exclude=excludes,
extensions=args.extensions.split(","),
)
if not files:
return
njobs = args.j
if njobs == 0:
njobs = multiprocessing.cpu_count() + 1
njobs = min(len(files), njobs)
if njobs == 1:
# execute directly instead of in a pool,
# less overhead, simpler stacktraces
it = (run_clang_format_diff_wrapper(args, file) for file in files)
pool = None
else:
pool = multiprocessing.Pool(njobs)
it = pool.imap_unordered(partial(run_clang_format_diff_wrapper, args), files)
pool.close()
while True:
try:
outs, errs = next(it)
except StopIteration:
break
except DiffError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
retcode = ExitStatus.TROUBLE
sys.stderr.writelines(e.errs)
except UnexpectedError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
sys.stderr.write(e.formatted_traceback)
retcode = ExitStatus.TROUBLE
# stop at the first unexpected error,
# something could be very wrong,
# don't process all files unnecessarily
if pool:
pool.terminate()
break
else:
sys.stderr.writelines(errs)
if outs == []:
continue
if not args.quiet:
print_diff(outs, use_color=colored_stdout)
if retcode == ExitStatus.SUCCESS:
retcode = ExitStatus.DIFF
if pool:
pool.join()
return retcode
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,29 @@
"""
Unit test file.
"""
import unittest
class ApiTester(unittest.TestCase):
"""Main tester class."""
def test_build_all_examples(self) -> None:
"""Test command line interface (CLI)."""
from fastled import Api, Test # type: ignore
with Api.server(auto_updates=True) as server:
exception_map = Test.test_examples(host=server)
if len(exception_map) > 0:
exception: Exception
msg: str = ""
for example, exception in exception_map.items():
msg += f"Failed to compile example: {example}, error: {exception}\n"
self.fail(msg)
# self.assertEqual(0, len(out), f"Failed tests: {out}")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,66 @@
import subprocess
import unittest
import warnings
from pathlib import Path
from ci.bin_2_elf import bin_to_elf
from ci.elf import dump_symbol_sizes
from ci.paths import PROJECT_ROOT
from ci.tools import Tools, load_tools
HERE = Path(__file__).resolve().parent.absolute()
UNO = HERE / "uno"
OUTPUT = HERE / "output"
BUILD_INFO_PATH = PROJECT_ROOT / ".build" / "uno" / "build_info.json"
DISABLED = True
class TestBinToElf(unittest.TestCase):
@classmethod
def setUpClass(cls):
if DISABLED:
return
uno_build = PROJECT_ROOT / ".build" / "uno"
print(f"Checking for Uno build in: {uno_build}")
if not uno_build.exists():
print("Uno build not found. Running compilation...")
try:
subprocess.run(
"uv run ci/ci-compile.py uno --examples Blink",
shell=True,
check=True,
)
print("Compilation completed successfully.")
except subprocess.CalledProcessError as e:
print(f"Error during compilation: {e}")
raise
@unittest.skip("Skip bin to elf conversion test")
def test_bin_to_elf_conversion(self) -> None:
if DISABLED:
return
tools: Tools = load_tools(BUILD_INFO_PATH)
bin_file = UNO / "firmware.hex"
map_file = UNO / "firmware.map"
output_elf = OUTPUT / "output.elf"
try:
bin_to_elf(
bin_file,
map_file,
tools.as_path,
tools.ld_path,
tools.objcopy_path,
output_elf,
)
stdout = dump_symbol_sizes(tools.nm_path, tools.cpp_filt_path, output_elf)
print(stdout)
except Exception as e:
warnings.warn(f"Error while converting binary to ELF: {e}")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,49 @@
import subprocess
import unittest
from pathlib import Path
from ci.elf import dump_symbol_sizes
from ci.paths import PROJECT_ROOT
from ci.tools import Tools, load_tools
HERE = Path(__file__).resolve().parent.absolute()
UNO = HERE / "uno"
OUTPUT = HERE / "output"
ELF_FILE = UNO / "firmware.elf"
BUILD_INFO_PATH = PROJECT_ROOT / ".build" / "uno" / "build_info.json"
PLATFORMIO_PATH = Path.home() / ".platformio"
PLATFORMIO_PACKAGES_PATH = PLATFORMIO_PATH / "packages"
TOOLCHAIN_AVR = PLATFORMIO_PACKAGES_PATH / "toolchain-atmelavr"
def init() -> None:
uno_build = PROJECT_ROOT / ".build" / "uno"
print(f"Checking for Uno build in: {uno_build}")
if not BUILD_INFO_PATH.exists() or not TOOLCHAIN_AVR.exists():
print("Uno build not found. Running compilation...")
try:
subprocess.run(
"uv run ci/ci-compile.py uno --examples Blink",
shell=True,
check=True,
cwd=str(PROJECT_ROOT),
)
print("Compilation completed successfully.")
except subprocess.CalledProcessError as e:
print(f"Error during compilation: {e}")
raise
class TestBinToElf(unittest.TestCase):
def test_bin_to_elf_conversion(self) -> None:
init()
tools: Tools = load_tools(BUILD_INFO_PATH)
msg = dump_symbol_sizes(tools.nm_path, tools.cpp_filt_path, ELF_FILE)
print(msg)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,16 @@
import unittest
from pathlib import Path
from ci.map_dump import map_dump
HERE = Path(__file__).resolve().parent.absolute()
UNO = HERE / "uno"
class TestMapParser(unittest.TestCase):
def test_map_parser(self):
map_dump(UNO / "firmware.map")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,119 @@
import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
SRC_ROOT = PROJECT_ROOT / "src"
NUM_WORKERS = (os.cpu_count() or 1) * 4
# Files that are allowed to not have #pragma once
EXCLUDED_FILES = [
# Add any exceptions here
]
EXCLUDED_DIRS = [
"third_party",
"platforms",
]
class TestMissingPragmaOnce(unittest.TestCase):
def check_file(self, file_path: str) -> list[str]:
"""Check if a header file has #pragma once directive or if a cpp file incorrectly has it."""
failings: list[str] = []
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
if file_path.endswith(".h"):
# For header files, check if #pragma once is missing
if "#pragma once" not in content:
failings.append(f"Missing #pragma once in {file_path}")
elif file_path.endswith(".cpp"):
# For cpp files, check if #pragma once is incorrectly present
if "#pragma once" in content:
failings.append(f"Incorrect #pragma once in cpp file: {file_path}")
return failings
def test_pragma_once_usage(self) -> None:
"""
Searches through files to:
1. Check for missing #pragma once in header files
2. Check for incorrect #pragma once in cpp files
"""
files_to_check = []
current_dir = None
# Collect files to check
for root, dirs, files in os.walk(SRC_ROOT):
# Log when we enter a new directory
rel_path = os.path.relpath(root, SRC_ROOT)
if current_dir != rel_path:
current_dir = rel_path
print(f"Traversing directory: {rel_path}")
if rel_path in EXCLUDED_DIRS:
print(f" Skipping excluded directory: {rel_path}")
dirs[:] = [] # Skip this directory and its subdirectories
continue
# Check if this directory should be excluded
# if any(os.path.normpath(root).startswith(os.path.normpath(excluded_dir))
# for excluded_dir in EXCLUDED_DIRS):
# print(f" Skipping excluded directory: {rel_path}")
# continue
for excluded_dir in EXCLUDED_DIRS:
npath = os.path.normpath(root)
npath_excluded = os.path.normpath(excluded_dir)
print(f"Checking {npath} against excluded {npath_excluded}")
if npath.startswith(npath_excluded):
print(f" Skipping excluded directory: {rel_path}")
break
for file in files:
if file.endswith((".h", ".cpp")): # Check both header and cpp files
file_path = os.path.join(root, file)
# Check if file is excluded
# if any(file_path.endswith(excluded) for excluded in EXCLUDED_FILES):
# print(f" Skipping excluded file: {file}")
# continue
for excluded in EXCLUDED_FILES:
# print(f"Checking {file_path} against excluded {excluded}")
if file_path.endswith(excluded):
print(f" Skipping excluded file: {file}")
break
files_to_check.append(file_path)
print(f"Found {len(files_to_check)} files to check")
# Process files in parallel
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
# Report results
if all_failings:
msg = f"Found {len(all_failings)} pragma once issues: \n" + "\n".join(
all_failings
)
for failing in all_failings:
print(failing)
self.fail(msg)
else:
print("All files have proper pragma once usage.")
print(f"Pragma once check completed. Processed {len(files_to_check)} files.")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,132 @@
import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
SRC_ROOT = PROJECT_ROOT / "src"
PLATFORMS_DIR = os.path.join(SRC_ROOT, "platforms")
PLATFORMS_ESP_DIR = os.path.join(PLATFORMS_DIR, "esp")
NUM_WORKERS = (os.cpu_count() or 1) * 4
ENABLE_PARANOID_GNU_HEADER_INSPECTION = False
if ENABLE_PARANOID_GNU_HEADER_INSPECTION:
BANNED_HEADERS_ESP = ["esp32-hal.h"]
else:
BANNED_HEADERS_ESP = []
BANNED_HEADERS_CORE = [
"assert.h",
"iostream",
"stdio.h",
"cstdio",
"cstdlib",
"vector",
"list",
"map",
"set",
"queue",
"deque",
"algorithm",
"memory",
"thread",
"mutex",
"chrono",
"fstream",
"sstream",
"iomanip",
"exception",
"stdexcept",
"typeinfo",
"ctime",
"cmath",
"complex",
"valarray",
"cfloat",
"cassert",
"cerrno",
"cctype",
"cwctype",
"cstring",
"cwchar",
"cuchar",
"cstdint",
"cstddef", # this certainally fails
"type_traits", # this certainally fails
"Arduino.h",
] + BANNED_HEADERS_ESP
EXCLUDED_FILES = [
"stub_main.cpp",
]
class TestNoBannedHeaders(unittest.TestCase):
def check_file(self, file_path: str) -> list[str]:
failings: list[str] = []
banned_headers_list = []
if file_path.startswith(PLATFORMS_DIR):
# continue # Skip the platforms directory
if file_path.startswith(PLATFORMS_ESP_DIR):
banned_headers_list = BANNED_HEADERS_ESP
else:
return failings
if len(banned_headers_list) == 0:
return failings
with open(file_path, "r", encoding="utf-8") as f:
for line_number, line in enumerate(f, 1):
if line.startswith("//"):
continue
for header in banned_headers_list:
if (
f"#include <{header}>" in line or f'#include "{header}"' in line
) and "// ok include" not in line:
failings.append(
f"Found banned header '{header}' in {file_path}:{line_number}"
)
return failings
def test_no_banned_headers(self) -> None:
"""Searches through the program files to check for banned headers, excluding src/platforms."""
files_to_check = []
for root, _, files in os.walk(SRC_ROOT):
for file in files:
if file.endswith(
(".cpp", ".h", ".hpp")
): # Add or remove file extensions as needed
file_path = os.path.join(root, file)
if not any(
file_path.endswith(excluded) for excluded in EXCLUDED_FILES
):
files_to_check.append(file_path)
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
if all_failings:
msg = f"Found {len(all_failings)} banned header(s): \n" + "\n".join(
all_failings
)
for failing in all_failings:
print(failing)
self.fail(
msg + "\n"
"You can add '// ok include' at the end of the line to silence this error for specific inclusions."
)
else:
print("No banned headers found.")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,60 @@
import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
SRC_ROOT = PROJECT_ROOT / "src"
PLATFORMS_DIR = os.path.join(SRC_ROOT, "platforms")
NUM_WORKERS = (os.cpu_count() or 1) * 4
class NoUsingNamespaceFlInHeaderTester(unittest.TestCase):
def check_file(self, file_path) -> list[str]:
if "FastLED.h" in file_path:
return []
failings: list[str] = []
with open(file_path, "r", encoding="utf-8") as f:
for line_number, line in enumerate(f, 1):
if line.startswith("//"):
continue
if "using namespace fl;" in line:
failings.append(f"{file_path}:{line_number}: {line.strip()}")
return failings
def test_no_using_namespace(self) -> None:
"""Searches through the program files to check for banned headers, excluding src/platforms."""
files_to_check = []
for root, _, files in os.walk(SRC_ROOT):
for file in files:
if file.endswith(
(".h", ".hpp")
): # Add or remove file extensions as needed
file_path = os.path.join(root, file)
files_to_check.append(file_path)
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
if all_failings:
msg = (
f'Found {len(all_failings)} header file(s) "using namespace fl": \n'
+ "\n".join(all_failings)
)
for failing in all_failings:
print(failing)
self.fail(msg)
else:
print("No using namespace fl; found in headers.")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,79 @@
import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
NUM_WORKERS = (os.cpu_count() or 1) * 4
WASM_ROOT = PROJECT_ROOT / "src" / "platforms" / "wasm"
class TestMissingPragmaOnce(unittest.TestCase):
def check_file(self, file_path: str) -> list[str]:
"""Check if a header file has #pragma once directive or if a cpp file incorrectly has it."""
failings: list[str] = []
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
if file_path.endswith(".h") or file_path.endswith(".cpp"):
content = f.read()
# For header files, check if #pragma once is missing
if "EM_ASM_" in content and "// clang-format off\n" not in content:
if "clang-format off" not in content:
failings.append(f"Missing clang-format off in {file_path}")
else:
failings.append(f"clang-format off is malformed in {file_path}")
return failings
def test_esm_asm_and_clang_format(self) -> None:
files_to_check = []
current_dir = None
# Collect files to check
for root, _, files in os.walk(WASM_ROOT):
# Log when we enter a new directory
rel_path = os.path.relpath(root, WASM_ROOT)
if current_dir != rel_path:
current_dir = rel_path
print(f"Traversing directory: {rel_path}")
for file in files:
if file.endswith((".h", ".cpp")): # Check both header and cpp files
file_path = os.path.join(root, file)
files_to_check.append(file_path)
print(f"Found {len(files_to_check)} files to check")
# Process files in parallel
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
# Report results
if all_failings:
msg = (
f"Found {len(all_failings)} clang format issues in wasm: \n"
+ "\n".join(all_failings)
)
for failing in all_failings:
print(failing)
print(
"Please be aware you need // then one space then clang-format off then a new line exactly"
)
self.fail(msg)
else:
print("All files passed the check.")
print(f"Clange format check completed. Processed {len(files_to_check)} files.")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,63 @@
import os
import unittest
from concurrent.futures import ThreadPoolExecutor
from ci.paths import PROJECT_ROOT
SRC_ROOT = PROJECT_ROOT / "src"
# PLATFORMS_DIR = os.path.join(SRC_ROOT, "platforms")
NUM_WORKERS = (os.cpu_count() or 1) * 4
WRONG_DEFINES: dict[str, str] = {
"#if ESP32": "Use #ifdef ESP32 instead of #if ESP32",
"#if defined(FASTLED_RMT5)": "Use #ifdef FASTLED_RMT5 instead of #if defined(FASTLED_RMT5)",
"#if defined(FASTLED_ESP_HAS_CLOCKLESS_SPI)": "Use #ifdef FASTLED_ESP_HAS_CLOCKLESS_SPI instead of #if defined(FASTLED_ESP_HAS_CLOCKLESS_SPI)",
}
class TestWrongDefines(unittest.TestCase):
def check_file(self, file_path) -> list[str]:
failings = []
with open(file_path, "r", encoding="utf-8") as f:
for line_number, line in enumerate(f, 1):
line = line.strip()
if line.startswith("//"):
continue
for needle, message in WRONG_DEFINES.items():
if needle in line:
failings.append(f"{file_path}:{line_number}: {message}")
return failings
def test_no_bad_defines(self) -> None:
"""Searches through the program files to check for banned headers, excluding src/platforms."""
files_to_check = []
for root, _, files in os.walk(SRC_ROOT):
for file in files:
if file.endswith(
(".cpp", ".h", ".hpp")
): # Add or remove file extensions as needed
file_path = os.path.join(root, file)
files_to_check.append(file_path)
all_failings = []
with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [
executor.submit(self.check_file, file_path)
for file_path in files_to_check
]
for future in futures:
all_failings.extend(future.result())
if all_failings:
msg = f"Found {len(all_failings)} bad defines: \n" + "\n".join(all_failings)
for failing in all_failings:
print(failing)
self.fail("Please fix the defines: \n" + msg + "\n")
else:
print("No bad defines found.")
if __name__ == "__main__":
unittest.main()

Binary file not shown.

View file

@ -0,0 +1,237 @@
:100000000C9435000C945D000C945D000C945D0024
:100010000C945D000C945D000C945D000C945D00EC
:100020000C945D000C945D000C945D000C945D00DC
:100030000C945D000C945D000C945D000C945D00CC
:100040000C948D050C945D000C945D000C945D0087
:100050000C945D000C945D000C945D000C945D00AC
:100060000C945D000C945D00BB0611241FBECFEF05
:10007000D8E0DEBFCDBF11E0A0E0B1E0EEE8FEE0E9
:1000800002C005900D92AE32B107D9F721E0AEE281
:10009000B1E001C01D92A637B207E1F710E0C5E359
:1000A000D0E004C02197FE010E943A07C433D10773
:1000B000C9F70E94D7050C9445070C9400003FB780
:1000C000F8948091550190915601A0915701B091FB
:1000D000580126B5A89B05C02F3F19F00196A11D18
:1000E000B11D3FBFBA2FA92F982F8827BC01CD0182
:1000F000620F711D811D911D42E0660F771F881FE1
:10010000991F4A95D1F708952F923F924F925F928F
:100110006F927F928F929F92AF92BF92CF92DF9217
:10012000EF92FF920F931F93CF93DF93CDB7DEB77C
:10013000C358D1090FB6F894DEBF0FBECDBF782EDD
:100140008091660190916701A0916801B091690169
:10015000892B8A2B8B2BD9F00E945F0000917201B2
:10016000109173012091740130917501601B710B26
:10017000820B930B00916601109167012091680139
:10018000309169016017710782079307C8F20E94D6
:100190005F00609372017093730180937401909378
:1001A0007501E0916E01F0916F01309759F0409127
:1001B0006A0150916B0160916C0170916D01872D06
:1001C0000995782EFE01319680E8DF011D928A950F
:1001D000E9F7E0907001F09071016F015E01B1E804
:1001E000AB0EB11C4F01472C512CE114F10409F462
:1001F0005CC08A149B0409F458C0D701ED91FC91AE
:100200000484F585E02DC7010995F401819391934C
:100210004F0180916401909165018436910518F435
:10022000D7011C961C92D701ED91FC91228033805E
:10023000F501108211821282772019F187010A5F7D
:100240001F4FF0E0E0E0D8012D918D012223A9F0AD
:1002500012966C90662089F030E02F5F3F4FD201FC
:100260000E9405079B01AC01A62DB0E011960E94EB
:100270001B07D501AE0FBF1F8C933196E330F105FC
:1002800011F7F501008111812281D7011D964D9151
:100290005C911E9712966D917C91C701F1010995B1
:1002A000D7011496ED90FC90A0CF009170011091B1
:1002B00071010115110599F0CA14DB0481F0F601F2
:1002C000619171916F01D801ED91FC910684F785E0
:1002D000E02DC8010995F80104811581EACF8091CC
:1002E0006101909162019C012F5F3F4F3093620149
:1002F0002093610149970CF44AC08FB7F89420917C
:10030000590130915A0140915B0150915C018FBFBE
:1003100080915D0190915E01A0915F01B0916001BB
:10032000281B390B4A0B5B0B21F421E030E040E045
:1003300050E0E0916101F091620188EE93E0E89F66
:10034000B001E99F700DF89F700D1124072E000C6D
:10035000880B990B0E94E30630936501209364019A
:1003600010926201109261012FB7F89480915901A7
:1003700090915A01A0915B01B0915C012FBF8093D5
:100380005D0190935E01A0935F01B0936001CD5732
:10039000DF4F0FB6F894DEBF0FBECDBFDF91CF9118
:1003A0001F910F91FF90EF90DF90CF90BF90AF9093
:1003B0009F908F907F906F905F904F903F902F9085
:1003C00008958F929F92AF92BF92CF92DF92EF9259
:1003D000FF920E945F004B015C0184EFC82EDD2478
:1003E000D394E12CF12C0E945F00681979098A09E5
:1003F0009B09683E734081059105A8F321E0C21A6C
:10040000D108E108F10888EE880E83E0981EA11C4F
:10041000B11CC114D104E104F10429F7FF90EF905D
:10042000DF90CF90BF90AF909F908F90089580E91C
:1004300091E008950F931F93CF93DF9320912F01A5
:100440002F5F322F377030932F0120FF2BC020E811
:1004500031FD2064347009F02062205FFC01EC0162
:10046000239600E011E06485662329F070E0C8015E
:100470000E94CF066F5F6187822F869F080E80E003
:10048000811D1124811110C01682662311F0615064
:1004900061873196EC17FD0731F7DF91CF911F91FE
:1004A0000F91089520E0D4CF81508683EECF4F92F4
:1004B0005F927F928F929F92AF92BF92CF92DF9284
:1004C000EF92FF920F931F93CF93DF932C01EB01D9
:1004D0000E945F00F20125893689621B730B6A3026
:1004E0007105B0F3F8948A819B81181619060CF0F7
:1004F000CDC1E881F9816BB1862E689483F83BB158
:10050000377F3BB9DA848F812D2D281B822F2F83D3
:100510004F85042E000C550BAA81BB817D85FC8480
:10052000EE847F5FF394E3949E819884B984AB84D6
:100530001181C12C6C2D0C2D2C2D2181112788941B
:100540002111280F08F42FEF8195889470FD120F68
:100550001795889471FD120F1795889472FD120FEC
:100560001795889473FD120F1795889474FD120FD8
:100570001795889475FD120F1795889476FD120FC4
:100580001795889477FD120F17958894622F711133
:10059000612F8D0D162F002C8BB800C017FF3BB9B3
:1005A00020816627889400C000C0002C3BB921112F
:1005B000290F00C0002C8BB800C016FF3BB908F40F
:1005C0002FEF9195889400C000C0002C3BB9F0FC3F
:1005D000620F00C0002C8BB800C015FF3BB96795B7
:1005E0008894F1FC620F00C000C0002C3BB96795F5
:1005F000889400C0002C8BB800C014FF3BB9F2FCFB
:10060000620F6795889400C000C0002C3BB9F3FCD2
:10061000620F00C0002C8BB800C013FF3BB9679578
:100620008894F4FC620F00C000C0002C3BB96795B1
:10063000889400C0002C8BB800C012FF3BB9F5FCB9
:10064000620F6795889400C000C0002C3BB9F6FC8F
:10065000620F00C0002C8BB800C011FF3BB967953A
:100660008894F7FC620F00C000C0002C3BB967956E
:10067000889400C0002C8BB800C010FF3BB9122F2B
:10068000F110162F9B0D00C000C0002C3BB900C01C
:1006900000C0002C8BB800C017FF3BB92281662731
:1006A000889400C000C0002C3BB92111290D00C066
:1006B000002C8BB800C016FF3BB908F42FEFE40FF5
:1006C000F51F00C000C0002C3BB9E0FC620F00C069
:1006D000002C8BB800C015FF3BB967958894E1FCEE
:1006E000620F00C000C0002C3BB96795889400C021
:1006F000002C8BB800C014FF3BB9E2FC620F679579
:10070000889400C000C0002C3BB9E3FC620F00C01D
:10071000002C8BB800C013FF3BB967958894E4FCAC
:10072000620F00C000C0002C3BB96795889400C0E0
:10073000002C8BB800C012FF3BB9E5FC620F679537
:10074000889400C000C0002C3BB9E6FC620F00C0DA
:10075000002C8BB800C011FF3BB967958894E7FC6B
:10076000620F00C000C0002C3BB96795889400C0A0
:10077000002C8BB800C010FF3BB9122FE110162FD0
:10078000919400C000C0002C3BB99A0C00C000C07E
:100790008BB800C017FF3BB921816627889400C041
:1007A00000C0002C3BB92111280F00C0002C8BB8D1
:1007B00000C016FF3BB908F42FEF8195889400C064
:1007C00000C0002C3BB970FD620F00C0002C8BB83C
:1007D00000C015FF3BB96795889471FD620F00C09A
:1007E00000C0002C3BB96795889400C0002C8BB8E2
:1007F00000C014FF3BB972FD620F6795889400C07A
:1008000000C0002C3BB973FD620F00C0002C8BB8F8
:1008100000C013FF3BB96795889474FD620F00C058
:1008200000C0002C3BB96795889400C0002C8BB8A1
:1008300000C012FF3BB975FD620F6795889400C038
:1008400000C0002C3BB976FD620F00C0002C8BB8B5
:1008500000C011FF3BB96795889477FD620F00C017
:1008600000C0002C3BB96795889400C0002C8BB861
:1008700000C010FF3BB9122F7111162F8D0D00C053
:1008800000C0002C3BB9119709F086CE4A815B81EC
:1008900020EE31E0DA010E941407DC01CB01F4E024
:1008A000B695A79597958795FA95D1F730E020E012
:1008B000B901EAE94E9F040E611D5E9F600D711D36
:1008C0001124650F711D860F971FA11DB11D893E53
:1008D00043E09407A105B10508F434C0885E934055
:1008E000A109B10942E0B695A795979587954A95D4
:1008F000D1F747E0849F080E211D949F200D311DE4
:100900001124290F311D60912E0170E0860F971F71
:10091000820F931F4091590150915A0160915B01E0
:1009200070915C01292F3327420F531F611D711DE8
:100930004093590150935A0160935B0170935C019D
:1009400080932E0178940E945F00F201768B658B74
:10095000DF91CF911F910F91FF90EF90DF90CF909B
:10096000BF90AF909F908F907F905F904F90089531
:1009700081E090E00895539A08956F927F928F924C
:10098000CF92DF92EF92FF920F931F93CF93DF935B
:10099000CDB7DEB762970FB6F894DEBF0FBECDBFFE
:1009A0006C017A013801822EDC011C962C91CA015F
:1009B00057FF04C088279927841B950B7A83698386
:1009C0009C838B839E838D836D867E868F8621306C
:1009D00049F5CE0101960E941A0283E0888B1A8A9B
:1009E000198AF7FE02C08DEF888BD601ED91FC913C
:1009F0000288F389E02DBE016F5F7F4FC601099524
:100A000062960FB6F894DEBF0FBECDBFDF91CF91D7
:100A10001F910F91FF90EF90DF90CF908F907F907C
:100A20006F9008951C861B861A86198618861F8269
:100A3000D4CFEF92FF920F931F93CF93DF93CDB755
:100A4000DEB762970FB6F894DEBF0FBECDBF7C0154
:100A5000DC011C968C917A8369835C834B835E8373
:100A60004D830D871E872F878130F9F4CE010196C3
:100A70000E941A02188A1A8A198AD701ED91FC91EC
:100A80000288F389E02DBE016F5F7F4FC701099592
:100A900062960FB6F894DEBF0FBECDBFDF91CF9147
:100AA0001F910F91FF90EF9008951C861B861A8668
:100AB000198618861F82DECF90E080E00895FC0141
:100AC00064870895FC01848590E00895FC01858584
:100AD000968508950F931F93CF93DF9300D01F92B5
:100AE000CDB7DEB7AB0119821A821B82DC01ED9112
:100AF000FC910190F081E02D00E010E020E0BE01CB
:100B00006F5F7F4F09950F900F900F90DF91CF91FE
:100B10001F910F9108950E9440071F920F920FB6E8
:100B20000F9211242F933F938F939F93AF93BF9373
:100B30008091590190915A01A0915B01B0915C01A3
:100B40003091540123E0230F2D3758F50196A11D54
:100B5000B11D209354018093590190935A01A093A1
:100B60005B01B0935C018091550190915601A09179
:100B70005701B09158010196A11DB11D80935501F7
:100B800090935601A0935701B0935801BF91AF9134
:100B90009F918F913F912F910F900FBE0F901F90BB
:100BA000189526E8230F0296A11DB11DD2CF789487
:100BB00084B5826084BD84B5816084BD85B5826062
:100BC00085BD85B5816085BD80916E008160809313
:100BD0006E0010928100809181008260809381007C
:100BE000809181008160809381008091800081608C
:100BF000809380008091B10084608093B1008091E7
:100C0000B00081608093B00080917A00846080930E
:100C10007A0080917A00826080937A0080917A00D5
:100C2000816080937A0080917A00806880937A0056
:100C30001092C10080914901811155C01092350177
:100C4000109234018FEF80933801809339018093A3
:100C50003A0180933B0180933C0180933D0181E008
:100C600080933E011092400110923F0180E797E18E
:100C7000909342018093410183E090E0909344017E
:100C80008093430110924601109245011092370162
:100C9000109236018091700190917101892B31F48D
:100CA00082E391E09093710180937001E0913001B3
:100CB000F0913101309721F082E391E095838483B4
:100CC00082E391E0909331018093300110924801CA
:100CD000109247018AE191E09093330180933201B1
:100CE00081E080934901539A81E591E09093350129
:100CF0008093340181E090E09093400180933F0124
:100D00008091660190916701A0916801B09169019D
:100D1000843C29E09207A105B10520F484EC99E018
:100D2000A0E0B0E08093660190936701A093680112
:100D3000B0936901CFEF00E010E0C0935101109231
:100D4000520110925301809163010E9484000E941D
:100D5000E1011092510110925201109253018091C1
:100D600063010E9484000E94E1010115110529F32D
:100D70000E940000E2CFE3E6F1E08FEF8083128271
:100D80001182148613868FEF9FEFDC018783908793
:100D9000A187B2871382148215821682089597FB69
:100DA000072E16F4009407D077FD09D00E9426077D
:100DB00007FC05D03EF4909581959F4F089570955E
:100DC00061957F4F0895A1E21A2EAA1BBB1BFD015E
:100DD0000DC0AA1FBB1FEE1FFF1FA217B307E4071A
:100DE000F50720F0A21BB30BE40BF50B661F771F72
:100DF000881F991F1A9469F7609570958095909552
:100E00009B01AC01BD01CF010895A29FB001B39F2A
:100E1000C001A39F700D811D1124911DB29F700D03
:100E2000811D1124911D08950E940507B7FF0895A3
:100E3000821B930B08950E940507A59F900DB49FF8
:100E4000900DA49F800D911D11240895AA1BBB1B1A
:100E500051E107C0AA1FBB1FA617B70710F0A61BBA
:100E6000B70B881F991F5A95A9F780959095BC01DB
:100E7000CD010895EE0FFF1F0590F491E02D099428
:0E0E800081E090E0F8940C944507F894FFCFC1
:100E8E00000000008B058B058B056A056605B8040E
:100E9E0062055F055C05000000001905BD04BB047A
:0E0EAE006A056605B80462055F051702570263
:00000001FF

View file

@ -0,0 +1,789 @@
Archive member included to satisfy reference by file (symbol)
.pio\build\uno\lib6ec\libsrc.a(FastLED.cpp.o)
.pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin) (_ZN14CLEDController7m_pHeadE)
.pio\build\uno\lib6ec\libsrc.a(crgb.cpp.o)
.pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin) (_ZN4CRGB17computeAdjustmentEhRKS_S1_)
.pio\build\uno\lib6ec\libsrc.a(lib8tion.cpp.o)
FastLED.cpp.o (symbol from plugin) (memset8)
.pio\build\uno\libFrameworkArduino.a(abi.cpp.o)
.pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin) (__cxa_pure_virtual)
.pio\build\uno\libFrameworkArduino.a(hooks.c.o)
FastLED.cpp.o (symbol from plugin) (yield)
.pio\build\uno\libFrameworkArduino.a(main.cpp.o)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o (main)
.pio\build\uno\libFrameworkArduino.a(wiring.c.o)
.pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin) (timer0_millis)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o (exit)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__divmodhi4)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__udivmodsi4)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__do_copy_data)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__do_clear_bss)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__do_global_ctors)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__umulhisi3)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__usmulhisi3)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (__muluhisi3)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o) (__udivmodhi4)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o) (__tablejump2__)
c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a(abort.o)
C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o (abort)
Discarded input sections
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.text 0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController4sizeEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController5lanesEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController13beginShowLedsEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController11endShowLedsEPv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZNK14CLEDController17getMaxRefreshRateEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19CPixelLEDControllerIL6EOrder66ELi1ELm4294967295EE5lanesEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZNK19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EE17getMaxRefreshRateEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDControllerC5Ev
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZTV14CLEDController
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController13getAdjustmentEh
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController9showColorERK4CRGBih
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController9clearLedsEi
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8CFastLED4showEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19CPixelLEDControllerIL6EOrder66ELi1ELm4294967295EEC5Ev
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZTV19CPixelLEDControllerIL6EOrder66ELi1ELm4294967295EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EEC5Ev
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZTV19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN22WS2812Controller800KhzILh3EL6EOrder66EEC5Ev
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZTV22WS2812Controller800KhzILh3EL6EOrder66EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8NEOPIXELILh3EEC5Ev
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZTV8NEOPIXELILh3EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8CFastLED7addLedsI8NEOPIXELLh3EEER14CLEDControllerP4CRGBii
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZGVZN8CFastLED7addLedsI8NEOPIXELLh3EEER14CLEDControllerP4CRGBiiE1c
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZZN8CFastLED7addLedsI8NEOPIXELLh3EEER14CLEDControllerP4CRGBiiE1c
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN7_AVRPINILh3ELh8E18__gen_struct_PORTD17__gen_struct_DDRD17__gen_struct_PINDE9setOutputEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EE4initEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8CMinWaitILi10EE4waitEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EE15showRGBInternalER15PixelControllerILS0_66ELi1ELm4294967295EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8CMinWaitILi10EE4markEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19ClocklessControllerILh3ELi4ELi10ELi6EL6EOrder66ELi0ELb0ELi10EE10showPixelsER15PixelControllerILS0_66ELi1ELm4294967295EE
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN15PixelControllerIL6EOrder66ELi1ELm4294967295EE11initOffsetsEi
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN15PixelControllerIL6EOrder66ELi1ELm4294967295EE21init_binary_ditheringEv
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZZN15PixelControllerIL6EOrder66ELi1ELm4294967295EE21init_binary_ditheringEvE1R
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN15PixelControllerIL6EOrder66ELi1ELm4294967295EE16enable_ditheringEh
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN15PixelControllerIL6EOrder66ELi1ELm4294967295EEC5EPK4CRGBiRS2_h
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19CPixelLEDControllerIL6EOrder66ELi1ELm4294967295EE4showEPK4CRGBiS2_
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN15PixelControllerIL6EOrder66ELi1ELm4294967295EEC5ERK4CRGBiRS2_h
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN19CPixelLEDControllerIL6EOrder66ELi1ELm4294967295EE9showColorERK4CRGBiS2_
0x00000000 0x0 .pio\build\uno\src\Blink.ino.cpp.o (symbol from plugin)
.data 0x00000000 0x0 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
.text 0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController12clearLedDataEv
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController13beginShowLedsEv
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController11endShowLedsEPv
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController13getAdjustmentEh
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController8showLedsEh
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN14CLEDController9showColorERK4CRGBh
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZNK14CLEDController17getMaxRefreshRateEv
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.gnu.linkonce.t._ZN8CFastLED4showEv
0x00000000 0x0 FastLED.cpp.o (symbol from plugin)
.text 0x00000000 0x0 crgb.cpp.o (symbol from plugin)
.text 0x00000000 0x0 lib8tion.cpp.o (symbol from plugin)
.text 0x00000000 0x0 abi.cpp.o (symbol from plugin)
.text 0x00000000 0x0 hooks.c.o (symbol from plugin)
.text 0x00000000 0x0 main.cpp.o (symbol from plugin)
.text 0x00000000 0x0 wiring.c.o (symbol from plugin)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.mul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.div
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.prologue
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.builtins
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.fmul
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text.libgcc.fixed
0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.text 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a(abort.o)
.data 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a(abort.o)
.bss 0x00000000 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a(abort.o)
Memory Configuration
Name Origin Length Attributes
text 0x00000000 0x00020000 xr
data 0x00800060 0x0000ffa0 rw !x
eeprom 0x00810000 0x00010000 rw !x
fuse 0x00820000 0x00000003 rw !x
lock 0x00830000 0x00000400 rw !x
signature 0x00840000 0x00000400 rw !x
user_signatures 0x00850000 0x00000400 rw !x
*default* 0x00000000 0xffffffff
Linker script and memory map
Address of section .data set to 0x800100
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
LOAD .pio\build\uno\src\Blink.ino.cpp.o
LOAD C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
START GROUP
LOAD .pio\build\uno\liba19\libSoftwareSerial.a
LOAD .pio\build\uno\lib8b0\libSPI.a
LOAD .pio\build\uno\lib6ec\libsrc.a
LOAD .pio\build\uno\libFrameworkArduinoVariant.a
LOAD .pio\build\uno\libFrameworkArduino.a
END GROUP
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libm.a
START GROUP
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libm.a
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a
LOAD c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libatmega328p.a
END GROUP
0x00020000 __TEXT_REGION_LENGTH__ = DEFINED (__TEXT_REGION_LENGTH__)?__TEXT_REGION_LENGTH__:0x20000
0x0000ffa0 __DATA_REGION_LENGTH__ = DEFINED (__DATA_REGION_LENGTH__)?__DATA_REGION_LENGTH__:0xffa0
0x00010000 __EEPROM_REGION_LENGTH__ = DEFINED (__EEPROM_REGION_LENGTH__)?__EEPROM_REGION_LENGTH__:0x10000
[0x00000003] __FUSE_REGION_LENGTH__ = DEFINED (__FUSE_REGION_LENGTH__)?__FUSE_REGION_LENGTH__:0x400
0x00000400 __LOCK_REGION_LENGTH__ = DEFINED (__LOCK_REGION_LENGTH__)?__LOCK_REGION_LENGTH__:0x400
0x00000400 __SIGNATURE_REGION_LENGTH__ = DEFINED (__SIGNATURE_REGION_LENGTH__)?__SIGNATURE_REGION_LENGTH__:0x400
0x00000400 __USER_SIGNATURE_REGION_LENGTH__ = DEFINED (__USER_SIGNATURE_REGION_LENGTH__)?__USER_SIGNATURE_REGION_LENGTH__:0x400
.hash
*(.hash)
.dynsym
*(.dynsym)
.dynstr
*(.dynstr)
.gnu.version
*(.gnu.version)
.gnu.version_d
*(.gnu.version_d)
.gnu.version_r
*(.gnu.version_r)
.rel.init
*(.rel.init)
.rela.init
*(.rela.init)
.rel.text
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t*)
.rela.text
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t*)
.rel.fini
*(.rel.fini)
.rela.fini
*(.rela.fini)
.rel.rodata
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r*)
.rela.rodata
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r*)
.rel.data
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d*)
.rela.data
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d*)
.rel.ctors
*(.rel.ctors)
.rela.ctors
*(.rela.ctors)
.rel.dtors
*(.rel.dtors)
.rela.dtors
*(.rela.dtors)
.rel.got
*(.rel.got)
.rela.got
*(.rela.got)
.rel.bss
*(.rel.bss)
.rela.bss
*(.rela.bss)
.rel.plt
*(.rel.plt)
.rela.plt
*(.rela.plt)
.text 0x00000000 0xe8e
*(.vectors)
.vectors 0x00000000 0x68 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
0x00000000 __vector_default
0x00000000 __vectors
*(.vectors)
*(.progmem.gcc*)
0x00000068 . = ALIGN (0x2)
0x00000068 __trampolines_start = .
*(.trampolines)
.trampolines 0x00000068 0x0 linker stubs
*(.trampolines*)
0x00000068 __trampolines_end = .
*libprintf_flt.a:*(.progmem.data)
*libc.a:*(.progmem.data)
*(.progmem*)
0x00000068 . = ALIGN (0x2)
*(.jumptables)
*(.jumptables*)
*(.lowtext)
*(.lowtext*)
0x00000068 __ctors_start = .
*(.ctors)
.ctors 0x00000068 0x2 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
0x0000006a __ctors_end = .
0x0000006a __dtors_start = .
*(.dtors)
0x0000006a __dtors_end = .
SORT(*)(.ctors)
SORT(*)(.dtors)
*(.init0)
.init0 0x0000006a 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
0x0000006a __init
*(.init0)
*(.init1)
*(.init1)
*(.init2)
.init2 0x0000006a 0xc c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
*(.init2)
*(.init3)
*(.init3)
*(.init4)
.init4 0x00000076 0x16 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
0x00000076 __do_copy_data
.init4 0x0000008c 0x10 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
0x0000008c __do_clear_bss
*(.init4)
*(.init5)
*(.init5)
*(.init6)
.init6 0x0000009c 0x16 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
0x0000009c __do_global_ctors
*(.init6)
*(.init7)
*(.init7)
*(.init8)
*(.init8)
*(.init9)
.init9 0x000000b2 0x8 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
*(.init9)
*(.text)
.text 0x000000ba 0x4 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
0x000000ba __vector_22
0x000000ba __vector_1
0x000000ba __vector_24
0x000000ba __vector_12
0x000000ba __bad_interrupt
0x000000ba __vector_6
0x000000ba __vector_3
0x000000ba __vector_23
0x000000ba __vector_25
0x000000ba __vector_11
0x000000ba __vector_13
0x000000ba __vector_17
0x000000ba __vector_19
0x000000ba __vector_7
0x000000ba __vector_5
0x000000ba __vector_4
0x000000ba __vector_9
0x000000ba __vector_2
0x000000ba __vector_21
0x000000ba __vector_15
0x000000ba __vector_8
0x000000ba __vector_14
0x000000ba __vector_10
0x000000ba __vector_18
0x000000ba __vector_20
.text 0x000000be 0xaf0 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
0x00000b1a __vector_16
0x00000bae . = ALIGN (0x2)
*(.text.*)
.text.startup 0x00000bae 0x1f0 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
0x00000bae main
.text.libgcc.div
0x00000d9e 0x28 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
0x00000d9e _div
0x00000d9e __divmodhi4
.text.libgcc.div
0x00000dc6 0x44 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
0x00000dc6 __udivmodsi4
.text.libgcc.mul
0x00000e0a 0x1e c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
0x00000e0a __umulhisi3
.text.libgcc.mul
0x00000e28 0xe c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
0x00000e28 __usmulhisi3
0x00000e2c __usmulhisi3_tail
.text.libgcc.mul
0x00000e36 0x16 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
0x00000e36 __muluhisi3
.text.libgcc.div
0x00000e4c 0x28 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
0x00000e4c __udivmodhi4
.text.libgcc 0x00000e74 0xc c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
0x00000e74 __tablejump2__
.text.avr-libc
0x00000e80 0xa c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5\libc.a(abort.o)
0x00000e80 abort
0x00000e8a . = ALIGN (0x2)
*(.fini9)
.fini9 0x00000e8a 0x0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
0x00000e8a _exit
0x00000e8a exit
*(.fini9)
*(.fini8)
*(.fini8)
*(.fini7)
*(.fini7)
*(.fini6)
*(.fini6)
*(.fini5)
*(.fini5)
*(.fini4)
*(.fini4)
*(.fini3)
*(.fini3)
*(.fini2)
*(.fini2)
*(.fini1)
*(.fini1)
*(.fini0)
.fini0 0x00000e8a 0x4 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
*(.fini0)
0x00000e8e _etext = .
.data 0x00800100 0x2e load address 0x00000e8e
0x00800100 PROVIDE (__data_start, .)
*(.data)
*(.data*)
*(.gnu.linkonce.d*)
*(.rodata)
.rodata 0x00800100 0x2e C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
*(.rodata*)
*(.gnu.linkonce.r*)
0x0080012e . = ALIGN (0x2)
0x0080012e _edata = .
0x0080012e PROVIDE (__data_end, .)
.bss 0x0080012e 0x48
0x0080012e PROVIDE (__bss_start, .)
*(.bss)
.bss 0x0080012e 0x48 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
*(.bss*)
*(COMMON)
0x00800176 PROVIDE (__bss_end, .)
0x00000e8e __data_load_start = LOADADDR (.data)
0x00000ebc __data_load_end = (__data_load_start + SIZEOF (.data))
.noinit 0x00800176 0x0
[!provide] PROVIDE (__noinit_start, .)
*(.noinit*)
[!provide] PROVIDE (__noinit_end, .)
0x00800176 _end = .
[!provide] PROVIDE (__heap_start, .)
.eeprom 0x00810000 0x0
*(.eeprom*)
0x00810000 __eeprom_end = .
.fuse
*(.fuse)
*(.lfuse)
*(.hfuse)
*(.efuse)
.lock
*(.lock*)
.signature
*(.signature*)
.user_signatures
*(.user_signatures*)
.stab
*(.stab)
.stabstr
*(.stabstr)
.stab.excl
*(.stab.excl)
.stab.exclstr
*(.stab.exclstr)
.stab.index
*(.stab.index)
.stab.indexstr
*(.stab.indexstr)
.comment 0x00000000 0x11
*(.comment)
.comment 0x00000000 0x11 C:\Users\niteris\AppData\Local\Temp\ccAA6ajC.ltrans0.ltrans.o
0x12 (size before relaxing)
.note.gnu.avr.deviceinfo
0x00000000 0x40
.note.gnu.avr.deviceinfo
0x00000000 0x40 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.note.gnu.build-id
*(.note.gnu.build-id)
.debug
*(.debug)
.line
*(.line)
.debug_srcinfo
*(.debug_srcinfo)
.debug_sfnames
*(.debug_sfnames)
.debug_aranges 0x00000000 0x160
*(.debug_aranges)
.debug_aranges
0x00000000 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.debug_aranges
0x00000020 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.debug_aranges
0x00000040 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.debug_aranges
0x00000060 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.debug_aranges
0x00000080 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.debug_aranges
0x000000a0 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.debug_aranges
0x000000c0 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.debug_aranges
0x000000e0 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.debug_aranges
0x00000100 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.debug_aranges
0x00000120 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.debug_aranges
0x00000140 0x20 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.debug_pubnames
*(.debug_pubnames)
.debug_info 0x00000000 0xdfd
*(.debug_info .gnu.linkonce.wi.*)
.debug_info 0x00000000 0x5f4 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.debug_info 0x000005f4 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.debug_info 0x000006af 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.debug_info 0x0000076a 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.debug_info 0x00000825 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.debug_info 0x000008e0 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.debug_info 0x0000099b 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.debug_info 0x00000a56 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.debug_info 0x00000b11 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.debug_info 0x00000bcc 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.debug_info 0x00000c87 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.debug_info 0x00000d42 0xbb c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.debug_abbrev 0x00000000 0x67e
*(.debug_abbrev)
.debug_abbrev 0x00000000 0x5a2 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.debug_abbrev 0x000005a2 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.debug_abbrev 0x000005b6 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.debug_abbrev 0x000005ca 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.debug_abbrev 0x000005de 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.debug_abbrev 0x000005f2 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.debug_abbrev 0x00000606 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.debug_abbrev 0x0000061a 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.debug_abbrev 0x0000062e 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.debug_abbrev 0x00000642 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.debug_abbrev 0x00000656 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.debug_abbrev 0x0000066a 0x14 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.debug_line 0x00000000 0x71a
*(.debug_line .debug_line.* .debug_line_end)
.debug_line 0x00000000 0x1a c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.debug_line 0x0000001a 0x62 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_exit.o)
.debug_line 0x0000007c 0xc8 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_divmodhi4.o)
.debug_line 0x00000144 0x122 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodsi4.o)
.debug_line 0x00000266 0x98 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_copy_data.o)
.debug_line 0x000002fe 0x86 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_clear_bss.o)
.debug_line 0x00000384 0x92 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_ctors.o)
.debug_line 0x00000416 0xb0 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_umulhisi3.o)
.debug_line 0x000004c6 0x7a c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_usmulhisi3.o)
.debug_line 0x00000540 0x92 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_muluhisi3.o)
.debug_line 0x000005d2 0xce c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_udivmodhi4.o)
.debug_line 0x000006a0 0x7a c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/avr5\libgcc.a(_tablejump2.o)
.debug_frame
*(.debug_frame)
.debug_str 0x00000000 0x208
*(.debug_str)
.debug_str 0x00000000 0x208 c:/users/niteris/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega328p.o
.debug_loc
*(.debug_loc)
.debug_macinfo
*(.debug_macinfo)
.debug_weaknames
*(.debug_weaknames)
.debug_funcnames
*(.debug_funcnames)
.debug_typenames
*(.debug_typenames)
.debug_varnames
*(.debug_varnames)
.debug_pubtypes
*(.debug_pubtypes)
.debug_ranges
*(.debug_ranges)
.debug_macro
*(.debug_macro)
OUTPUT(.pio\build\uno\firmware.elf elf32-avr)
LOAD linker stubs

View file

@ -0,0 +1,46 @@
import argparse
import subprocess
import sys
from typing import List, Tuple
def parse_args() -> Tuple[argparse.Namespace, list[str]]:
parser = argparse.ArgumentParser(description="Compile wasm")
parser.add_argument(
"sketch_dir",
nargs="?",
default="examples/wasm",
help="The directory of the sketch to compile",
)
# return parser.parse_args()
known_args, unknown_args = parser.parse_known_args()
return known_args, unknown_args
def run_command(cmd_list: List[str]) -> int:
"""Run a command and return its exit code."""
cmd_str = subprocess.list2cmdline(cmd_list)
print(f"Running command: {cmd_str}")
rtn = subprocess.call(cmd_list)
if rtn != 0:
print(f"ERROR: Command {cmd_str} failed with return code {rtn}")
return rtn
def main() -> int:
args, unknown_args = parse_args()
# First run the build command
build_cmd = ["fastled", args.sketch_dir, "--build"] + unknown_args
build_result = run_command(build_cmd)
# Then run the compile command
compile_cmd = ["fastled", args.sketch_dir, "--just-compile"] + unknown_args
compile_result = run_command(compile_cmd)
# Return non-zero if either command failed
return build_result if build_result != 0 else compile_result
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,108 @@
import asyncio
import os
import sys
import time
from pathlib import Path
from playwright.async_api import async_playwright # type: ignore
HERE = Path(__file__).parent
PROJECT_ROOT = HERE.parent
# Ensure Playwright browsers are installed
def install_playwright_browsers():
print("Installing Playwright browsers...")
try:
# Simulate the `playwright install` command
os.system(f"{sys.executable} -m playwright install chromium")
print("Playwright browsers installed successfully.")
except Exception as e:
print(f"Failed to install Playwright browsers: {e}", file=sys.stderr)
sys.exit(1)
# Start an HTTP server on the dynamic port
def start_http_server(port: int, directory: Path):
from fastled import Test # type: ignore
server_process = Test.spawn_http_server(
directory=directory, port=port, open_browser=False
)
return server_process
async def main() -> None:
install_playwright_browsers()
# Find an available port
port = (
8080 # Todo, figure out why the http server ignores any port other than 8080.
)
print(f"Using port: {port}")
# Start the HTTP server
os.chdir(str(PROJECT_ROOT))
directory = Path("examples/wasm/fastled_js")
server_process = start_http_server(port=port, directory=directory)
try:
# Give the server some time to start
time.sleep(2)
# Use Playwright to test the server
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
try:
await page.goto(f"http://localhost:{port}", timeout=30000)
# Listen for console messages
def console_log_handler(msg):
if "INVALID_OPERATION" in msg.text:
print(
"INVALID_OPERATION detected in console log", file=sys.stderr
)
raise Exception("INVALID_OPERATION detected in console log")
page.on("console", console_log_handler)
# Evaluate and monitor window.frameCallCount
await page.evaluate(
"""
window.frameCallCount = 0;
globalThis.FastLED_onFrame = (jsonStr) => {
console.log('FastLED_onFrame called with:', jsonStr);
window.frameCallCount++;
};
"""
)
await page.wait_for_timeout(5000)
call_count = await page.evaluate("window.frameCallCount")
if call_count > 0:
print(
f"Success: FastLED.js was initialized and FastLED_onFrame was called {call_count} times"
)
else:
print(
"Error: FastLED.js had something go wrong and FastLED_onFrame was not called within 5 seconds",
file=sys.stderr,
)
raise Exception("FastLED.js failed to initialize")
except Exception as e:
print(f"An error occurred: {e}", file=sys.stderr)
raise Exception(f"An error occurred: {e}") from e
finally:
await browser.close()
finally:
# Terminate the server process
server_process.terminate()
# Run the main function
if __name__ == "__main__":
sys.exit(asyncio.run(main()))

25
libraries/FastLED/clean Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
set -x
# cd to the directory of the script
cd "$(dirname "$0")"
rm -rf .venv
rm -rf .build
rm -rf .pio
rm -rf ci/tmp
rm -rf tests/.build
rm -rf .*_cache
rm -rf __pycache__
rm -rf .tools
rm -rf ci/__pycache__
rm -rf ci/.*_cache
# remove any CMakeCache.txt files
find . -name "CMakeCache.txt" -type f -delete
rm -f uv.lock

View file

@ -0,0 +1,134 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* "Trolling" and excessive complaints without the due benefit of contributing code.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders via a bug report with the title [CODE OF CONDUCT] as the beginning text.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

37
libraries/FastLED/compile Executable file
View file

@ -0,0 +1,37 @@
#!/bin/bash
set -e
# Function to find Python executable
find_python() {
if command -v python3 &> /dev/null; then
echo "python3"
elif command -v python &> /dev/null; then
echo "python"
else
echo "Python not found. Please install Python 3."
exit 1
fi
}
# Check if uv is installed, if not, install it
if ! command -v uv &> /dev/null; then
echo "uv command not found. Installing uv..."
PYTHON=$(find_python)
$PYTHON -m pip install uv
fi
cd "$(dirname "$0")"
# if .venv not found
if [ ! -d .venv ]; then
# create virtual environment
./install
fi
interactive_stmt=""
# if no arguments
if [ $# -eq 0 ]; then
# set interactive statement
interactive_stmt="--interactive"
fi
uv run ci/ci-compile.py $interactive_stmt "$@"

41
libraries/FastLED/compile.bat Executable file
View file

@ -0,0 +1,41 @@
@echo off
setlocal
rem Function to find Python executable
where python >nul 2>nul
if %errorlevel% equ 0 (
set "PYTHON=python"
) else (
where python3 >nul 2>nul
if %errorlevel% equ 0 (
set "PYTHON=python3"
) else (
echo Python not found. Please install Python 3.
exit /b 1
)
)
rem Check if uv is installed, if not, install it
where uv >nul 2>nul
if %errorlevel% neq 0 (
echo "uv" command not found. Please install "uv" by running "pip install uv" and try again.
exit /b 1
)
rem Change to the directory of the batch file
cd /d "%~dp0"
rem Check if .venv directory exists
if not exist .venv (
rem Create virtual environment
call install.bat
)
set "interactive_stmt="
rem Check if no arguments were provided
if "%~1"=="" (
set "interactive_stmt=--interactive"
)
rem Run the Python script
.venv\Scripts\python.exe ci\ci-compile.py %interactive_stmt% %*

View file

@ -0,0 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS := ./src src/platforms/esp/32
COMPONENT_SRCDIRS := ./src src/platforms/esp/32

View file

@ -0,0 +1,31 @@
* Advanced Color Gradient using online version of FastLED.
* https://wokwi.com/projects/285170662915441160
* LedMapper tool for irregular shapes
* https://github.com/jasoncoon/led-mapper
* list of projects on reddit:
* https://www.reddit.com/r/FastLED/wiki/index/user_examples/
* mesh networked esp32 with mutli wifi connections for redundancy
* https://github.com/Souravgoswami/Arduino-FastLED-Cool-Effects
* FastLED-IR: https://github.com/marcmerlin/FastLED-IR
* https://github.com/marcmerlin/NeoMatrix-FastLED-IR?tab=readme-ov-file
* Tree IR:
* https://www.evilgeniuslabs.org/tree-v2
* Esp32 server for fastled
* https://github.com/jasoncoon/esp32-fastled-webserver
* Strip tease - cool fx for strips
* https://github.com/lpaolini/Striptease?tab=readme-ov-file
* Soulematelights:
* https://editor.soulmatelights.com/gallery
* https://github.com/marcmerlin/FastLED_NeoMatrix_SmartMatrix_LEDMatrix_GFX_Demos/blob/master/LEDMatrix/Table_Mark_Estes/Table_Mark_Estes.ino
* llm-min.txt
* https://github.com/marv1nnnnn/llm-min.txt

3
libraries/FastLED/dev.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
uv run dev/dev.py

View file

@ -0,0 +1,58 @@
/// @file DemoReel100.ino
/// @brief FastLED "100 lines of code" demo reel, showing off some effects
/// @example DemoReel100.ino
#include "fx/2d/animartrix.hpp"
#include <Arduino.h>
#include "FastLED.h"
#include <iostream>
#define WIDTH 22 // how many columns?
#define HEIGHT 22 // how many rows?
#define DEBUG_PRINT 0
#define NUM_LED (WIDTH * HEIGHT)
#define SERPENTINE true
#define CYCLE_THROUGH_ANIMATIONS 10
CRGB leds[NUM_LED]; // framebuffer
XYMap xyMap(WIDTH, HEIGHT, SERPENTINE);
AnimartrixPtr fxAnimator = AnimartrixPtr::New(xyMap, POLAR_WAVES);
void setup() {
FastLED.addLeds<WS2811, 2, GRB>(leds, NUM_LED);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 2000); // optional current limiting [5V, 2000mA]
Serial.begin(115200); // check serial monitor for current fps count
// fill_rainbow(leds, NUM_LED, 0);
fill_solid(leds, NUM_LED, CRGB::Black);
FastLED.show();
}
void loop() {
uint32_t now = millis();
// Change animation every 10 seconds
#if CYCLE_THROUGH_ANIMATIONS > 0
EVERY_N_SECONDS(CYCLE_THROUGH_ANIMATIONS) {
fxAnimator->fxNext();
#if DEBUG_PRINT
std::cout << "New animation: " << fxAnimator.fxName() << std::endl;
#endif
}
#endif
fxAnimator->draw(Fx::DrawContext{millis(), leds});
FastLED.show();
uint32_t elapsed = millis() - now;
EVERY_N_SECONDS(1) {
#if DEBUG_PRINT
std::cout << "frame time: " << elapsed << "ms" << std::endl;
#endif
}
}

Some files were not shown because too many files have changed in this diff Show more