The Moon Project

While I was browsing thingiverse, and I found a cool model for the moon.  It has both a bump map for the surface–giving the texture of the moon with all of its craters–and a bump map for the darkness on the moon, which is on the inside of the model.  The inner bump map is used for lights: A light on the inside will show darker on the thicker spots of the moon, making it look more like the moon.  You can read more about how it works, and how you can make your own model here.

Printing The Moon

Since my dad’s birthday is coming up, I decided to print one for him.  In the first print, I ran out of filament but had some old extra stuff that was not on a roll.  It ended up tangling up but able to finish.  The two colors were not able to stay together, so I had to reprint it.  The second print was done completely with the old filament.  However, it got tangled in the night and ultimately failed.  I was now out of white filament, so I reprinted with silver.  It turned out great except for the fact that it was silver.  No light could pass through the moon, and so I had to reprint again.  I was able to order some white filament in time and print it in white.  It finished but the printer was having temperature problems, and so parts were under-extruded.  I will have to print again, but I haven’t yet.  Also, did I mention that each moon took 25 hours to print?

Lighting System

After I printed the moons, I had to figure some way to light it up.  My first lighting system was to use a light from a Christmas house.  It worked, but I did not like it.  There were rings of light at the top, and the lighting was uneven.

img_0004

I have a few NeoPixels laying around, as well as a light up button and a Pro Trinket.  So I will create my own lighting system.  I started out making an Interpolation library.  This way the lights can turn on smoothly.  After completing the library, I found that someone had already created one: Ramp.  I will use it in future projects, but for this one, I’ll continue using my home-made library.

The program has two modes: Power, and Brightness.  Power turns on and off the light when the button is pressed/released.  Brightness dims and err, undims the light until the button is pressed switching to power mode and saving the current brightness in EEPROM.  I uploaded my code to a Gist, but I recommend you write your own version while using mine as a guide.

Probably the most tricky part was designing a model for the electronics.  The cone I designed is small, so I have to fit the Pro Trinket with a USB plug in there without it making holes on the outside.  Since it is very specific to my parts, you might want to design your own, but I uploaded both STL and STEP files if you are interested.

Moon

See my code on my gist


#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;
}

view raw

color.ino

hosted with ❤ by GitHub


#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;
}
}

view raw

control.ino

hosted with ❤ by GitHub


#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);
}

view raw

main.ino

hosted with ❤ by GitHub


////////////////////////////
//
// 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_;
}
};

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s