// Errors are 6.x
#include "global.h"
#include "GEColorEffectsBulb.h"
// Bit encoding
//
// A 1 is encoded as 001
// A 0 is encoded as 011
// Global prep area for preparing the data we are going to send to the bulbs
byte _prep[82];
// preset random brightnesses
static const byte _brightnesses[]
#if defined(PC) || defined(RASPBERRY)
#else
PROGMEM
#endif
= { 0, 50, 100, 150, 200 };
// preset random colours
static const short _colours[]
#if defined(PC) || defined(RASPBERRY)
#else
PROGMEM
#endif
// red green blue yellow orange violet white magenta cyan
= { 0xF00, 0x0F0, 0x00F, 0xF80, 0xF20, 0x80F, 0xDDD, 0xF0F, 0x0FF };
// Get a random brightness
byte Bulb::RandomBrightness()
{
// pick one the the available brightnesses
#if defined(PC) || defined(RASPBERRY)
return _brightnesses[rand() % 5];
#else
return pgm_read_byte_near(_brightnesses + random(5));
#endif
}
// Get a random bulb
byte Bulb::RandomBulb(byte bulbs)
{
#if defined(PC) || defined(RASPBERRY)
return (byte)(rand() % bulbs);
#else
return (byte)random(bulbs);
#endif
}
// Get a random colour
short Bulb::RandomColour()
{
// pick one the the available colours
#if defined(PC) || defined(RASPBERRY)
// return the number of parameters for the provided command type
return _colours[rand() % 9];
#else
return pgm_read_word_near(_colours + random(9));
#endif
}
// Get the red component of the bulb colour
byte NonDisplayableBulb::Red(short colour)
{
return (byte)((colour & 0xF00)>>8);
}
// Get the green component of the bulb colour
byte NonDisplayableBulb::Green(short colour)
{
return (byte)((colour & 0xF0)>>4);
}
// Get the blue component of the bulb colour
byte NonDisplayableBulb::Blue(short colour)
{
return (byte)(colour & 0x0F);
}
// create a bulb
void Bulb::Construct(byte string, byte address, short colour, byte brightness)
{
#ifdef INCLUDE_PARAM_CHECK
if ((colour & 0xF000) > 0)
{
#ifdef MINMEMORY
_platform->WriteMessage(MSG_CRITICAL,"6.1\n");
#else
_platform->WriteMessage(MSG_CRITICAL, "Bulb::Construct Invalid colour 0x%X.\n", colour);
#endif
_platform->Error(6);
}
#endif
// save the values
_string = string;
_address = address;
_colour = colour;
_brightness = brightness;
// creating a bulb is a change so set the changed flag
SetChanged();
}
// create a bulb
void NonDisplayableBulb::Construct(short colour, byte brightness)
{
// save the values
_colour = colour;
_brightness = brightness;
// dont need to set changed as these bulbs can't be displayed
}
// Get the bulb brightness
byte NonDisplayableBulb::Brightness()
{
return _brightness;
}
// Set the bulb brightness
void NonDisplayableBulb::SetBrightness(byte brightness)
{
byte b = brightness;
// if greater than maximum make it the maximum
if (brightness > 0xCC)
{
// 0xCC is the highest value allowed
b = 0xCC;
}
if (b == _brightness)
{
return;
}
// save the new brightness
_brightness = b;
// it has been changed
SetChanged();
}
// Get the bulb colour
short NonDisplayableBulb::Colour()
{
// return the colour without the changed flag bit
return (_colour & 0x0FFF);
}
// set the bulb colour
void NonDisplayableBulb::SetColour(short colour)
{
#ifdef INCLUDE_PARAM_CHECK
if ((colour & 0xF000) > 0)
{
#ifdef MINMEMORY
_platform->WriteMessage(MSG_CRITICAL,"6.2\n");
#else
_platform->WriteMessage(MSG_CRITICAL, "NonDisplayableBulb::SetColour Invalid colour 0x%X.\n", colour);
#endif
_platform->Error(6);
}
#endif
// if the colour has changed
if (Colour() != colour)
{
// save the new colour
_colour = colour;
// flag the bulb as changed
SetChanged();
}
}
// flag a bulb as having changed
void NonDisplayableBulb::SetChanged()
{
// set the high bit in the colour
_colour = _colour | 0x8000;
}
// flag a bulb as not having changed
void NonDisplayableBulb::ClearChanged()
{
// clear the high bit in the colour
_colour = _colour & 0x7FFF;
}
// check the bulb changed flag
bool NonDisplayableBulb::IsChanged()
{
// check the high bit
return ((_colour & 0x8000) > 0);
}
// converts a non zero value into an inverted bit ... this is done because the middle bit in an encoded bit is the inverse of the bit value
inline byte Bulb::GetBit(short i)
{
return (i==0);
}
// converts a non zero value into an inverted bit ... the big difference here is the 1 value returned is positional within a byte and is passed in
inline byte Bulb::GetParallelBit(short bit, byte one)
{
if(bit)
{
return 0;
}
else
{
return one;
}
}
// populates the _prep buffer with the command string for 8 bulbs
void Bulb::PopulateMultiBulbBits(Bulb* bulbs[])
{
// clear the buffer
memset(_prep, 0x00, 82);
// look at each string
for (byte i = 0; i < MAXSTRINGS; i++)
{
// if the string has a bulb at this position and it has changed
if (bulbs[i] != NULL && bulbs[i]->IsChanged())
{
#ifdef INCLUDE_MSG_SUPERDETAIL
#ifdef MINMEMORY
_platform->WriteMessage(MSG_SUPERDETAIL,"6.3\n");
#else
_platform->WriteMessage(MSG_SUPERDETAIL, "String %d Sending bulb %d brightness %d red %d green %d blue %d.\n", i, bulbs[i]->Address(), bulbs[i]->Brightness(), NonDisplayableBulb::Red(bulbs[i]->Colour()), NonDisplayableBulb::Green(bulbs[i]->Colour()), NonDisplayableBulb::Blue(bulbs[i]->Colour()));
#endif
#endif
// create a one value which will be at the right position in the byte
byte one = 0x80 >> i;
// Start Bit
_prep[3] += one;
// Address
_prep[5] += GetParallelBit(bulbs[i]->Address() & 0x20, one);
_prep[6] += one;
_prep[8] += GetParallelBit(bulbs[i]->Address() & 0x10, one);
_prep[9] += one;
_prep[11] += GetParallelBit(bulbs[i]->Address() & 0x08, one);
_prep[12] += one;
_prep[14] += GetParallelBit(bulbs[i]->Address() & 0x04, one);
_prep[15] += one;
_prep[17] += GetParallelBit(bulbs[i]->Address() & 0x02, one);
_prep[18] += one;
_prep[20] += GetParallelBit(bulbs[i]->Address() & 0x01, one);
_prep[21] += one;
// Brightness
_prep[23] += GetParallelBit(bulbs[i]->Brightness() & 0x80, one);
_prep[24] += one;
_prep[26] += GetParallelBit(bulbs[i]->Brightness() & 0x40, one);
_prep[27] += one;
_prep[29] += GetParallelBit(bulbs[i]->Brightness() & 0x20, one);
_prep[30] += one;
_prep[32] += GetParallelBit(bulbs[i]->Brightness() & 0x10, one);
_prep[33] += one;
_prep[35] += GetParallelBit(bulbs[i]->Brightness() & 0x08, one);
_prep[36] += one;
_prep[38] += GetParallelBit(bulbs[i]->Brightness() & 0x04, one);
_prep[39] += one;
_prep[41] += GetParallelBit(bulbs[i]->Brightness() & 0x02, one);
_prep[42] += one;
_prep[44] += GetParallelBit(bulbs[i]->Brightness() & 0x01, one);
_prep[45] += one;
// Blue
_prep[47] += GetParallelBit(bulbs[i]->Colour() & 0x0008, one);
_prep[48] += one;
_prep[50] += GetParallelBit(bulbs[i]->Colour() & 0x0004, one);
_prep[51] += one;
_prep[53] += GetParallelBit(bulbs[i]->Colour() & 0x0002, one);
_prep[54] += one;
_prep[56] += GetParallelBit(bulbs[i]->Colour() & 0x0001, one);
_prep[57] += one;
// Green
_prep[59] += GetParallelBit(bulbs[i]->Colour() & 0x0080, one);
_prep[60] += one;
_prep[62] += GetParallelBit(bulbs[i]->Colour() & 0x0040, one);
_prep[63] += one;
_prep[65] += GetParallelBit(bulbs[i]->Colour() & 0x0020, one);
_prep[66] += one;
_prep[68] += GetParallelBit(bulbs[i]->Colour() & 0x0010, one);
_prep[69] += one;
// Red
_prep[71] += GetParallelBit(bulbs[i]->Colour() & 0x0800, one);
_prep[72] += one;
_prep[74] += GetParallelBit(bulbs[i]->Colour() & 0x0400, one);
_prep[75] += one;
_prep[77] += GetParallelBit(bulbs[i]->Colour() & 0x0200, one);
_prep[78] += one;
_prep[80] += GetParallelBit(bulbs[i]->Colour() & 0x0100, one);
_prep[81] += one;
}
}
}
// populates the _prep buffer with the command string for 1 bulb
void Bulb::PopulateOneBulbBits()
{
// clear the buffer
memset(_prep, 0x00, 82);
// if the bulb hasnt changed then dont send anything!
if (!IsChanged())
{
return;
}
#ifdef INCLUDE_MSG_SUPERDETAIL
#ifdef MINMEMORY
_platform->WriteMessage(MSG_SUPERDETAIL,"6.4\n");
#else
_platform->WriteMessage(MSG_SUPERDETAIL, "String %d Sending bulb %d brightness %d red %d green %d blue %d.\n", (short)_string, (short)_address, (short)_brightness, ((Colour() & 0xF00) >> 8), ((Colour() & 0xF0)) >> 4, Colour() & 0x0F);
#endif
#endif
// Start Bit
_prep[3] = 1;
// Address
_prep[5] = GetBit(_address & 0x20);
_prep[6] = 1;
_prep[8] = GetBit(_address & 0x10);
_prep[9] = 1;
_prep[11] = GetBit(_address & 0x08);
_prep[12] = 1;
_prep[14] = GetBit(_address & 0x04);
_prep[15] = 1;
_prep[17] = GetBit(_address & 0x02);
_prep[18] = 1;
_prep[20] = GetBit(_address & 0x01);
_prep[21] = 1;
// Brightness
_prep[23] = GetBit(_brightness & 0x80);
_prep[24] = 1;
_prep[26] = GetBit(_brightness & 0x40);
_prep[27] = 1;
_prep[29] = GetBit(_brightness & 0x20);
_prep[30] = 1;
_prep[32] = GetBit(_brightness & 0x10);
_prep[33] = 1;
_prep[35] = GetBit(_brightness & 0x08);
_prep[36] = 1;
_prep[38] = GetBit(_brightness & 0x04);
_prep[39] = 1;
_prep[41] = GetBit(_brightness & 0x02);
_prep[42] = 1;
_prep[44] = GetBit(_brightness & 0x01);
_prep[45] = 1;
// Blue
_prep[47] = GetBit(_colour & 0x0008);
_prep[48] = 1;
_prep[50] = GetBit(_colour & 0x0004);
_prep[51] = 1;
_prep[53] = GetBit(_colour & 0x0002);
_prep[54] = 1;
_prep[56] = GetBit(_colour & 0x0001);
_prep[57] = 1;
// Green
_prep[59] = GetBit(_colour & 0x0080);
_prep[60] = 1;
_prep[62] = GetBit(_colour & 0x0040);
_prep[63] = 1;
_prep[65] = GetBit(_colour & 0x0020);
_prep[66] = 1;
_prep[68] = GetBit(_colour & 0x0010);
_prep[69] = 1;
// Red
_prep[71] = GetBit(_colour & 0x0800);
_prep[72] = 1;
_prep[74] = GetBit(_colour & 0x0400);
_prep[75] = 1;
_prep[77] = GetBit(_colour & 0x0200);
_prep[78] = 1;
_prep[80] = GetBit(_colour & 0x0100);
_prep[81] = 1;
}
// Display bulbs on multiple strings in parallel
void Bulb::DisplayParallel(Bulb* bulbs[])
{
bool changed = false; // on of the bulbs has changed
// look at each string
for (byte i = 0; i < MAXSTRINGS; i++)
{
// if the bulb exists && it has changed
if (bulbs[i] != NULL && bulbs[i]->IsChanged())
{
// as long as one of them is changed I need to display it
changed = true;
break;
}
}
// only if something has changed
if (changed)
{
// prepare the buffer for display
Bulb::PopulateMultiBulbBits(bulbs);
// try to clear any work which might interrupt us
#if defined(RASPBERRY) && defined(DOYIELD)
sched_yield();
#endif
// grab the start time
#ifdef RASPBERRY
#ifdef USEDELAYUNTIL
unsigned until = _platform->DelayUntil(0);
#endif
#endif
#ifdef USEARDUINO
noInterrupts();
#endif
// for each component of the message
for(byte i = 0; i < 82; i++)
{
// write the message
_platform->WriteParallel(_prep[i]);
// wait so that the message bit is on the wire for 10us
#if defined(RASPBERRY) && defined(USEDELAYUNTIL)
until += 10; // hardcoded 10us pulse width
_platform->DelayUntil(until);
#else
if (MICROSECONDSWAITPARALLEL != 0)
{
_platform->DelayMicroseconds(MICROSECONDSWAITPARALLEL);
}
#endif
}
#ifndef PC
// leave all the lines low
_platform->WriteParallel((byte)0);
#endif
#ifdef USEARDUINO
interrupts();
#endif
#if defined(SENDTOEMULATOR) || defined(SENDTOEMULATORMSGS)
// for each component of the message
for(byte i = 0; i < 82; i++)
{
// send it to the emulator
_platform->SendToEmulator(_prep[i]);
}
#endif
}
// for each light string
for (byte i = 0; i < MAXSTRINGS; i++)
{
// if we have a bulb
if (bulbs[i] != NULL)
{
// clear its changed state ... I do this even if it is not flagged as changed as it is cheaper than checking and clearing
bulbs[i]->ClearChanged();
}
}
#ifdef INCLUDE_MSG_SUPERDETAIL
#ifdef MINMEMORY
_platform->WriteMessage(MSG_SUPERDETAIL,"6.5\n");
#else
_platform->WriteMessage(MSG_SUPERDETAIL, "Bulbs Sent.\n");
#endif
#endif
}
// Display the bulb
void Bulb::Display()
{
if (IsChanged())
{
// prepare the buffer for display
PopulateOneBulbBits();
// clear the changed bit on this bulb
ClearChanged();
// try to clear any work which might interrupt us
#if defined(RASPBERRY) && defined(DOYIELD)
sched_yield();
#endif
// grab the start time
#if defined(RASPBERRY) && defined(USEDELAYUNTIL)
unsigned until = _platform->DelayUntil(0);
#endif
#ifdef USEARDUINO
noInterrupts();
#endif
// for each component of the message
for(byte i = 0; i < 82; i++)
{
// write the message
_platform->WritePin(_string, _prep[i]);
// wait so that the message bit is on the wire for 10us
#if defined(RASPBERRY) && defined(USEDELAYUNTIL)
until += 10; // hardcoded 10us pulse width
_platform->DelayUntil(until);
#else
if (USWAITSINGLE != 0)
{
_platform->DelayMicroseconds(USWAITSINGLE);
}
#endif
}
#ifndef PC
// leave all the lines low
_platform->WritePin(_string, 0);
#endif
#ifdef USEARDUINO
interrupts();
#endif
#if defined(SENDTOEMULATOR) || defined(SENDTOEMULATORMSGS)
// for each component of the message
for(byte i = 0; i < 82; i++)
{
// send to emulator
_platform->SendToEmulator(_string, _prep[i]);
}
#endif
#ifdef INCLUDE_MSG_SUPERDETAIL
#ifdef MINMEMORY
_platform->WriteMessage(MSG_SUPERDETAIL,"6.6\n");
#else
_platform->WriteMessage(MSG_SUPERDETAIL, "Bulb Sent.\n");
#endif
#endif
}
}