I've just started tinkering with arduinos and my experience level is sufficiently low when it comes to the hardware side in general.
I couldn't really identify a memory leak, but might be wrong about that. I also do not really know how to debug my code, apart from unit tests and print to serial debugging.
If I had a memory leak of say 1 bytes per led->rainbow call, that would mean I would have already reserved around 1.56 MB after the 1556909th call, which is way more than the nano 33 even has, so that shouldn't be the issue, right?
My code is as follows:
main.cpp:
#include <Arduino.h>
#include <USB/PluggableUSBSerial.h>
#include "LED/RgbLED.hpp"
std::unique_ptr<RgbLED> led1;
std::unique_ptr<RgbLED> led2;
int main(void)
{
init();
// we are never going to use getchar/scanf directly
mbed::mbed_file_handle(STDIN_FILENO)->enable_input(false);
#if defined(SERIAL_CDC)
PluggableUSBD().begin();
_SerialUSB.begin(115200);
#endif
setup();
for (;;) {
loop();
if (arduino::serialEventRun) arduino::serialEventRun();
}
return 0;
}
void setup() {
led1 = std::make_unique<RgbLED>(D9, D10, D11);
//led2 = std::make_unique<RgbLED>(D3, D5, D6);
led1->setColor("#FF00FF");
//led2->setColor("#FF00FF");
Serial.begin(8600);
while (!Serial) delay(10);
}
unsigned long counter = 0;
void loop() {
//delay(100);
//led1->pulse(0.0001);
counter++;
Serial.println(counter);
led1->rainbow(0.00005);
}
RgbLED.hpp:
#include <array>
#include "RainbowPhase.hpp"
class RgbLED {
public:
RgbLED(unsigned redPin, unsigned greenPin, unsigned bluePin);
void setColor(int redValue, int greenValue, int blueValue, double brightness);
void setColor(int redValue, int greenValue, int blueValue);
void setColor(std::string color, double brightness);
void setColor(std::string color);
void pulse(double step);
void rainbow(double step, double brightness);
void rainbow(double step);
void normalizeValues();
unsigned const COLOR_MAX_VALUE = 255;
unsigned const BRIGHTNESS_MAX_VALUE = 1;
unsigned const RAINBOW_MAX_VALUE = 1;
unsigned const MIN_VALUE = 0;
private:
void updateLED();
void nextRainbowPhase();
void printStatus() const;
unsigned m_redPin;
unsigned m_greenPin;
unsigned m_bluePin;
double m_redValue;
double m_greenValue;
double m_blueValue;
double m_brightness;
RAINBOW_PHASE m_rainbowPhase;
double m_rainbowValue;
bool m_rising;
};
RgbLED.cpp:
#include "RgbLED.hpp"
#include <Arduino.h>
#include <regex>
#include <utility>
RgbLED::RgbLED(unsigned int redPin, unsigned int greenPin, unsigned int bluePin) {
m_redPin = redPin;
m_greenPin = greenPin;
m_bluePin = bluePin;
m_redValue = MIN_VALUE;
m_greenValue = MIN_VALUE;
m_blueValue = MIN_VALUE;
m_brightness = MIN_VALUE;
m_rising = true;
m_rainbowPhase = NONE;
m_rainbowValue = MIN_VALUE;
}
void RgbLED::setColor(int redValue, int greenValue, int blueValue) {
setColor(redValue, greenValue, blueValue, 1);
}
void RgbLED::setColor(int redValue, int greenValue, int blueValue, double brightness) {
m_redValue = redValue;
m_greenValue = greenValue;
m_blueValue = blueValue;
m_brightness = brightness;
m_rainbowPhase = NONE;
normalizeValues();
updateLED();
}
void RgbLED::setColor(std::string color) {
setColor(std::move(color), BRIGHTNESS_MAX_VALUE);
}
void RgbLED::setColor(std::string color, double brightness) {
std::regex colorRegex("^#?[0-9A-F]{6}$");
if (!std::regex_match(color, colorRegex)) {
return;
}
if(color.at(0) == '#') {
color.erase(0, 1);
}
setColor(std::stol(color.substr(0, 2), nullptr, 16),
std::stol(color.substr(2, 2), nullptr, 16),
std::stol(color.substr(4, 2), nullptr, 16),
brightness);
}
void RgbLED::pulse(double step) {
m_brightness += m_rising ? step : -step;
normalizeValues();
updateLED();
}
void RgbLED::rainbow(double step) {
rainbow(step, BRIGHTNESS_MAX_VALUE);
}
void RgbLED::rainbow(double step, double brightness) {
if (m_rainbowPhase == NONE) {
m_redValue = MIN_VALUE;
m_greenValue = MIN_VALUE;
m_blueValue = MIN_VALUE;
m_brightness = brightness;
nextRainbowPhase();
}
double *value1, *value2;
if (m_rainbowPhase == RED_GREEN) {
value1 = &m_redValue;
value2 = &m_greenValue;
} else if (m_rainbowPhase == GREEN_BLUE) {
value1 = &m_greenValue;
value2 = &m_blueValue;
} else {
value1 = &m_blueValue;
value2 = &m_redValue;
}
double rainbowCalcBuffer = m_rainbowValue * 0.5 * M_PI;
*value1 = COLOR_MAX_VALUE * cos(rainbowCalcBuffer);
*value2 = COLOR_MAX_VALUE * sin(rainbowCalcBuffer);
if (*value1 > *value2) {
double ratio = *value1 / *value2;
*value1 = COLOR_MAX_VALUE;
*value2 = COLOR_MAX_VALUE / ratio;
} else {
double ratio = *value2 / *value1;
*value1 = COLOR_MAX_VALUE / ratio;
*value2 = COLOR_MAX_VALUE;
}
m_rainbowValue += step;
if (m_rainbowValue > RAINBOW_MAX_VALUE) {
nextRainbowPhase();
}
normalizeValues();
updateLED();
}
void RgbLED::normalizeValues() {
auto values = { &m_redValue, &m_greenValue, &m_blueValue };
for (auto const& value : values) {
if (*value < MIN_VALUE) {
*value = MIN_VALUE;
} else if (*value > COLOR_MAX_VALUE) {
*value = COLOR_MAX_VALUE;
}
}
if (m_brightness < MIN_VALUE) {
m_brightness = MIN_VALUE;
m_rising = true;
} else if (m_brightness > BRIGHTNESS_MAX_VALUE) {
m_brightness = BRIGHTNESS_MAX_VALUE;
m_rising = false;
}
}
void RgbLED::updateLED() {
analogWrite(m_redPin, static_cast<int>(round(m_redValue * m_brightness)));
analogWrite(m_greenPin, static_cast<int>(round(m_greenValue * m_brightness)));
analogWrite(m_bluePin, static_cast<int>(round(m_blueValue * m_brightness)));
//printStatus();
}
void RgbLED::nextRainbowPhase() {
if (m_rainbowPhase == RED_GREEN) {
m_rainbowPhase = GREEN_BLUE;
} else if (m_rainbowPhase == GREEN_BLUE) {
m_rainbowPhase = BLUE_RED;
} else if (m_rainbowPhase == BLUE_RED) {
m_rainbowPhase = RED_GREEN;
} else {
m_rainbowPhase = RED_GREEN;
}
m_rainbowValue = MIN_VALUE;
}
void RgbLED::printStatus() const {
if (!Serial) {
return;
}
auto string = "R: " + std::to_string(m_redValue) +
", G: " + std::to_string(m_greenValue) +
", B: " + std::to_string(m_blueValue) +
", %: " + std::to_string(m_brightness);
Serial.println(string.c_str());
}