Skip to content

Instantly share code, notes, and snippets.

@ttocsneb ttocsneb/color.ino
Last active Feb 16, 2020

Embed
What would you like to do?
Moon Light software. This is for my father's birthday present: a light up moon, read more on my blog https://blog.benscraft.info/one-shots/the-moon-project/
#include <stdlib.h>
Color::Color(uint8_t r, uint8_t g, uint8_t b) {
this->r = r;
this->g = g;
this->b = b;
}
uint32_t Color::to32() {
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
//Operators
Color Color::operator+(const Color &o) {
return Color(
r + o.r,
g + o.g,
b + o.b
);
}
SignedColor Color::operator-(const Color &o) {
SignedColor c;
c.r = r - o.r;
c.g = g - o.g;
c.b = b - o.b;
return c;
}
Color Color::operator*(const float f) {
return Color(
r * f,
g * f,
b * f
);
}
SignedColor::operator Color() const {
return Color(abs(r), abs(g), abs(b));
}
SignedColor SignedColor::operator+(SignedColor &o) {
SignedColor c;
c.r = r + o.r;
c.g = g + o.g;
c.b = b + o.b;
return c;
}
SignedColor SignedColor::operator+(Color &o) {
SignedColor c;
c.r = r + o.r;
c.g = g + o.g;
c.b = b + o.b;
return c;
}
SignedColor SignedColor::operator-(SignedColor &o) {
SignedColor c;
c.r = r - o.r;
c.g = g - o.g;
c.b = b - o.b;
return c;
}
SignedColor SignedColor::operator-(Color &o) {
SignedColor c;
c.r = r - o.r;
c.g = g - o.g;
c.b = b - o.b;
return c;
}
SignedColor SignedColor::operator*(const float f) {
SignedColor c;
c.r = r * f;
c.g = g * f;
c.b = b * f;
return c;
}
#include <EEPROM.h>
#include <Adafruit_NeoPixel.h>
#define NEO_LED_PIN 3
#define NUM_NEO 1
#define BTN_LED_PIN 5
#define MOON_TIME 1000
#define MOON_SET_TIME 5000
#define LED_TIME 250
#define LED_FLASH_TIME 1000
#define EE_R 0
#define EE_G 1
#define EE_B 2
Interpolate<uint8_t> led(SMOOTH);
Interpolate<Color> color(SMOOTH);
Adafruit_NeoPixel neopixels(NUM_NEO, NEO_LED_PIN);
control::Mode mode;
Color setColor;
void set_moon(bool value) {
static bool lastValue(!value);
if(value != lastValue) {
if(color.isDone()) {
color.setValue(value ? setColor : Color(0, 0, 0), MOON_TIME);
} else {
color.setValue(value ? setColor : Color(0, 0, 0), color.getTimeLeft() / (1 - color.getDelta()));
}
}
lastValue = value;
}
void set_neo(uint32_t color) {
for(uint8_t i=0; i < NUM_NEO; i++) {
neopixels.setPixelColor(i, color);
}
}
void control::begin() {
EEPROM.begin();
setColor.r = EEPROM.read(EE_R);
setColor.g = EEPROM.read(EE_G);
setColor.b = EEPROM.read(EE_B);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(BTN_LED_PIN, OUTPUT);
neopixels.begin();
set_neo(neopixels.Color(0, 0, 0));
neopixels.show();
led.setValue(0, 0);
color.setValue(Color(0, 0, 0), 0);
setMode(POWER);
}
bool ledOn;
bool moonOn;
void control::update() {
static uint32_t lastTime(millis());
uint32_t currentTime = millis();
if(!led.isDone() || led.justFinished()) {
analogWrite(BTN_LED_PIN, led.getValue());
}
if(!color.isDone() || color.justFinished()) {
set_neo(color.getValue().to32());
neopixels.show();
}
if(mode == BRIGHTNESS) {
if(color.isDone()) {
color.setValue(moonOn ? Color(0, 0, 0) : Color(255, 255, 255), MOON_SET_TIME);
moonOn = !moonOn;
}
if(led.isDone()) {
led.setValue(ledOn ? 0 : 255, LED_FLASH_TIME);
ledOn = !ledOn;
}
}
led.step(currentTime - lastTime);
color.step(currentTime - lastTime);
lastTime = currentTime;
}
void control::setMode(control::Mode m) {
if(mode == BRIGHTNESS && m == POWER) {
setColor = color.getValue();
EEPROM.update(EE_R, setColor.r);
EEPROM.update(EE_G, setColor.g);
EEPROM.update(EE_B, setColor.b);
color.setInterpolation(SMOOTH);
set_moon(true);
color.setValue(setColor, 0);
led.setValue(255, LED_TIME);
} else if(mode == POWER && m == BRIGHTNESS) {
moonOn = true;
color.setInterpolation(LINEAR);
color.setValue(Color(255, 255, 255), MOON_SET_TIME);
ledOn = true;
led.setValue(255, LED_FLASH_TIME);
}
digitalWrite(LED_BUILTIN, m == BRIGHTNESS);
mode = m;
}
uint32_t lastTime = 0;
void control::push() {
lastTime = millis();
switch(mode) {
case POWER:
led.setValue(255, LED_TIME);
set_moon(true);
break;
case BRIGHTNESS:
setMode(POWER);
break;
}
}
void control::release() {
uint32_t t = millis();
switch(mode) {
case POWER:
if(t - lastTime < 500) {
setMode(BRIGHTNESS);
} else {
set_moon(false);
led.setValue(0, LED_TIME);
}
break;
case BRIGHTNESS:
break;
}
}
#define BTN_PIN 8
bool buttonUp;
void setup() {
Serial.begin(9600);
pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
control::begin();
if(!digitalRead(BTN_PIN)) {
buttonUp = false;
control::push();
} else {
buttonUp = true;
}
}
void loop() {
if(digitalRead(BTN_PIN) != buttonUp) {
buttonUp = !buttonUp;
if(buttonUp) {
control::release();
} else {
control::push();
}
}
control::update();
delay(16);
}
////////////////////////////
//
// Moon Project arduino:
//
// This uses code from one of my libraries, which I have embeded into this project so you wont have
// to wory about downloading it.
// https://github.com/ttocsneb/Arduino-Interpolate
//
// You will have to download the Adafruit NeoPixel Library, which you can do from the Library Manager
// (Sketch -> Include Library -> Manage Libraries) or Ctrl + Shift + I
//
// The moon_project file holds no code, and only declaraions.
// The control file holds the main code from control.cpp
// The main file holds the code for starting the sketch (setup and loop)
//
// the color file is the color.cpp from the color.h library
//
// Feel free to make any changes you like :)
//
////////////////////////////
////////////////////////////
//
// Control.h -- The Declaration for the control code
//
////////////////////////////
namespace control {
enum Mode {POWER,BRIGHTNESS};
void begin();
void setMode(Mode mode);
void update();
void push();
void release();
}
////////////////////////////
//
// color.h -- A library I made
// https://github.com/ttocsneb/Arduino-Interpolate/blob/master/src/color.h
//
////////////////////////////
class SignedColor;
/**
* This is used primarily for the Interpolate class if you need to interpolate colors.
*
* I use it for color interpolation with NeoPixels, so it has a function that makes it
* easier to send color to the NeoPixel class: Color::to32()
*
* There is a signed version of this class which is used for subtraction in the interpolate class.
* I may in the future add more operators for more advanced color operations, but for now, there
* is just enough for the Interpolate class.
*
* You may use this class as a template for creating your own interpolateable classes. I found that
* if your class is unsigned, you will need to have a signed version for interpolation to work properly
*
*/
class Color {
public:
uint8_t r;
uint8_t g;
uint8_t b;
/**
* Create a new Color object
*
* @param r red
* @param g green
* @param b blue
*/
Color(uint8_t r=0, uint8_t g=0, uint8_t b=0);
/**
* Converts the Color object to a uint32_t type.
*
* This can be used with the Adafruit NeoPixel library
*
* @return Color compatible with NeoPixels
*/
uint32_t to32();
//These operators are intended for the Interpolate class.
Color operator+(const Color &opperand);
SignedColor operator-(const Color &opperand);
Color operator*(const float f);
};
/**
* The SignedColor class is used for subtraction in the Interpolation class
*
* The interpolation class, when moving down, will at some point be negative.
* standard numbers are fine as they are normally are casted to singed numbers,
* but the color class needs an equivelent singed class to work properly.
*
*/
class SignedColor {
public:
int16_t r;
int16_t g;
int16_t b;
operator Color() const;
SignedColor operator+(SignedColor &b);
SignedColor operator+(Color &b);
SignedColor operator-(SignedColor &b);
SignedColor operator-(Color &b);
SignedColor operator*(const float f);
};
////////////////////////////
//
// Interpolate.h -- A library I made
// https://github.com/ttocsneb/Arduino-Interpolate/blob/master/src/interpolate.h
//
////////////////////////////
#include <stdint.h>
enum InterpolateType {LINEAR,SMOOTH};
/**
* Interpolate numbers
*
* You can use any type that allows addition, signed subtraction, and multiplication with floats
*
* The Color class that comes with this library can be used as a template for creating your own
* interpolateable classes
*
* any number (int, float, signed int, etc.) can be used without issues
*
*/
template<typename T>
class Interpolate {
private:
T begin_;
T end_;
InterpolateType type_;
float delta_;
uint32_t time_;
bool done_;
//smooth interpolation
float smooth() {
return delta_ * delta_ * (3 - (2 * delta_));
}
//linear interpolation
float linear() {
return delta_;
}
public:
/**
* Create a new Interpolate class with a default value
*
* @param interp Interpolation type (default: LINEAR)
*
* @see InterpolateType
*/
Interpolate(InterpolateType interp=LINEAR) : Interpolate(T(), interp) {}
/**
* Create a new Interpolate class with a custom value
*
* @param initial initial value
* @param interp Interpolation Type (default: LINEAR)
*
* @see Interpolate Type
*/
Interpolate(T initial, InterpolateType interp=LINEAR) {
begin_ = initial;
end_ = initial;
setInterpolation(interp);
delta_ = 1;
done_ = true;
}
/**
* Change the Interpolation used
*
* @note it is generally not a good idea to change interpolation
* while in the middle of a change
*
* @param type Interpolation
*
*/
void setInterpolation(InterpolateType type) {
type_ = type;
}
/**
* Get the current value
*
* @return current value
*/
T getValue() {
if(delta_ == 1) {
return end_;
} else if(delta_ == 0) {
return begin_;
}
float delta;
if(type_ == SMOOTH) {
delta = smooth();
} else {
delta = linear();
}
return static_cast<T>((end_ - begin_) * delta + begin_);
}
/**
* Start interpolating to a new value
*
* It doesn't matter if currently in an interpolation, it
* will then start from the current value, then interpolate
* to the final value
*
* @param value final value
* @param t time (could be any unit, but step() needs to use the same units)
* @param t If 0 the change will be immidiate without any interpolation
*/
void setValue(T value, uint32_t t) {
begin_ = getValue();
end_ = value;
time_ = t;
if(t == 0) {
begin_ = value;
delta_ = 1;
return;
}
delta_ = 0;
done_ = false;
}
/**
* progress through the interpolation t amount of time
*
* @param t time (could be any unit, but setValue() needs to use the same units)
*/
void step(uint32_t t) {
if(t > time_ || delta_ >= 1 || time_ == 0) {
delta_ = 1;
return;
}
float delta = static_cast<float>(t) / time_;
if(delta + delta_ > 1) {
delta_ = 1;
} else {
delta_ += delta;
}
}
/**
* Check if the current interpolation has finished
*
* @return true if done
*/
bool isDone() {
return delta_ >= 1;
}
/**
* Check if the current interpolation has just finished since
* the last time this has been called
*
* @return true if just finished
*/
bool justFinished() {
if(isDone() && !done_) {
done_ = true;
return true;
} else {
return false;
}
}
/**
* Get the delta value for the interpolation
*
* @note the returned interpolation is always linear
*
* return delta (0.0 to 1.0)
*/
float getDelta() {
return delta_;
}
/**
* Get the time left in the interpolation
*
* @note returns 0 if done_
*
* @return time
*/
uint32_t getTimeLeft() {
return static_cast<uint32_t>((1 - delta_) * time_);
}
/**
* Get the interpolation type
*
* @return interpolation type
*/
InterpolateType getInterpolation() {
return type_;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.