Lab_interaccio/2014/bhoreal_slim_midi/Bhoreal.cpp
2025-02-25 21:29:42 +01:00

779 lines
23 KiB
C++

#include "bhoreal.h"
#include "Adafruit_NeoPixel.h"
byte tempR;
byte tempC;
byte lastread;
byte command = 0;
boolean ready = true;
boolean refresh_ok = false;
uint16_t IntensityMAX = 255;
// Default draw colour. Each channel can be between 0 and 255.
int red = 0;
int green = 0;
int blue = 0;
// Auxiliary analog output definitions
#define ANALOG0 A5 // SLIDER POT MINI
#define ANALOG1 A1
boolean adc[2] = { // On or off state analog inputs
1, 0};
byte analogval[2]; //The last reported value
byte tempADC; //Temporary storage for comparison purposes
#if MODEL == SLIM
// Pin definitions for the 74HC164 SIPO shift register (drives button rows high)
#define DATAPIN 9 // aka analog pin 2 (what, you didn't know that analog pins 0-5 are also digital pins 14-19? Well, now you do!)
#define CLOCKPIN 8
// Pin definitions for the 74HC165 PISO shift register (reads button column state)
#define INDATAPIN 13
#define INCLOCKPIN 5
#define INLOADPIN 10 // toggling this tell the 165 to read the value into its memory for reading
#define AWAKE 18 // AWAKE WIFLY
#define DTR 11
#define MUX 12
#define BOT 7 // sleep pushbotom
uint16_t MAX = 8;
int NUM_LEDS = 64;
#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
#else
uint16_t MAX = 4;
int NUM_LEDS = 16;
#define PIN 11
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
byte row[4] = { // ROW pins for matrix pushbottons
13, 5, 10, 9};
byte column[4] = { // COL pins for matrix pushbottons
8, 6, 12, 4};
#endif
boolean pressed[8][8] = { // pushbottons states matrix
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1}
};
const byte remapMini[4][4] = // mapping matrix for Mini
{
{ 3, 4, 11, 12 },
{ 2, 5, 10, 13 },
{ 1, 6, 9, 14 },
{ 0, 7, 8, 15 }
};
byte remapSlim[8][8] = { // mapping matrix for Slim
{7,8,23,24, 39,40,55,56},
{6,9,22,25, 38,41,54,57},
{5,10,21,26, 37,42,53,58},
{4,11,20,27, 36,43,52,59},
{3,12,19,28, 35,44,51,60},
{2,13,18,29, 34,45,50,61},
{1,14,17,30, 33,46,49,62},
{0,15,16,31, 32,47,48,63},
};
int levelR[64] = { // Red Led intensity matrix
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
int levelG[64] = { // Green Led intensity matrix
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
int levelB[64] = { // Blue Led intensity matrix
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
//////////////////////////////////////////////////////////////////////
////////////////////// BHOREAL BEGIN //////////////////////
//////////////////////////////////////////////////////////////////////
void Bhoreal::begin(uint32_t BAUD)
{
#if MODEL == SLIM
// 165 Setup
pinMode(INDATAPIN, INPUT);
pinMode(INCLOCKPIN, OUTPUT);
pinMode(INLOADPIN, OUTPUT);
// 164 Setup
pinMode(DATAPIN, OUTPUT);
pinMode(CLOCKPIN, OUTPUT);
pinMode(DTR, OUTPUT);
pinMode(MUX, OUTPUT);
pinMode(AWAKE, OUTPUT);
pinMode(BOT, INPUT); //Sleep Bottom
digitalWrite(AWAKE, LOW);
digitalWrite(MUX, HIGH); //Modo Wifly ON
digitalWrite(DTR, HIGH); //Reset atmega328 OFF
strip.begin(); // Initialization of led matrix
strip.show();
#else
for(byte i = 0; i<4; i++)
{
pinMode(column[i], INPUT);
pinMode(row[i], OUTPUT);
digitalWrite(row[i], LOW);
}
// Start the serial port
#if SERIAL_ENABLE
Serial.begin(BAUD);
#endif
strip.begin(); // Initialization of led matrix
strip.show();
// Setup the timer interrupt
PORTE |= B01000000;
DDRE |= B01000000;
timer1Initialize();
//timer3Initialize(); // Disable Serial interrupt!
#endif
}
////////////////////////////////////////////////////////////////
////////////////////// STARTUP //////////////////////
////////////////////////////////////////////////////////////////
// Run this animation once at startup. Currently unfinished.
void Bhoreal::startup(){
for(int x = 0; x < NUM_LEDS; ++x){
#if MODEL == SLIM
uint32_t c = hue2rgb(x*2); // 128 HUE steps / 64 leds, 2 steps x led
uint8_t
r = (uint8_t)(c >> 16),
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
levelG[remapSlim[x>>3][x%8]] = g;
levelB[remapSlim[x>>3][x%8]] = b;
#else
uint32_t c = hue2rgb(x*8); // 128 HUE steps / 16 leds, 8 steps x led
uint8_t
r = (uint8_t)(c >> 16),
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
levelR[remapMini[x>>2][x%4]] = r;
levelG[remapMini[x>>2][x%4]] = g;
levelB[remapMini[x>>2][x%4]] = b;
#endif
}
for(int x = 0; x < NUM_LEDS; ++x) strip.setPixelColor(x, levelR[x], levelG[x], levelB[x]);
// for(int x = 0; x < NUM_LEDS; ++x) strip.setPixelColor(x, 255, 255, 255);
strip.show();
}
//////////////////////////////////////////////////////////////////////
////////////////////// SERIAL PRESS & RELEASE //////////////////////
//////////////////////////////////////////////////////////////////////
void Bhoreal::on_press(byte r, byte c){
#if SERIAL_DATA
Serial.print(1);
Serial.print(" ");
Serial.println( (r << 4) | c, HEX);
#endif
#if MODEL == SLIM
MIDIEvent e1 = { 0x09, 0x90, ((r << 3) | c) , 64 };
#else
MIDIEvent e1 = { 0x09, 0x90, ((r << 2) | c) , 64 };
#endif
MIDIUSB.write(e1);
}
void Bhoreal::on_release(byte r, byte c){
#if SERIAL_DATA
Serial.print(0);
Serial.print(" ");
Serial.println( (r << 4) | c, HEX);
#endif
#if MODEL == SLIM
MIDIEvent e1 = { 0x09, 0x90, ((r << 3) | c) , 0 };
#else
MIDIEvent e1 = { 0x09, 0x90, ((r << 2) | c) , 0 };
#endif
MIDIUSB.write(e1);
}
///////////////////////////////////////////////////////////////
////////////////////// CHECK BUTTONS //////////////////////
///////////////////////////////////////////////////////////////
void Bhoreal::checkButtons(){
#if MODEL == SLIM
if (digitalRead(BOT))
{
Serial.println("OFF");
for(int x = 0; x < NUM_LEDS; ++x){
levelR[x] = 0;
levelG[x] = 0;
levelB[x] = 0;
}
for(int x = 0; x < NUM_LEDS; ++x) strip.setPixelColor(x, levelR[x], levelG[x], levelB[x]);
strip.show();
}
digitalWrite(CLOCKPIN,LOW);
digitalWrite(DATAPIN, HIGH);
for(byte c = 0; c < MAX; c++){
digitalWrite(CLOCKPIN, HIGH);
digitalWrite(INLOADPIN, LOW); // read into register
digitalWrite(INLOADPIN, HIGH); // done reading into register, ready for us to read
for(int r= MAX/2; r < MAX; r++){ // read each of the 165's 8 inputs (or its snapshot of it rather)
// tell the 165 to send the first inputs pin state
digitalWrite(INCLOCKPIN, LOW);
// read the current output
if(pressed[c][r] != digitalRead(INDATAPIN)){ // read the state
pressed[c][r] = digitalRead(INDATAPIN);
if(!pressed[c][r]){
on_press(c, r);
}
else {
on_release(c, r);
}
}
// tell the 165 we are done reading the state, the next inclockpin=0 will output the next input value
digitalWrite(INCLOCKPIN, 1);
}
for(int r= 0; r < MAX/2; r++){ // read each of the 165's 8 inputs (or its snapshot of it rather)
// tell the 165 to send the first inputs pin state
digitalWrite(INCLOCKPIN, LOW);
// read the current output
if(pressed[c][r] != digitalRead(INDATAPIN)){ // read the state
pressed[c][r] = digitalRead(INDATAPIN);
if(!pressed[c][r]){
on_press(c, r);
}
else {
on_release(c, r);
}
}
// tell the 165 we are done reading the state, the next inclockpin=0 will output the next input value
digitalWrite(INCLOCKPIN, 1);
}
digitalWrite(CLOCKPIN, LOW);
digitalWrite(DATAPIN, LOW);
}
#else
for(byte c = 0; c < MAX; c++)
{
digitalWrite(row[c],HIGH);
for(int r= MAX - 1; r >= 0; r--)
{
if(pressed[c][r] != digitalRead(column[r]))
{ // read the state
delay(1); // to prevent bounces!!!
pressed[c][r] = digitalRead(column[r]);
if(pressed[c][r]) on_press(c, r);
else on_release(c, r);
}
}
digitalWrite(row[c],LOW);
}
#endif
}
////////////////////////////////////////////////////////////////
////////////////////// REFRESH LED //////////////////////
////////////////////////////////////////////////////////////////
unsigned long time = 0;
void Bhoreal::refresh(){
if (refresh_ok)
{
strip.show();
refresh_ok=false;
}
// if ((millis() - time)>=100)
// {
// strip.show();
// time = millis();
// }
}
////////////////////////////////////////////////////////////////
////////////////////// REFRESH MIDI & LED /////////////////////
////////////////////////////////////////////////////////////////
void Bhoreal::midiRefresh(){
while(MIDIUSB.available() > 0)
{
MIDIEvent e;
e = MIDIUSB.read();
#if SERIAL_ENABLE
if(MIDI_DEBUG)
{
if(e.type != 0x0F) // timestamp 1 BYTE
{
Serial.print("Midi Packet: ");
Serial.print(e.type);
Serial.print("\t");
Serial.print(e.m1);
Serial.print("\t");
Serial.print(e.m2);
Serial.print("\t");
Serial.println(e.m3);
}
}
#endif
#if MODEL == SLIM
if((e.type == 0x09) && (e.m3)) // NoteON midi message with vel > 0
{
uint32_t c = hue2rgb(e.m3); // velocity is used to HUE color selection and HUE is converted to RGB uint32
uint8_t
r = (uint8_t)(c >> 16),
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
strip.setPixelColor(remapSlim[e.m2>>3][e.m2%8], r, g, b);
strip.show();
}
else if( (e.type == 0x08) || ((e.type == 0x09) && !e.m3) ) // NoteOFF midi message
{
strip.setPixelColor(remapSlim[e.m2>>3][e.m2%8], 0, 0, 0);
strip.show();
}
#else
if((e.type == 0x09) && (e.m3)) // NoteON midi message with vel > 0
{
uint32_t c = hue2rgb(e.m3);
uint8_t
r = (uint8_t)(c >> 16),
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
strip.setPixelColor(remapMini[e.m2>>2][e.m2%4], r, g, b);
strip.show();
}
else if( (e.type == 0x08) || ((e.type == 0x09) && !e.m3) ) // NoteOFF midi message
{
strip.setPixelColor(remapMini[e.m2>>2][e.m2%4], 0, 0, 0);
strip.show();
}
#endif
MIDIUSB.flush(); // delete it???
}
}
////////////////////////////////////////////////////////////////
////////////////////// CHECK ADC INPUTS //////////////////////
////////////////////////////////////////////////////////////////
void Bhoreal::checkADC(){
// For all of the ADC's which are activated, check if the analog value has changed,
// and send a message if it has.
if(adc[0]){
tempADC = (analogRead(ANALOG0) >> 2);
if(abs((int)analogval[0] - (int)tempADC) > 2 ){
//if(analogval[0] != (analogRead(ANALOG0) >> 2)){
//analogval[0] = (analogRead(ANALOG0) >> 2);
analogval[0] = tempADC;
#if SERIAL_DATA
Serial.write(14 << 4);
Serial.write(analogval[0]);
#endif
// Send the control change message for the slider potentiometer,
// by defect we use CC64 controller
MIDIEvent e1 = {0x0B, 0xB0, 64, analogval[0]>>1};
MIDIUSB.write(e1);
}
}
if(adc[1]){
if(analogval[1] != (analogRead(ANALOG1) >> 2)){
analogval[1] = (analogRead(ANALOG1) >> 2);
#if SERIAL_DATA
Serial.write(14 << 4 | 1);
Serial.write(analogval[1]);
#endif
}
}
}
////////////////////////////////////////////////////////////////
////////////////////// SERIAL INTERRUPT //////////////////////
////////////////////////////////////////////////////////////////
//boolean flag = true;
/*TIMER*/
ISR(TIMER3_OVF_vect)
{
cli();
do{ // This do ensures that the data is always parsed at least once per cycle
#if SERIAL_DATA
if(Serial.available()){
if(ready){ // If the last command has finished executing, read in the next command and reset the command flag
command = Serial.read();
ready = false;
}
switch (command >> 4) { //Execute the appropriate command, but only if we have received enough bytes to complete it. We might one day add "partial completion" for long command strings.
case 1: // set colour
if( Serial.available() > 2 ) {
red = Serial.read();
green = Serial.read();
blue = Serial.read();
ready=true;
}
break;
case 2: // led_on
if( Serial.available() ) {
lastread = Serial.read();
tempR = lastread >> 4;
tempC = lastread & B1111;
if ((tempR < MAX)&&(tempC < MAX))
{
levelR[remapMini[tempC][tempR]] = red;
levelG[remapMini[tempC][tempR]] = green;
levelB[remapMini[tempC][tempR]] = blue;
strip.setPixelColor(remapMini[tempC][tempR], red, green, blue);
refresh_ok=true;
}
ready = true;
}
break;
case 3: // led_off
if( Serial.available() ) {
lastread = Serial.read();
tempR = lastread >> 4;
tempC = lastread & B1111;
if ((tempR < MAX)&&(tempC < MAX))
{
levelR[remapMini[tempC][tempR]] = 0;
levelG[remapMini[tempC][tempR]] = 0;
levelB[remapMini[tempC][tempR]] = 0;
strip.setPixelColor(remapMini[tempC][tempR], 0, 0, 0);
refresh_ok=true;
}
ready = true;
}
break;
case 4: // led_row1
if( Serial.available() ) {
tempR = command & B1111;
lastread = Serial.read();
if (tempR < MAX)
{
for(tempC = 0; tempC < MAX; ++tempC){
if(lastread & (1 << tempC) ){
levelR[remapMini[tempR][tempC]] = red;
levelG[remapMini[tempR][tempC]] = green;
levelB[remapMini[tempR][tempC]] = blue;
strip.setPixelColor(remapMini[tempR][tempC], red, green, blue);
}
else {
levelR[remapMini[tempR][tempC]] = 0;
levelG[remapMini[tempR][tempC]] = 0;
levelB[remapMini[tempR][tempC]] = 0;
strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0);
}
}
}
refresh_ok=true;
ready = true;
}
break;
case 5: // led_col1
if( Serial.available() ) {
tempC = command & B1111;
lastread = Serial.read();
if (tempC < MAX)
{
for(tempR = 0; tempR < MAX; ++tempR){
if(lastread & (1 << tempR) ){
levelR[remapMini[tempR][tempC]] = red;
levelG[remapMini[tempR][tempC]] = green;
levelB[remapMini[tempR][tempC]] = blue;
strip.setPixelColor(remapMini[tempR][tempC], red, green, blue);
}
else {
levelR[remapMini[tempR][tempC]] = 0;
levelG[remapMini[tempR][tempC]] = 0;
levelB[remapMini[tempR][tempC]] = 0;
strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0);
}
}
}
refresh_ok=true;
ready = true;
}
break;
case 8: //frame
if( Serial.available() > 7 ) {
for(tempR=0; tempR < MAX; ++tempR){
lastread = Serial.read();
for(tempC = 0; tempC < MAX; ++tempC){
if(lastread & (1 << tempC) ){
levelR[remapMini[tempR][tempC]] = red;
levelG[remapMini[tempR][tempC]] = green;
levelB[remapMini[tempR][tempC]] = blue;
strip.setPixelColor(remapMini[tempR][tempC], red, green, blue);
}
else {
levelR[remapMini[tempR][tempC]] = 0;
levelG[remapMini[tempR][tempC]] = 0;
levelB[remapMini[tempR][tempC]] = 0;
strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0);
}
}
}
refresh_ok=true;
ready = true;
}
break;
case 9: // FULL COLOR OR CLEAR
if(command & 1){
byte TEMPMAX = MAX*MAX;
for(int x = 0; x< TEMPMAX;++x){
levelR[x] = red;
levelG[x] = green;
levelB[x] = blue;
strip.setPixelColor(x, red, green, blue);
}
}
else{
byte TEMPMAX = MAX*MAX;
for(int x = 0; x< TEMPMAX;++x){
levelR[x] = 0;
levelG[x] = 0;
levelB[x] = 0;
strip.setPixelColor(x, 0, 0, 0);
}
}
refresh_ok=true;
ready = true;
break;
case 12:
switch(command & 15){
case 0:
adc[0] = true;
analogval[0] = (analogRead(ANALOG0) >> 2);
Serial.write(14 << 4);
Serial.write(analogval[0]);
break;
case 1:
adc[1] = true;
analogval[1] = (analogRead(ANALOG1) >> 2);
Serial.write(14 << 4 | 1);
Serial.write(analogval[1]);
break;
default:
break;
}
ready = true;
break;
case 13:
adc[command & 15] = false;
ready = true;
break;
default:
break;
}
}
#endif
}
// If the serial buffer is getting too close to full, keep executing the parsing until it falls below a given level
// This might cause flicker, or even dropped messages, but it should prevent a crash.
while (Serial.available() > TOOFULL);
strip.show();
sei();
}
///////////////////////////////////////////////////////////////
////////////////////// TIMERS SETTINGS //////////////////////
///////////////////////////////////////////////////////////////
#define RESOLUTION 65535 // Timer1 is 16 bit
unsigned int pwmPeriod;
unsigned char clockSelectBits;
char oldSREG; // To hold Status
void setPeriodTimer1(long microseconds) // AR modified for atomic access
{
long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
if(cycles < RESOLUTION) clockSelectBits = _BV(CS10); // no prescale, full xtal
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); // prescale by /8
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); // prescale by /256
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024
else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum
oldSREG = SREG;
cli(); // Disable interrupts for 16 bit register access
ICR1 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode
SREG = oldSREG;
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
TCCR1B |= clockSelectBits; // reset clock select register, and starts the clock
}
void Bhoreal::timer1Initialize()
{
TCCR1A = 0; // clear control register A
TCCR1B = _BV(WGM13); // set mode 8: phase and frequency correct pwm, stop the timer
setPeriodTimer1(5); // Time interruption in ms
TIMSK1 = _BV(TOIE1);
}
boolean flag = false;
ISR(TIMER1_OVF_vect)
{
if (flag) { PORTE |= B01000000; flag=0;}
else if (!flag) { PORTE &= B10111111; flag=1;}
}
void setPeriodTimer3(long microseconds) // AR modified for atomic access
{
long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
if(cycles < RESOLUTION) clockSelectBits = _BV(CS30); // no prescale, full xtal
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS31); // prescale by /8
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS31) | _BV(CS30); // prescale by /64
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS32); // prescale by /256
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS32) | _BV(CS30); // prescale by /1024
else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS32) | _BV(CS30); // request was out of bounds, set as maximum
oldSREG = SREG;
cli(); // Disable interrupts for 16 bit register access
ICR3 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode
SREG = oldSREG;
TCCR3B &= ~(_BV(CS30) | _BV(CS31) | _BV(CS32));
TCCR3B |= clockSelectBits; // reset clock select register, and starts the clock
}
void Bhoreal::timer3Initialize()
{
TCCR3A = 0; // clear control register A
TCCR3B = _BV(WGM33); // set mode 8: phase and frequency correct pwm, stop the timer
setPeriodTimer3(10000);
TIMSK3 = _BV(TOIE3);
// TCCR3A = 0;
// TCCR3B = 0<<CS32 | 0<<CS31 | 1<<CS30;
// //Timer1 Overflow Interrupt Enable
// TIMSK3 = 1<<TOIE3;
}
///////////////////////////////////////////////////////////////
////////////////////// HUE -> RGB //////////////////////
///////////////////////////////////////////////////////////////
uint32_t Bhoreal::hue2rgb(uint16_t hueValue)
{
uint8_t r;
uint8_t g;
uint8_t b;
hueValue<<= 3; // 128 midi steps -> 1024 hue steps
if (hueValue < 341) { // Lowest third of the potentiometer's range (0-340)
hueValue = (hueValue * 3) / 4; // Normalize to 0-255
r = 255 - hueValue; // Red from full to off
g = hueValue; // Green from off to full
b = 1; // Blue off
}
else if (hueValue < 682) { // Middle third of potentiometer's range (341-681)
hueValue = ( (hueValue-341) * 3) / 4; // Normalize to 0-255
r = 1; // Red off
g = 255 - hueValue; // Green from full to off
b = hueValue; // Blue from off to full
}
else { // Upper third of potentiometer"s range (682-1023)
hueValue = ( (hueValue-683) * 3) / 4; // Normalize to 0-255
r = hueValue; // Red from off to full
g = 1; // Green off
b = 255 - hueValue; // Blue from full to off
}
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}