Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
518 lines (316 sloc) 13.5 KB
VaRGB library example usage.
Copyright (C) 2013 Pat Deegan. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
***************************** OVERVIEW *****************************
In this program, we do a lot of relatively convoluted stuff, just to
play with the various "logical" transition curves.
You can see it running at the end of the intro/demo video:
You can compile the program to output data to the serial port and/or
to use the TinyBrite library to display everything through a ShiftBrite
or MegaBrite.
The USE_TINYBRITE and DEBUG_TO_SERIAL #defines, below, configure which
functionality is actually enabled. If you want to play with the TinyBrite
output, get the lib at
***************************** HW SETUP *****************************
Hardware setup when using an actual Shift/MegaBrite to show values.
The following code expects an Arduino-compatible device tied to a
chain of three *Brites (can be ShiftBrite, MegaBrite) or anything
that uses the A6281 PWM LED driver.
Basic hardware setup (power connections/voltage regulation for the
Arduino are not shown):
Arduino *Brite device
+===========+datapin +============+ DO (data out)
| |----------------|DI (data) |-------------->
| Ardweeny | | | EO (enable out)
| | ------|EI (enable) |-------------->
| | | | |
| | GND | |
| |clockpin | | CO (clock out)
| or any |----------------|CI (clock) |--------------> To second *Brite...
| type |latchpin | | LO (latch out)
| *duino |----------------|LI (latch) |-------------->
+===========+ +============+
*** NOTE ***: you may want to use pull up resistors on the
clock and data pins (pins A and B, above).
This just means putting a (few k) resistor between +5V and
each pin.
/* *** Includes *** */
// vaRGB includes
#include <VaRGB.h>
#include <VaRGBCurves.h>
// uncomment the following line to enable use
// of the TinyBrite library (which must be installed)
// Get it at
// #define USE_TINYBRITE
// We'll use TinyBrite to set some MegaBrites
#include <TinyBrite.h>
// *** DEBUG ***
// uncomment the define below to send
// color adjustment debug info to Serial
// if we're not using TinyBrite, send output to serial
#define serial_baud_rate 115200
// a few useful defines
#define megabrite_num_modules 1
// the pins we're using to talk to the 'brites
#define brite_datapin 3
#define brite_latchpin 4
#define brite_clockpin 5
#define max_color_val 1023
#define half_color_val 511
#define SERIAL_OUT(msg) Serial.print(msg)
#define SERIAL_OUTLN(msg) Serial.println(msg)
void out_color(vargb::VaRGBColorValue c)
byte i;
byte amount = c/16;
byte spaces = (64 - amount)/2;
for (i=0; i<spaces; i++)
Serial.print(" ");
for (i=0; i<amount; i++)
for (i=0; i<spaces; i++)
Serial.print(" ");
#define SERIAL_OUT(msg) ;
#define SERIAL_OUTLN(msg) ;
/* MegaBrites
** To control the RGB LEDs, we'll create a TinyBrite global object.
** You can use any method you like in the set-color callback to control
** your R-G-B settings, I just have a particular fondness for the MegaBrites
** and the TinyBrite lib ('cause, you know, I wrote it ;-) ).
** See the TinyBrite docs for info on this
** (, the short version
** is that we call the constructor with the number of brites in the chain.
TinyBrite Brite_chain(megabrite_num_modules);
/* *** The VaRGB driver object *** */
// callback declarations.
// We need a color-setting callback in order to create
// our VaRGB driver.
// this is a forward declarations, the actual function
// is defined below.
// You can change the name, but not the signature
void setColorCB(vargb::ColorSettings * set_to);
// with our callback recognizable by the compiler as a function of the
// correct type, we create the VaRGB driver object:
vargb::VaRGB logic_driver(setColorCB);
/* *** Schedules *** */
// Now we need at least one RGB-variation schedule to play with.
// Each schedule encapsulates a series of transitions between R-G-B settings,
// that will run in the order they are added to the schedule (below).
// Using multiple schedules allows you to package related curves (transition
// logic) together so you can easily switch between them.
vargb::Schedule transition_schedule;
/* *** Curves -- the RGB transition specifications *** */
// As mentioned, a schedule is comprised of one or more curves. These curves
// describe the behaviour of the RGB settings with time.
// There are a few kinds of curves available with the library, like:
// * Linear -- each RGB component varies linearly with time (a constant
// slope), like a simple "fade" in or out
// * Sine -- RGB components vary as sine waves, so quickly around their
// mid-point and slower at the extremes
// * Flasher -- RGB components are alternated between 0 and their setting
// In addition, there are "logic" curves which allow you to combine
// other curves with AND, OR and NOT logic.
// You can stack logic curves together, for instance taking a sine wave
// then invert a channel (say, red) using a Not and combine with a linear
// curve that has the red channel masked out (using an And), with an Or.
// Logically, this would look like
// Or(And(Linear(...), Constant(...)), Not(Sine(...), true, false, false))
// now, let's create a few of these curves...
// a single simple "white" flasher
#define num_flashes 15
#define time_flashing 3
vargb::Curve::Flasher flasher_curve(max_color_val,
max_color_val, max_color_val,
time_flashing, num_flashes);
vargb::Curve::Flasher second_flasher(max_color_val,
max_color_val, max_color_val,
time_flashing, num_flashes);
// The Not is created with the curve to apply it to, along with a boolean to denote whether
// each of the RED, GREEN, and BLUE channels are affected. Here we only NOT the GREEN:
vargb::Curve::Not not_flasher_curve(&second_flasher, false, true, false);
// a sine wave that goes from 0 to max on all channels
#define sine_runtime 8
#define sinewave_seconds_per_cycle 2
vargb::Curve::Sine sine_wave(max_color_val, max_color_val, max_color_val,
sine_runtime, sinewave_seconds_per_cycle);
// Now we half-rectify that sine wave by creating a Threshold curve
vargb::Curve::Threshold half_rectified_sine(&sine_wave, 511, vargb::Curve::ThresholdAbove, 512);
// More rectification, we'll fully rectify the red and blue components of the signal by doing this:
// Create a second sine wave of the same duration and frequency as the first
vargb::Curve::Sine sine_wave_2(max_color_val, max_color_val, max_color_val,
sine_runtime, sinewave_seconds_per_cycle);
// Invert the red and blue channels, this will shift the phase of the sine wave on these channels
vargb::Curve::Not inverted_sine(&sine_wave_2, true, false, true);
// create a mask so we'll let through only the red and blue channels
vargb::Curve::Constant redandblue_mask_curve(max_color_val, 0, max_color_val, sine_runtime);
// mask the inverted sine, by AND-ing with the mask
vargb::Curve::AndLogic redandblue_inverted_sine(&redandblue_mask_curve, &inverted_sine);
// half-rectify our red+blue sine signals, using a Threshold
vargb::Curve::Threshold half_rectified_redandbluesine(&redandblue_inverted_sine, 511, vargb::Curve::ThresholdAbove, 512);
// combine the half-rectified red+blue with the original half-rectified sine wave
vargb::Curve::OrLogic fully_rectified_red_and_blue(&half_rectified_redandbluesine, &half_rectified_sine);
/* grand finale */
// For our grand finale, we'll have three distinct waves for each color channel
#define grand_finale_seconds 22
// green will have a linear sawtooth, a signal that rises steadily to max, then abruptly
// drops to 0:
// create a steady rising linear value on green
vargb::Curve::Linear steady_rising(0, max_color_val, 0, grand_finale_seconds);
// create a mask for some of the lower bits, i.e. 124 == b001111100
vargb::Curve::Constant green_mask_curve(0, 124, 0, grand_finale_seconds);
// apply the mask to the linear green curve
vargb::Curve::AndLogic masked_steady_rising(&steady_rising, &green_mask_curve);
// left-shift the resulting values up, so the signal strength is very visible:
// yay, we have a sawtooth
vargb::Curve::Shift sawtooth(&masked_steady_rising, 3, vargb::Curve::ShiftLeft);
// for the red channel, we'll have a pseudo-sawtooth, based on a sine signal
// so it will vary the rate at which it changes and the direction of the sawtooth
// create a slowly changing sine wave
vargb::Curve::Sine slow_sine(max_color_val, 0, 0, grand_finale_seconds, 10);
// create a mask for the red channel, 62 == b0000111110, so we keep the lower bits
vargb::Curve::Constant red_mask_curve(62, 0, 0, grand_finale_seconds);
// mask that slow sine wave by AND-ing with our constant value curve
vargb::Curve::AndLogic masked_sine(&slow_sine, &red_mask_curve);
// shift the signal up (left) so we get some visible action
vargb::Curve::Shift alt_sawtooth(&masked_sine, 4, vargb::Curve::ShiftLeft);
// now we have two kinds of sawtooth on the red and green channels
// combine them into one curve, by OR-ing:
vargb::Curve::OrLogic both_sawtooths(&alt_sawtooth, &sawtooth);
// create a quicker sine wave on the blue channel
vargb::Curve::Sine quick_sine(0, 0, max_color_val, grand_finale_seconds, 2);
// combine that quick sine wave with the sawtooths from before
vargb::Curve::OrLogic sawtooths_and_sine(&both_sawtooths, &quick_sine);
// one second break...
vargb::Curve::Constant rest_curve(0,0,0, 1);
// straight to black
vargb::Curve::Linear instant_black(0,0,0,0);
/* *** Callbacks *** */
// these are the callbacks used by the VaRGB driver, that we promised we would
// be defining above (the forward declarations)
void setColorCB(vargb::ColorSettings * set_to)
// Some debug output
// send the update
Brite_chain.sendColor(set_to->red,set_to->green, set_to->blue);
/* *** Arduino functions *** */
// here are the standard Arduino functions, setup() to do just that
// and loop() which will be called repeatedly for ever and ever.
void setup()
// Tell the schedule what curves it is responsible for:
// straight flasher:
// inverted green flasher:
// straight sine wave
// half-rectified sine wave
// inverted red and blue sine
// fully rectified
// blacken
// wow me, amadeus
// break time
// Finally, tell our VaRGB driver which schedule to use
// two little TODOs for the TinyBrite: tell it which pins
// the chain is setup on, and tell it to keep track of state
// because we're using getState() in the color-setting callbacks:
// pin setup
Brite_chain.setup(brite_datapin, brite_clockpin, brite_latchpin);
// auto-updates "on"
void loop()
// see the main loop() description in the "Basics" example for details. In
// essence, we need to tick all our VaRGB drivers and wait around a bit.
// here, we do it "right" by ensuring we delay the correct amount
unsigned long time_spent_putzing_around_ms = 0;
unsigned long start_time = millis();
// do anything you want to do here...
// ...
// time passes...
// figure out if we spent any measurable time doing stuff...
unsigned long end_time = millis();
if (end_time > start_time)
time_spent_putzing_around_ms = end_time - start_time;
// the delay time is constant for all the instances of VaRGB and NOT additive
// so you need only wait "once"
if (time_spent_putzing_around_ms < logic_driver.tickDelayTimeMs())
delay(logic_driver.tickDelayTimeMs() - time_spent_putzing_around_ms);
// and they lived happily ever after. the end.
// PatD