Components and supplies
Kohree LED Fairy String Lights
Individually Addressable LED Strip
Net Lights
Mini String Lights
5x7cm Double Sided PCB Board
Arduino Mega 2560
Arduino Nano R3
NXP Semiconductors PCA9955 16-channel Constant Current LED Driver
DSD TECH USB to TTL Serial Adapter
LED Strip Lights
FTDI FT232RL
Project description
Code
Single_Element_Controller
c_cpp
This code is meant to run on an Arduino Nano. It will access all 16 pulse width modulated outputs on the fairy board and is capable of using 14 of the 16 available digital I/O of the Arduino. When interfacing to Vixen, the first 16 channels correspond to the PWM outputs. The next 16 correspond to the digital I/O.
1// 16 Channel Single Element Driver with 16 relay outputs 2// 3// (c) Paul Zavracky, January, 2018, updated August 2020 4 5#define VERSION "1.1" 6#include <Wire.h> // I2C library 7 8#define OE 2 // pca9955 location of Output Enable pin, active low 9#define RESET 3 // pca9955 location of reset pin, active low 10#define MAX_CHANNELS 32 // 16 pca9955 Channels and 16 digital channels (relays) 11#define BAUD_RATE 115200 12 13// Define relay output pins 14#define DIGITAL_OUT_0 5 15#define DIGITAL_OUT_1 6 16#define DIGITAL_OUT_2 7 17#define DIGITAL_OUT_3 8 18 19#define DIGITAL_OUT_4 9 20#define DIGITAL_OUT_5 10 21#define DIGITAL_OUT_6 11 22#define DIGITAL_OUT_7 12 23 24#define DIGITAL_OUT_8 A0 25#define DIGITAL_OUT_9 A1 26#define DIGITAL_OUT_10 A2 27#define DIGITAL_OUT_11 A3 28// A4 and A5 are used for i2c interface 29#define DIGITAL_OUT_12 A6 // not currently accessable on board 30#define DIGITAL_OUT_13 A7 // not currently accessable on board 31#define DIGITAL_OUT_14 4 32#define DIGITAL_OUT_15 13 33 34// Display Driver Initialization from Adafruit 35#include <Adafruit_GFX.h> 36#include <Adafruit_SSD1306.h> 37 38#define SCREEN_WIDTH 128 // OLED display width, in pixels 39#define SCREEN_HEIGHT 64 // OLED display height, in pixels 40 41// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 42#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 43Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 44 45// Globals 46 47int ch; 48int state; 49int chVal[MAX_CHANNELS] = {0}; 50byte error; 51 // 0:success 52 // 1:data too long to fit in transmit buffer 53 // 2:received NACK on transmit of address 54 // 3:received NACK on transmit of data 55 // 4:other i2c error 56 // 5:no serial data recieved 57 58const char error_0[] PROGMEM = "i2c Success"; // "String 0" etc are strings to store - change to suit. 59const char error_1[] PROGMEM = "i2c too long"; 60const char error_2[] PROGMEM = "i2c addr NACK"; 61const char error_3[] PROGMEM = "i2c data NACK"; 62const char error_4[] PROGMEM = "other i2c error"; 63const char error_5[] PROGMEM = "no serial data"; 64 65const char *const error_table[] PROGMEM = {error_0, error_1, error_2, error_3, error_4, error_5}; // create a table of strings in PROGMEM 66 67const char messages_0[] PROGMEM = "Welcome to the Looneypoons 16 channel Single Element driver, V"; // this string is 29 character long 68const char messages_1[] PROGMEM = "SSD1306 allocation failed"; 69const char messages_2[] PROGMEM = "Looneypoon"; 70const char messages_3[] PROGMEM = "i2c check"; 71const char messages_4[] PROGMEM = "Scanning..."; 72const char messages_5[] PROGMEM = "No Serial Data"; 73const char messages_6[] PROGMEM = "Sys Chk complete"; 74const char messages_7[] PROGMEM = "serial bus OK"; 75 76const char *const messages_table[] PROGMEM = {messages_0, messages_1, messages_2, messages_3, messages_4, messages_5, messages_6, messages_7}; 77 78char buffer[30]; // length of maximum message at minimum 79 80bool i2cErrorFlag = false; 81byte i2cError[MAX_CHANNELS] = {0}; // initial to zero so that change detection will work 82 83bool serialErrorFlag = false; // set error flags to true to force system to clear error from display 84bool oldSerialErrorFlag = true; // this forces the display of the i2c status and the serial status 85 86byte i2cAddress = 0x5A; 87unsigned int loopCounter = 0; // keeps track of how many times through the main loop collecting no serial data 88 89byte digitalOut[] = {DIGITAL_OUT_0, DIGITAL_OUT_1, DIGITAL_OUT_2, DIGITAL_OUT_3, DIGITAL_OUT_4, DIGITAL_OUT_5, DIGITAL_OUT_6, DIGITAL_OUT_7, DIGITAL_OUT_8, 90 DIGITAL_OUT_9, DIGITAL_OUT_10, DIGITAL_OUT_11, DIGITAL_OUT_12, DIGITAL_OUT_13, DIGITAL_OUT_14, DIGITAL_OUT_15}; 91 92enum states 93{ 94 IDLE, 95 DELIM, 96 READ, 97 DISP 98}; 99 100void setup() { 101 int nDevices; 102 byte address; 103 104 Serial.begin(BAUD_RATE); 105 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[0]))); // Necessary casts and dereferencing, just copy. 106 Serial.print(buffer); Serial.println(VERSION); 107 108 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 109 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 110 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[1]))); // SSD1306 allocation failed 111 Serial.println(buffer); 112 for(;;); // Don't proceed, loop forever 113 } 114 115 // Set up PCA9955 Chip Reset and Output Enable 116 pinMode(RESET, INPUT); // set pin to input 117 digitalWrite(RESET, HIGH); // turn on pullup resistors 118 pinMode(RESET, OUTPUT); 119 digitalWrite(RESET, HIGH); // reset for the PCA9955 is active low 120 delay(1000); // wait 1 second 121 digitalWrite(RESET, LOW); // reset for the PCA9955 is active low 122 delay(100); // send 100 ms reset pulse 123 digitalWrite(RESET, HIGH); // reset for the PCA9955 is active low 124 delay(100); // provide time for 9955 to reset 125 126 pinMode(OE, INPUT); // set pin to input 127 digitalWrite(OE, HIGH); // turn on pullup resistors 128 pinMode(OE, OUTPUT); 129 digitalWrite(OE, HIGH); // output enable for the PCA9955 is active low 130 delay(1000); // wait 1 second 131 digitalWrite(OE, LOW); // output enable for the PCA9955 is active low 132 delay(100); // provide time for 9955 to settle 133 134 // Set Initial Display 135 display.clearDisplay(); 136 display.setTextSize(2); // Normal 1:1 pixel scale 137 display.setTextColor(SSD1306_WHITE); // Draw white text 138 display.setCursor(0,0); // Start at top-left corner 139 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[2]))); // SSD1306 allocation failed 140 display.println(buffer); //"Looneypoon" 141 display.display(); 142 delay(1000); 143 144 display.setTextSize(1); 145 display.print(F("V"));display.println(VERSION); 146 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[3]))); // SSD1306 allocation failed 147 display.println(buffer); //"i2c check" 148 display.println(); 149 display.display(); 150 delay(1000); 151 152 // Start by scanning i2c slave addresses 153 Wire.begin(); // I2C library 154 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[4]))); // SSD1306 allocation failed 155 display.println(buffer); //"Scanning..." 156 display.display(); 157 158 nDevices = 0; 159 for(address = 1; address < 127; address++ ) 160 { 161 // The i2c_scanner uses the return value of 162 // the Write.endTransmisstion to see if 163 // a device did acknowledge to the address. 164 Wire.beginTransmission(address); 165 error = Wire.endTransmission(); 166 // Serial.print("error = "); Serial.println(error); 167 if (error == 0) 168 { 169 display.print(F("0x")); 170 if (address<16) 171 display.print(F("0")); 172 display.print(address,HEX); 173 display.print(", "); 174 nDevices++; 175 } 176 else if (error==4) 177 { 178 display.print(F("Unknown error at ")); 179 if (address<16) 180 display.print(F("0")); 181 display.println(address,HEX); 182 } 183 } 184 display.display(); 185 delay(1000); 186 if (nDevices == 0) 187 display.println(F("No I2C devices found\ 188")); 189 else 190 display.println("done\ 191"); 192 display.display(); 193 delay(3000); // delay so one can read screen 194 195 //Serial.println("The above lists the device/board addresses found.\ 196\ Address 70h and 76h are common to all PCA9955 devices."); 197 198 // Set the output current level 199 display.clearDisplay(); 200 display.setCursor(0,0); // Start at top-left corner 201 display.setTextSize(2); 202 display.println(F("Looneypoon")); 203 display.display(); 204 display.setTextSize(1); 205 206 Wire.beginTransmission(byte(i2cAddress)); // transmit to PCA9955 device specific address 207 Wire.write(byte(0x45)); // Selects the IREFALL register (pg 32 of PCA9955B data sheet) 208 Wire.write(255); // sends current value byte 209 error = Wire.endTransmission(); // stop transmitting 210 display.print(" error = "); display.println(error); 211 212 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[6]))); 213 display.println(buffer); // "Sys Chk complete" 214 display.println(""); // space 215 display.display(); 216 217 Serial.println(buffer); // "Sys Chk complete" 218 delay(3000); 219 220 for (int i = 0; i<12; i++) { // set up output ports 221 pinMode(digitalOut[i], OUTPUT); 222 digitalWrite(digitalOut[i], HIGH); // logic low activates relay 223 } 224 state = IDLE; 225 ch = 0; 226} 227 228void loop() { 229 if (Serial.available()) 230 { 231 switch (state) 232 { 233 case IDLE: 234 ch = 0; 235 if (Serial.read() == '+') 236 { 237 state = DELIM; 238 } 239 else 240 { 241 state = IDLE; 242 } 243 break; 244 245 case DELIM: 246 ch = 0; 247 if (Serial.read() == '>') 248 { 249 state = READ; 250 } 251 else 252 { 253 state = IDLE; 254 } 255 break; 256 257 case READ: 258 chVal[ch++] = Serial.read(); 259 if (ch >= MAX_CHANNELS) 260 { 261 ch = 0; 262 state = DISP; 263 } 264 break; 265 266 case DISP: 267 state = IDLE; 268 //Serial.println("made it to disp"); 269 i2cErrorFlag = false; // initialize 270 for (ch=0; ch<16; ch++) // board has 16 PWM channels 271 { 272 Serial.print(ch);Serial.print(" ");Serial.println(chVal[ch]); 273 Wire.beginTransmission(byte(i2cAddress)); // transmit to PCA9955 device specific address 274 Wire.write(byte(0x08 + ch)); // Selects the PWM6 register (pg 14 of PCA9955B data sheet) 275 Wire.write(chVal[ch]); // sends PWM value byte 276 error = Wire.endTransmission(); // stop transmitting 277 if (i2cError[ch] != error) {i2cErrorFlag = true;} // set error flag only on a change of error condition 278 i2cError[ch] = error; 279 } 280 for (ch=16; ch<MAX_CHANNELS; ch++) // board has 8 digital channels 281 { 282 Serial.print(ch);Serial.print(" ");Serial.println(chVal[ch]); 283 digitalWrite(digitalOut[ch - 16],(chVal[ch]>0) ? LOW : HIGH ); // sends value as boolean (relays are active low!!) 284 } 285 loopCounter = 0; // we got serial data, so can reset the loop counter 286 serialErrorFlag = false; // also reset flag 287 break; 288 } 289 } 290 loopCounter++; 291 if (loopCounter == 0) {serialErrorFlag = true; } // we've gone x times without recieving data 292 if((serialErrorFlag != oldSerialErrorFlag) || i2cErrorFlag) {updateErrorDisplay();} // check for errors 293 oldSerialErrorFlag = serialErrorFlag; 294} 295 296void updateErrorDisplay(){ 297 byte j = 1; 298 299 clearDisplayText(); 300 display.setTextSize(1); 301 // check i2c errors 302 if (i2cErrorFlag) { 303 for (int i = 0; i < MAX_CHANNELS; i++) { 304 if(i2cError[i] != 0) { 305 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 306 display.print("channel ");display.print(ch);display.print(" error = "); display.println(i2cError[i]); 307 j++; 308 } 309 } 310 } 311 else { // if the above did not detect an i2c error then... 312 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 313 display.print("no I2C errors "); 314 j++; 315 } 316 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 317 if (serialErrorFlag) { 318 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[5]))); 319 display.print(buffer); // "No Serial Data" 320 //Serial.println(buffer); 321 } 322 else { 323 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[7]))); 324 display.print(buffer); // "serial bus OK" 325 //Serial.println(buffer); 326 } 327 display.display(); 328} 329 330void clearDisplayText() { 331 display.fillRect(0, 16, 127, 47, BLACK); 332 display.display(); 333} 334
Individually Addressable Controller (approach 2)
c_cpp
This is the second approach to individually addressable strips. It assumes that the strip data lines are tied end to end and therefore, only one Arduino pin is required to drive several elements in the series. In this approach it is imperative that the count of LEDs in each element exactly matches that defined in Vixen.
1// Individually Addressable Controller 2// 3// (c) Paul Zavracky, September, 2020; update Nov 2021 4 5#define VERSION "2.0" 6#include <Wire.h> // I2C library 7#include <Adafruit_NeoPixel.h> 8 9// start of strip definition 10#define BRIGHTNESS 64 11 12// strip definitions 13 14#define BNRpin 4 15#define BNRpixels 252 16 17#define MAX_CHANNELS 756 // This is the total number of leds (counting each color) on the nose 18#define BAUD_RATE0 2000000 // this is the rate at which the serial monitor operates - wants to be higher than below 19#define BAUD_RATE3 115200 // this is the rate for data coming in from Vixen (on port 3) 20 21// Display Driver Initialization from Adafruit 22#include <Adafruit_GFX.h> 23 24// Globals 25 26int ch; 27int state; 28int chVal[MAX_CHANNELS] = {0}; 29char serialInput; 30 31bool serialErrorFlag = false; // set error flags to true to force system to clear error 32bool oldSerialErrorFlag = false; // this forces the display of the serial status 33bool firstData; 34 35enum states 36{ 37 IDLE, 38 DELIM, 39 READ, 40 DISP 41}; 42 43Adafruit_NeoPixel strip(BNRpixels, BNRpin, NEO_GRB + NEO_KHZ800); 44 45unsigned long int pixelSum; // used to keep track of the assignment of Vixen data with pixel count and strip 46unsigned long int color; // must convert chVal()s RGB into long integer 47 48void setup() { 49 Serial.begin(BAUD_RATE0); 50 Serial3.begin(BAUD_RATE3); 51 Serial.println(); 52 Serial.print("Individually Addressable Controller, VERSION "); Serial.println(VERSION); 53 54 strip.setBrightness(BRIGHTNESS); 55 strip.begin(); 56 showStrip(); // Turn all lights on white 57 Serial.println("made it to show strip"); 58 delay(1000); 59 clearStrip(); // Initialize all pixels to 'off' 60 Serial.println("made it to clear strip"); 61 delay(1000); 62 63 Serial.println("Sys Chk complete" ); 64 delay(3000); 65 66 state = IDLE; 67 ch = 0; 68 firstData = true; // true for test of first serial data response 69} 70 71void loop() { 72 73 if (Serial3.available()) 74 { 75 if (firstData) { Serial.println("got serial data"); firstData = false;} 76 switch (state) 77 { 78 case IDLE: 79 ch = 0; 80 if (Serial3.read() == '+') 81 { 82 Serial.println("+"); 83 state = DELIM; 84 } 85 else 86 { 87 state = IDLE; 88 } 89 break; 90 91 case DELIM: 92 ch = 0; 93 if (Serial3.read() == '>') 94 { 95 Serial.println(">"); 96 state = READ; 97 } 98 else 99 { 100 state = IDLE; 101 } 102 break; 103 104 case READ: 105 chVal[ch++] = Serial3.read(); 106 if (ch > MAX_CHANNELS) 107 { 108 ch = 0; 109 state = DISP; 110 } 111 break; 112 113 case DISP: 114 state = IDLE; 115 pixelSum = 0; 116 for(long int i = 0; i < BNRpixels; i++) { // for all pixels within a strip 117 strip.setPixelColor(i, chVal[3*i], chVal[3*i+1], chVal[3*i+2]); // set pixel to Vixen color 118 } 119 strip.show(); 120 break; 121 } 122 } 123} 124 125void updateErrorDisplay(){ 126 127 if (serialErrorFlag) { 128 Serial.println("No Serial Data"); 129 } 130 else { 131 Serial.println("serial bus OK"); 132 } 133} 134 135void showStrip() { 136 for( long int i = 0; i < BNRpixels; i++){ 137 strip.setPixelColor(i, 0x111111); 138 } 139 strip.show(); 140} 141 142void clearStrip() { 143 for( long int i = 0; i < BNRpixels; i++){ 144 strip.setPixelColor(i, 0x000000); 145 } 146 strip.show(); 147} 148 149unsigned long int pzRGB(byte r, byte g, byte b) { 150 return (r*65536 + g*256 + b); 151} 152
Individually Addressable Controller (approach 1)
c_cpp
This software is meant to run on an Arduino Mega. It can be used in a situation where there are multiple individually addressable strips to control separately. This is one of two approaches that can be employed. In this approach, the software creates strips that match the hardware configuration and then manages them. The strip definitions in this code must match the strip definitions used by Vixen.
1// Individually Addressable Controller 2// 3// (c) Paul Zavracky, September, 2020; update Nov 2021 4 5#define VERSION "2.0" 6#include <Wire.h> // I2C library 7#include <Adafruit_NeoPixel.h> 8 9// start of strip definition 10#define NUM_STRIPS 10 11#define BRIGHTNESS 100 12 13// strip definitions 14#define UNDP 0 // element order in hardware - must be converted to match Vixen (see stripDataOrder) 15#define UNDPpin 3 // Uppoer Nose Diagonal Port (1 O'clock on ten hour clock) 16#define UNDPpixels 67 // Uppoer Nose Diagonal Port 17 18#define DNTP 1 19#define DNTPpin 4 // Diagonal Nose Top Port (2 O'clock) 20#define DNTPpixels 78 // Diagonal Nose Top Port 21 22#define HNP 2 23#define HNPpin 5 // Horizontal Nose Port (3 O'Clock) 24#define HNPpixels 78 // Horizontal Nose Port 25 26#define DNBP 3 27#define DNBPpin 6 // Diagonal Nose Bottom Port (4 O'Clock) 28#define DNBPpixels 78 // Diagonal Nose Bottom Port 29 30#define VNB 4 31#define VNBpin 7 // Vertical Nose Bottom (5 O'Clock) 32#define VNBpixels 17 // Vertical Nose Bottom 33 34#define DNBS 5 35#define UNDSpin 8 // Uppoer Nose Diagonal Port (6 O'Clock) 36#define UNDSpixels 78 // Uppoer Nose Diagonal Port 37 38#define HNS 6 39#define DNTSpin 9 // Diagonal Nose Top Port (7 O'Clock) 40#define DNTSpixels 78 // Diagonal Nose Top Port 41 42#define DNTS 7 43#define HNSpin 10 // Horizontal Nose Port (8 O'Clock) 44#define HNSpixels 78 // Horizontal Nose Port 45 46#define UNDS 8 47#define DNBSpin 11 // Diagonal Nose Bottom Port (9 O'Clock) 48#define DNBSpixels 67 // Diagonal Nose Bottom Port (9) 49 50#define VNT 9 51#define VNTpin 12 // Vertical Nose Top (10 O'Clock) 52#define VNTpixels 60 // Vertical Nose Top 53 54#define MAX_CHANNELS 2046 // 3*(4*DNTPpixels+2*UNDPpixels+VNBpixels+VNTpixels) This is the total number of leds (counting each color) on the nose 55#define BAUD_RATE0 115200 // this is the rate at which the serial monitor operates - wants to be higher than below 56#define BAUD_RATE3 115200 // this is the rate for data coming in from Vixen (on port 3) 57 58// Display Driver Initialization from Adafruit 59#include <Adafruit_GFX.h> 60 61// Globals 62 63int ch; 64int ch1; 65byte stripNum; 66int state; 67int chVal[MAX_CHANNELS] = {0}; 68byte STRIP_PINS[NUM_STRIPS] {UNDPpin, DNTPpin, HNPpin, DNBPpin, VNBpin, UNDSpin, DNTSpin, HNSpin, DNBSpin, VNTpin}; 69int NUM_PIXELS[NUM_STRIPS] {UNDPpixels, DNTPpixels, HNPpixels, DNBPpixels, VNBpixels, UNDSpixels, DNTSpixels, HNSpixels, DNBSpixels, VNTpixels}; 70 71byte stripDataOrder[] = {HNP,HNS,VNT,VNB,DNTS,DNBS,DNTP,DNBP,UNDP,UNDS}; // this translates to the order of elements in Vixen 72 73bool serialErrorFlag = false; // set error flags to true to force system to clear error 74bool oldSerialErrorFlag = false; // this forces the display of the serial status 75 76enum states 77{ 78 IDLE, 79 DELIM, 80 READ, 81 DISP 82}; 83 84Adafruit_NeoPixel strip[] = { 85 Adafruit_NeoPixel(NUM_PIXELS[0], STRIP_PINS[0], NEO_GRB + NEO_KHZ800), 86 Adafruit_NeoPixel(NUM_PIXELS[1], STRIP_PINS[1], NEO_GRB + NEO_KHZ800), 87 Adafruit_NeoPixel(NUM_PIXELS[2], STRIP_PINS[2], NEO_GRB + NEO_KHZ800), 88 Adafruit_NeoPixel(NUM_PIXELS[3], STRIP_PINS[3], NEO_GRB + NEO_KHZ800), 89 Adafruit_NeoPixel(NUM_PIXELS[4], STRIP_PINS[4], NEO_GRB + NEO_KHZ800), 90 Adafruit_NeoPixel(NUM_PIXELS[5], STRIP_PINS[5], NEO_GRB + NEO_KHZ800), 91 Adafruit_NeoPixel(NUM_PIXELS[6], STRIP_PINS[6], NEO_GRB + NEO_KHZ800), 92 Adafruit_NeoPixel(NUM_PIXELS[7], STRIP_PINS[7], NEO_GRB + NEO_KHZ800), 93 Adafruit_NeoPixel(NUM_PIXELS[8], STRIP_PINS[8], NEO_GRB + NEO_KHZ800), 94 Adafruit_NeoPixel(NUM_PIXELS[9], STRIP_PINS[9], NEO_GRB + NEO_KHZ800) 95}; 96 97unsigned long int pixelSum; // used to keep track of the assignment of Vixen data with pixel count and strip 98unsigned long int color; // must convert chVal()s RGB into long integer 99 100void setup() { 101 102 Serial2.begin(BAUD_RATE0); 103 Serial.begin(BAUD_RATE3); 104 Serial2.println(); 105 Serial2.print("Individually Addressable Controller, VERSION "); Serial2.println(VERSION); 106 107 for (int i = 0; i<NUM_STRIPS; i++) { 108 strip[i].setBrightness(BRIGHTNESS); 109 strip[i].begin(); 110 clearStrip(i); // Initialize all pixels to 'off' 111 Serial2.print("strip "); Serial2.println(i); 112 } 113 //delay(1000); 114 115 Serial2.println("Sys Chk complete" ); 116 delay(3000); 117 118 state = IDLE; 119 ch = 0; 120} 121 122void loop() { 123 124 if (Serial.available()) 125 { 126 switch (state) 127 { 128 case IDLE: 129 ch = 0; 130 if (Serial.read() == '+') 131 { 132 state = DELIM; 133 } 134 else 135 { 136 state = IDLE; 137 } 138 break; 139 140 case DELIM: 141 ch = 0; 142 if (Serial.read() == '>') 143 { 144 state = READ; 145 } 146 else 147 { 148 state = IDLE; 149 } 150 break; 151 152 case READ: 153 chVal[ch++] = Serial.read(); 154 if (ch > MAX_CHANNELS) 155 { 156 ch = 0; 157 state = DISP; 158 } 159 break; 160 161 case DISP: 162 state = IDLE; 163 pixelSum = 0; 164 for(int j = 0; j < NUM_STRIPS; j++) { // for all the strips 165 for(long int i = 0; i < NUM_PIXELS[stripDataOrder[j]]; i++) { // for all pixels within a strip 166 strip[stripDataOrder[j]].setPixelColor(i, chVal[3*i+pixelSum], chVal[3*i+1+pixelSum], chVal[3*i+2+pixelSum]); // set pixel to Vixen color 167 /* 168 Serial2.print(3*i+pixelSum);Serial2.print(" ");Serial2.print(chVal[3*i+pixelSum]);Serial2.print(" "); 169 Serial2.print(chVal[3*i+1+pixelSum]);Serial2.print(" "); 170 Serial2.println(chVal[3*i+2+pixelSum]); 171 Serial2.println(); 172 */ 173 } 174 strip[stripDataOrder[j]].show(); 175 pixelSum = pixelSum + 3 * NUM_PIXELS[stripDataOrder[j]]; 176 } 177 //Serial2.println("end of line");Serial2.println(); 178 break; 179 } 180 } 181} 182 183void updateErrorDisplay(){ 184 185 if (serialErrorFlag) { 186 Serial2.println("No Serial Data"); 187 } 188 else { 189 Serial2.println("serial bus OK"); 190 } 191} 192 193void clearStrip(byte j) { 194 for( long int i = 0; i<NUM_PIXELS[j]; i++){ 195 strip[j].setPixelColor(i, 0x000000); strip[j].show(); 196 } 197} 198 199unsigned long int pzRGB(byte r, byte g, byte b) { 200 return (r*65536 + g*256 + b); 201}
16 Channel Fairy Light Driver
c_cpp
This software was loaded on an Arduino Nano. It is meant to control four wire fairy light strings.
1// 16 Channel Fairy Light Driver 2// 3// (c) Paul Zavracky, January, 2018, updated August 2020 4 5#define VERSION "5.4" 6#include <Wire.h> // I2C library 7 8#define OE 2 // pca9955 location of Output Enable pin, active low 9#define RESET 3 // pca9955 location of reset pin, active low 10#define NUM_BOARDS 3 11#define MAX_CHANNELS NUM_BOARDS*15 // Three Boards 15 channels each, three RGB LEDs 12#define BAUD_RATE 57600 13 14// Display Driver Initialization from Adafruit 15#include <Adafruit_GFX.h> 16#include <Adafruit_SSD1306.h> 17 18#define SCREEN_WIDTH 128 // OLED display width, in pixels 19#define SCREEN_HEIGHT 64 // OLED display height, in pixels 20 21// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 22#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 23Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 24 25// Globals 26 27int ch; 28int state; 29int chVal[MAX_CHANNELS] = {0}; 30byte error; 31 // 0:success 32 // 1:data too long to fit in transmit buffer 33 // 2:received NACK on transmit of address 34 // 3:received NACK on transmit of data 35 // 4:other i2c error 36 // 5:no serial data recieved 37 38const char error_0[] PROGMEM = "i2c Success"; // "String 0" etc are strings to store - change to suit. 39const char error_1[] PROGMEM = "i2c too long"; 40const char error_2[] PROGMEM = "i2c addr NACK"; 41const char error_3[] PROGMEM = "i2c data NACK"; 42const char error_4[] PROGMEM = "other i2c error"; 43const char error_5[] PROGMEM = "no serial data"; 44 45const char *const error_table[] PROGMEM = {error_0, error_1, error_2, error_3, error_4, error_5}; // create a table of strings in PROGMEM 46 47const char messages_0[] PROGMEM = "Welcome to the Looneypoons 16 channel fairy light driver, V"; // this string is 29 character long 48const char messages_1[] PROGMEM = "SSD1306 allocation failed"; 49const char messages_2[] PROGMEM = "Looneypoon"; 50const char messages_3[] PROGMEM = "i2c check"; 51const char messages_4[] PROGMEM = "Scanning..."; 52const char messages_5[] PROGMEM = "No Serial Data"; 53const char messages_6[] PROGMEM = "Sys Chk complete"; 54const char messages_7[] PROGMEM = "serial bus OK"; 55 56const char *const messages_table[] PROGMEM = {messages_0, messages_1, messages_2, messages_3, messages_4, messages_5, messages_6, messages_7}; 57 58char buffer[30]; // length of maximum message at minimum 59 60bool i2cErrorFlag = false; 61byte i2cError[MAX_CHANNELS] = {0}; // initial to zero so that change detection will work 62 63bool serialErrorFlag = false; // set error flags to true to force system to clear error from display 64bool oldSerialErrorFlag = true; // this forces the display of the i2c status and the serial status 65 66byte i2cAddress[NUM_BOARDS] = {0x5C, 0x5D, 0x5E}; 67unsigned int loopCounter = 0; // keeps track of how many times through the main loop collecting no serial data 68 69enum states 70{ 71 IDLE, 72 DELIM, 73 READ, 74 DISP 75}; 76 77void setup() { 78 int nDevices; 79 byte address; 80 81 Serial.begin(BAUD_RATE); 82 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[0]))); // Necessary casts and dereferencing, just copy. 83 Serial.print(buffer); Serial.println(VERSION); 84 85 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 86 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 87 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[1]))); // SSD1306 allocation failed 88 Serial.println(buffer); 89 for(;;); // Don't proceed, loop forever 90 } 91 92 // Set up PCA9955 Chip Reset and Output Enable 93 pinMode(RESET, INPUT); // set pin to input 94 digitalWrite(RESET, HIGH); // turn on pullup resistors 95 pinMode(RESET, OUTPUT); 96 digitalWrite(RESET, HIGH); // reset for the PCA9955 is active low 97 delay(1000); // wait 1 second 98 digitalWrite(RESET, LOW); // reset for the PCA9955 is active low 99 delay(100); // send 100 ms reset pulse 100 digitalWrite(RESET, HIGH); // reset for the PCA9955 is active low 101 delay(100); // provide time for 9955 to reset 102 103 pinMode(OE, INPUT); // set pin to input 104 digitalWrite(OE, HIGH); // turn on pullup resistors 105 pinMode(OE, OUTPUT); 106 digitalWrite(OE, HIGH); // output enable for the PCA9955 is active low 107 delay(1000); // wait 1 second 108 digitalWrite(OE, LOW); // output enable for the PCA9955 is active low 109 delay(100); // provide time for 9955 to settle 110 111 // Set Initial Display 112 display.clearDisplay(); 113 display.setTextSize(2); // Normal 1:1 pixel scale 114 display.setTextColor(SSD1306_WHITE); // Draw white text 115 display.setCursor(0,0); // Start at top-left corner 116 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[2]))); // SSD1306 allocation failed 117 display.println(buffer); //"Looneypoon" 118 display.display(); 119 delay(1000); 120 121 display.setTextSize(1); 122 display.print(F("V"));display.println(VERSION); 123 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[3]))); // SSD1306 allocation failed 124 display.println(buffer); //"i2c check" 125 display.println(); 126 display.display(); 127 delay(1000); 128 129 // Start by scanning i2c slave addresses 130 Wire.begin(); // I2C library 131 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[4]))); // SSD1306 allocation failed 132 display.println(buffer); //"Scanning..." 133 display.display(); 134 135 nDevices = 0; 136 for(address = 1; address < 127; address++ ) 137 { 138 // The i2c_scanner uses the return value of 139 // the Write.endTransmisstion to see if 140 // a device did acknowledge to the address. 141 Wire.beginTransmission(address); 142 error = Wire.endTransmission(); 143 // Serial.print("error = "); Serial.println(error); 144 if (error == 0) 145 { 146 display.print(F("0x")); 147 if (address<16) 148 display.print(F("0")); 149 display.print(address,HEX); 150 display.print(", "); 151 nDevices++; 152 } 153 else if (error==4) 154 { 155 display.print(F("Unknown error at ")); 156 if (address<16) 157 display.print(F("0")); 158 display.println(address,HEX); 159 } 160 } 161 display.display(); 162 delay(1000); 163 if (nDevices == 0) 164 display.println(F("No I2C devices found\ 165")); 166 else 167 display.println("done\ 168"); 169 display.display(); 170 delay(3000); // delay so one can read screen 171 172 //Serial.println("The above lists the device/board addresses found.\ 173\ Address 70h and 76h are common to all PCA9955 devices."); 174 175 // Set the output current level 176 display.clearDisplay(); 177 display.setCursor(0,0); // Start at top-left corner 178 display.setTextSize(2); 179 display.println(F("Looneypoon")); 180 display.display(); 181 display.setTextSize(1); 182 for (int board = 0; board < NUM_BOARDS; board++) { 183 Wire.beginTransmission(byte(i2cAddress[board])); // transmit to PCA9955 device specific address 184 Wire.write(byte(0x45)); // Selects the IREFALL register (pg 32 of PCA9955B data sheet) 185 Wire.write(255); // sends current value byte 186 error = Wire.endTransmission(); // stop transmitting 187 display.print("board ");display.print(board);display.print(" error = "); display.println(error); 188 } 189 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[6]))); 190 display.println(buffer); // "Sys Chk complete" 191 display.println(""); // space 192 display.display(); 193 194 Serial.println(buffer); // "Sys Chk complete" 195 delay(3000); 196 197 state = IDLE; 198 ch = 0; 199 200 201} 202 203void loop() { 204 if (Serial.available()) 205 { 206 switch (state) 207 { 208 case IDLE: 209 ch = 0; 210 if (Serial.read() == '+') 211 { 212 state = DELIM; 213 } 214 else 215 { 216 state = IDLE; 217 } 218 break; 219 220 case DELIM: 221 ch = 0; 222 if (Serial.read() == '>') 223 { 224 state = READ; 225 } 226 else 227 { 228 state = IDLE; 229 } 230 break; 231 232 case READ: 233 chVal[ch++] = Serial.read(); 234 if (ch >= MAX_CHANNELS) 235 { 236 ch = 0; 237 state = DISP; 238 } 239 break; 240 241 case DISP: 242 state = IDLE; 243 i2cErrorFlag = false; // initialize 244 for (int board = 0; board < NUM_BOARDS; board++) { 245 for (ch=15*board; ch<15*(1+board); ch++) // each board has 15 channels 246 { 247 //Serial.print(ch);Serial.print(" ");Serial.println(chVal[ch]); 248 Wire.beginTransmission(byte(i2cAddress[board])); // transmit to PCA9955 device specific address 249 Wire.write(byte(0x08 + ch%15)); // Selects the PWM6 register (pg 14 of PCA9955B data sheet) 250 //Serial.println(byte(0x08+ch%15)); 251 Wire.write(chVal[ch]); // sends PWM value byte 252 error = Wire.endTransmission(); // stop transmitting 253 if (i2cError[ch] != error) {i2cErrorFlag = true;} // set error flag only on a change of error condition 254 i2cError[ch] = error; 255 } 256 } 257 loopCounter = 0; // we got serial data, so can reset the loop counter 258 serialErrorFlag = false; // also reset flag 259 break; 260 } 261 } 262 loopCounter++; 263 if (loopCounter == 0) {serialErrorFlag = true; } // we've gone x times without recieving data 264 if((serialErrorFlag != oldSerialErrorFlag) || i2cErrorFlag) {updateErrorDisplay();} // check for errors 265 oldSerialErrorFlag = serialErrorFlag; 266} 267 268void updateErrorDisplay(){ 269 byte j = 1; 270 271 clearDisplayText(); 272 display.setTextSize(1); 273 // check i2c errors 274 if (i2cErrorFlag) { 275 for (int i = 0; i < MAX_CHANNELS; i++) { 276 if(i2cError[i] != 0) { 277 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 278 display.print("channel ");display.print(ch);display.print(" error = "); display.println(i2cError[i]); 279 j++; 280 } 281 } 282 } 283 else { // if the above did not detect an i2c error then... 284 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 285 display.print("no I2C errors "); 286 j++; 287 } 288 display.setCursor(0,16+9*j); // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the first two are yellow) 289 if (serialErrorFlag) { 290 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[5]))); 291 display.print(buffer); // "No Serial Data" 292 //Serial.println(buffer); 293 } 294 else { 295 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[7]))); 296 display.print(buffer); // "serial bus OK" 297 //Serial.println(buffer); 298 } 299 display.display(); 300} 301 302void clearDisplayText() { 303 display.fillRect(0, 16, 127, 47, BLACK); 304 display.display(); 305}
16 Channel Fairy Light Driver
c_cpp
This software was loaded on an Arduino Nano. It is meant to control four wire fairy light strings.
1// 16 Channel Fairy Light Driver 2// 3// (c) Paul Zavracky, January, 4 2018, updated August 2020 5 6#define VERSION "5.4" 7#include <Wire.h> // 8 I2C library 9 10#define OE 2 // pca9955 location of Output Enable pin, active 11 low 12#define RESET 3 // pca9955 location of reset pin, active low 13#define NUM_BOARDS 14 3 15#define MAX_CHANNELS NUM_BOARDS*15 // Three Boards 15 channels each, three 16 RGB LEDs 17#define BAUD_RATE 57600 18 19// Display Driver Initialization from 20 Adafruit 21#include <Adafruit_GFX.h> 22#include <Adafruit_SSD1306.h> 23 24#define 25 SCREEN_WIDTH 128 // OLED display width, in pixels 26#define SCREEN_HEIGHT 64 // 27 OLED display height, in pixels 28 29// Declaration for an SSD1306 display connected 30 to I2C (SDA, SCL pins) 31#define OLED_RESET 4 // Reset pin # (or -1 if sharing 32 Arduino reset pin) 33Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, 34 OLED_RESET); 35 36// Globals 37 38int ch; 39int state; 40int chVal[MAX_CHANNELS] 41 = {0}; 42byte error; 43 // 0:success 44 // 1:data too long to 45 fit in transmit buffer 46 // 2:received NACK on transmit of address 47 48 // 3:received NACK on transmit of data 49 // 4:other i2c error 50 51 // 5:no serial data recieved 52 53const char error_0[] PROGMEM = "i2c 54 Success"; // "String 0" etc are strings to store - change to suit. 55const char 56 error_1[] PROGMEM = "i2c too long"; 57const char error_2[] PROGMEM = "i2c addr 58 NACK"; 59const char error_3[] PROGMEM = "i2c data NACK"; 60const char error_4[] 61 PROGMEM = "other i2c error"; 62const char error_5[] PROGMEM = "no serial data"; 63 64const 65 char *const error_table[] PROGMEM = {error_0, error_1, error_2, error_3, error_4, 66 error_5}; // create a table of strings in PROGMEM 67 68const char messages_0[] 69 PROGMEM = "Welcome to the Looneypoons 16 channel fairy light driver, V"; // this 70 string is 29 character long 71const char messages_1[] PROGMEM = "SSD1306 allocation 72 failed"; 73const char messages_2[] PROGMEM = "Looneypoon"; 74const char messages_3[] 75 PROGMEM = "i2c check"; 76const char messages_4[] PROGMEM = "Scanning..."; 77const 78 char messages_5[] PROGMEM = "No Serial Data"; 79const char messages_6[] PROGMEM 80 = "Sys Chk complete"; 81const char messages_7[] PROGMEM = "serial bus OK"; 82 83const 84 char *const messages_table[] PROGMEM = {messages_0, messages_1, messages_2, messages_3, 85 messages_4, messages_5, messages_6, messages_7}; 86 87char buffer[30]; // length 88 of maximum message at minimum 89 90bool i2cErrorFlag = false; 91byte 92 i2cError[MAX_CHANNELS] = {0}; // initial to zero so that change detection will work 93 94bool 95 serialErrorFlag = false; // set error flags to true to force system to clear error 96 from display 97bool oldSerialErrorFlag = true; // this forces the display of the 98 i2c status and the serial status 99 100byte i2cAddress[NUM_BOARDS] 101 = {0x5C, 0x5D, 0x5E}; 102unsigned int loopCounter = 0; // keeps track of how many 103 times through the main loop collecting no serial data 104 105enum states 106{ 107 108 IDLE, 109 DELIM, 110 READ, 111 DISP 112}; 113 114void setup() { 115 int nDevices; 116 117 byte address; 118 119 Serial.begin(BAUD_RATE); 120 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[0]))); 121 // Necessary casts and dereferencing, just copy. 122 Serial.print(buffer); Serial.println(VERSION); 123 124 125 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 126 if(!display.begin(SSD1306_SWITCHCAPVCC, 127 0x3C)) { // Address 0x3C for 128x64 128 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[1]))); 129 // SSD1306 allocation failed 130 Serial.println(buffer); 131 for(;;); // Don't 132 proceed, loop forever 133 } 134 135 // Set up PCA9955 Chip Reset and Output 136 Enable 137 pinMode(RESET, INPUT); // set pin to input 138 digitalWrite(RESET, 139 HIGH); // turn on pullup resistors 140 pinMode(RESET, OUTPUT); 141 digitalWrite(RESET, 142 HIGH); // reset for the PCA9955 is active low 143 delay(1000); // wait 1 second 144 145 digitalWrite(RESET, LOW); // reset for the PCA9955 is active low 146 delay(100); 147 // send 100 ms reset pulse 148 digitalWrite(RESET, HIGH); // reset for the PCA9955 149 is active low 150 delay(100); // provide time for 9955 to reset 151 152 pinMode(OE, 153 INPUT); // set pin to input 154 digitalWrite(OE, HIGH); // turn 155 on pullup resistors 156 pinMode(OE, OUTPUT); 157 digitalWrite(OE, HIGH); // output 158 enable for the PCA9955 is active low 159 delay(1000); // wait 1 second 160 digitalWrite(OE, 161 LOW); // output enable for the PCA9955 is active low 162 delay(100); // provide 163 time for 9955 to settle 164 165 // Set Initial Display 166 display.clearDisplay(); 167 168 display.setTextSize(2); // Normal 1:1 pixel scale 169 display.setTextColor(SSD1306_WHITE); 170 // Draw white text 171 display.setCursor(0,0); // Start at 172 top-left corner 173 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[2]))); 174 // SSD1306 allocation failed 175 display.println(buffer); //"Looneypoon" 176 177 display.display(); 178 delay(1000); 179 180 display.setTextSize(1); 181 display.print(F("V"));display.println(VERSION); 182 183 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[3]))); // SSD1306 allocation 184 failed 185 display.println(buffer); //"i2c check" 186 display.println(); 187 188 display.display(); 189 delay(1000); 190 191 // Start by scanning i2c slave addresses 192 193 Wire.begin(); // I2C library 194 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[4]))); 195 // SSD1306 allocation failed 196 display.println(buffer); //"Scanning..." 197 198 display.display(); 199 200 nDevices = 0; 201 for(address = 1; address < 127; 202 address++ ) 203 { 204 // The i2c_scanner uses the return value of 205 // 206 the Write.endTransmisstion to see if 207 // a device did acknowledge to the address. 208 209 Wire.beginTransmission(address); 210 error = Wire.endTransmission(); 211 212 // Serial.print("error = "); Serial.println(error); 213 if (error == 0) 214 215 { 216 display.print(F("0x")); 217 if (address<16) 218 display.print(F("0")); 219 220 display.print(address,HEX); 221 display.print(", "); 222 nDevices++; 223 224 } 225 else if (error==4) 226 { 227 display.print(F("Unknown error 228 at ")); 229 if (address<16) 230 display.print(F("0")); 231 display.println(address,HEX); 232 233 } 234 } 235 display.display(); 236 delay(1000); 237 if (nDevices == 238 0) 239 display.println(F("No I2C devices found\ 240")); 241 else 242 display.println("done\ 243"); 244 245 display.display(); 246 delay(3000); // delay so one can read screen 247 248 249 //Serial.println("The above lists the device/board addresses found.\ 250\ Address 251 70h and 76h are common to all PCA9955 devices."); 252 253 // Set the output current 254 level 255 display.clearDisplay(); 256 display.setCursor(0,0); // Start 257 at top-left corner 258 display.setTextSize(2); 259 display.println(F("Looneypoon")); 260 261 display.display(); 262 display.setTextSize(1); 263 for (int board = 0; board 264 < NUM_BOARDS; board++) { 265 Wire.beginTransmission(byte(i2cAddress[board])); 266 // transmit to PCA9955 device specific address 267 Wire.write(byte(0x45)); // 268 Selects the IREFALL register (pg 32 of PCA9955B data sheet) 269 Wire.write(255); 270 // sends current value byte 271 error = Wire.endTransmission(); 272 // stop transmitting 273 display.print("board ");display.print(board);display.print(" 274 error = "); display.println(error); 275 } 276 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[6]))); 277 278 display.println(buffer); // "Sys Chk complete" 279 display.println(""); 280 // space 281 display.display(); 282 283 Serial.println(buffer); // "Sys Chk 284 complete" 285 delay(3000); 286 287 state = IDLE; 288 ch = 0; 289 290 291} 292 293void 294 loop() { 295 if (Serial.available()) 296 { 297 switch (state) 298 { 299 300 case IDLE: 301 ch = 0; 302 if (Serial.read() == '+') 303 { 304 305 state = DELIM; 306 } 307 else 308 { 309 310 state = IDLE; 311 } 312 break; 313 314 case DELIM: 315 316 ch = 0; 317 if (Serial.read() == '>') 318 { 319 state 320 = READ; 321 } 322 else 323 { 324 state = IDLE; 325 326 } 327 break; 328 329 case READ: 330 chVal[ch++] = 331 Serial.read(); 332 if (ch >= MAX_CHANNELS) 333 { 334 ch = 335 0; 336 state = DISP; 337 } 338 break; 339 340 case 341 DISP: 342 state = IDLE; 343 i2cErrorFlag = false; // initialize 344 345 for (int board = 0; board < NUM_BOARDS; board++) { 346 for (ch=15*board; 347 ch<15*(1+board); ch++) // each board has 15 channels 348 { 349 //Serial.print(ch);Serial.print(" 350 ");Serial.println(chVal[ch]); 351 Wire.beginTransmission(byte(i2cAddress[board])); 352 // transmit to PCA9955 device specific address 353 Wire.write(byte(0x08 354 + ch%15)); // Selects the PWM6 register (pg 14 of PCA9955B data sheet) 355 356 //Serial.println(byte(0x08+ch%15)); 357 Wire.write(chVal[ch]); 358 // sends PWM value byte 359 error = Wire.endTransmission(); 360 // stop transmitting 361 if (i2cError[ch] != error) {i2cErrorFlag 362 = true;} // set error flag only on a change of error condition 363 i2cError[ch] 364 = error; 365 } 366 } 367 loopCounter = 0; // we got serial 368 data, so can reset the loop counter 369 serialErrorFlag = false; // also 370 reset flag 371 break; 372 } 373 } 374 loopCounter++; 375 if (loopCounter 376 == 0) {serialErrorFlag = true; } // we've gone x times without recieving data 377 378 if((serialErrorFlag != oldSerialErrorFlag) || i2cErrorFlag) {updateErrorDisplay();} 379 // check for errors 380 oldSerialErrorFlag = serialErrorFlag; 381} 382 383void 384 updateErrorDisplay(){ 385 byte j = 1; 386 387 clearDisplayText(); 388 display.setTextSize(1); 389 390 // check i2c errors 391 if (i2cErrorFlag) { 392 for (int i = 0; i < MAX_CHANNELS; 393 i++) { 394 if(i2cError[i] != 0) { 395 display.setCursor(0,16+9*j); // 396 Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the 397 first two are yellow) 398 display.print("channel ");display.print(ch);display.print(" 399 error = "); display.println(i2cError[i]); 400 j++; 401 } 402 } 403 404 } 405 else { // if the above did not detect an i2c error then... 406 display.setCursor(0,16+9*j); 407 // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, 408 the first two are yellow) 409 display.print("no I2C errors "); 410 j++; 411 412 } 413 display.setCursor(0,16+9*j); // Start at top-left corner of blue area 414 only ( there are 8 pages 8 pixels high, the first two are yellow) 415 if (serialErrorFlag) 416 { 417 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[5]))); 418 display.print(buffer); 419 // "No Serial Data" 420 //Serial.println(buffer); 421 } 422 else { 423 424 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[7]))); 425 display.print(buffer); 426 // "serial bus OK" 427 //Serial.println(buffer); 428 } 429 display.display(); 430} 431 432void 433 clearDisplayText() { 434 display.fillRect(0, 16, 127, 47, BLACK); 435 display.display(); 436}
Individually Addressable Controller (approach 1)
c_cpp
This software is meant to run on an Arduino Mega. It can be used in a situation where there are multiple individually addressable strips to control separately. This is one of two approaches that can be employed. In this approach, the software creates strips that match the hardware configuration and then manages them. The strip definitions in this code must match the strip definitions used by Vixen.
1// Individually Addressable Controller 2// 3// (c) Paul Zavracky, 4 September, 2020; update Nov 2021 5 6#define VERSION "2.0" 7#include <Wire.h> 8 // I2C library 9#include <Adafruit_NeoPixel.h> 10 11// start of strip definition 12#define 13 NUM_STRIPS 10 14#define BRIGHTNESS 100 15 16// strip definitions 17#define UNDP 18 0 // element order in hardware - must be converted to match Vixen (see stripDataOrder) 19#define 20 UNDPpin 3 // Uppoer Nose Diagonal Port (1 O'clock on ten hour clock) 21#define 22 UNDPpixels 67 // Uppoer Nose Diagonal Port 23 24#define DNTP 1 25#define DNTPpin 26 4 // Diagonal Nose Top Port (2 O'clock) 27#define DNTPpixels 78 // Diagonal Nose 28 Top Port 29 30#define HNP 2 31#define HNPpin 5 // Horizontal Nose Port (3 O'Clock) 32#define 33 HNPpixels 78 // Horizontal Nose Port 34 35#define DNBP 3 36#define DNBPpin 6 37 // Diagonal Nose Bottom Port (4 O'Clock) 38#define DNBPpixels 78 // Diagonal Nose 39 Bottom Port 40 41#define VNB 4 42#define VNBpin 7 // Vertical Nose Bottom (5 43 O'Clock) 44#define VNBpixels 17 // Vertical Nose Bottom 45 46#define DNBS 5 47#define 48 UNDSpin 8 // Uppoer Nose Diagonal Port (6 O'Clock) 49#define UNDSpixels 78 // Uppoer 50 Nose Diagonal Port 51 52#define HNS 6 53#define DNTSpin 9 // Diagonal Nose Top 54 Port (7 O'Clock) 55#define DNTSpixels 78 // Diagonal Nose Top Port 56 57#define 58 DNTS 7 59#define HNSpin 10 // Horizontal Nose Port (8 O'Clock) 60#define HNSpixels 61 78 // Horizontal Nose Port 62 63#define UNDS 8 64#define DNBSpin 11 // Diagonal 65 Nose Bottom Port (9 O'Clock) 66#define DNBSpixels 67 // Diagonal Nose Bottom Port 67 (9) 68 69#define VNT 9 70#define VNTpin 12 // Vertical Nose Top (10 O'Clock) 71#define 72 VNTpixels 60 // Vertical Nose Top 73 74#define MAX_CHANNELS 2046 // 3*(4*DNTPpixels+2*UNDPpixels+VNBpixels+VNTpixels) 75 This is the total number of leds (counting each color) on the nose 76#define BAUD_RATE0 77 115200 // this is the rate at which the serial monitor operates - wants to be higher 78 than below 79#define BAUD_RATE3 115200 // this is the rate for data coming in 80 from Vixen (on port 3) 81 82// Display Driver Initialization from Adafruit 83#include 84 <Adafruit_GFX.h> 85 86// Globals 87 88int ch; 89int ch1; 90byte stripNum; 91int 92 state; 93int chVal[MAX_CHANNELS] = {0}; 94byte STRIP_PINS[NUM_STRIPS] {UNDPpin, 95 DNTPpin, HNPpin, DNBPpin, VNBpin, UNDSpin, DNTSpin, HNSpin, DNBSpin, VNTpin}; 96int 97 NUM_PIXELS[NUM_STRIPS] {UNDPpixels, DNTPpixels, HNPpixels, DNBPpixels, VNBpixels, 98 UNDSpixels, DNTSpixels, HNSpixels, DNBSpixels, VNTpixels}; 99 100byte stripDataOrder[] 101 = {HNP,HNS,VNT,VNB,DNTS,DNBS,DNTP,DNBP,UNDP,UNDS}; // this translates to the order 102 of elements in Vixen 103 104bool serialErrorFlag = false; // set error flags 105 to true to force system to clear error 106bool oldSerialErrorFlag = false; // this 107 forces the display of the serial status 108 109enum states 110{ 111 IDLE, 112 113 DELIM, 114 READ, 115 DISP 116}; 117 118Adafruit_NeoPixel strip[] = { 119 Adafruit_NeoPixel(NUM_PIXELS[0], 120 STRIP_PINS[0], NEO_GRB + NEO_KHZ800), 121 Adafruit_NeoPixel(NUM_PIXELS[1], STRIP_PINS[1], 122 NEO_GRB + NEO_KHZ800), 123 Adafruit_NeoPixel(NUM_PIXELS[2], STRIP_PINS[2], NEO_GRB 124 + NEO_KHZ800), 125 Adafruit_NeoPixel(NUM_PIXELS[3], STRIP_PINS[3], NEO_GRB + NEO_KHZ800), 126 127 Adafruit_NeoPixel(NUM_PIXELS[4], STRIP_PINS[4], NEO_GRB + NEO_KHZ800), 128 Adafruit_NeoPixel(NUM_PIXELS[5], 129 STRIP_PINS[5], NEO_GRB + NEO_KHZ800), 130 Adafruit_NeoPixel(NUM_PIXELS[6], STRIP_PINS[6], 131 NEO_GRB + NEO_KHZ800), 132 Adafruit_NeoPixel(NUM_PIXELS[7], STRIP_PINS[7], NEO_GRB 133 + NEO_KHZ800), 134 Adafruit_NeoPixel(NUM_PIXELS[8], STRIP_PINS[8], NEO_GRB + NEO_KHZ800), 135 136 Adafruit_NeoPixel(NUM_PIXELS[9], STRIP_PINS[9], NEO_GRB + NEO_KHZ800) 137}; 138 139 140unsigned long int pixelSum; // used to keep track of the assignment of Vixen 141 data with pixel count and strip 142unsigned long int color; // must convert chVal()s 143 RGB into long integer 144 145void setup() { 146 147 Serial2.begin(BAUD_RATE0); 148 149 Serial.begin(BAUD_RATE3); 150 Serial2.println(); 151 Serial2.print("Individually 152 Addressable Controller, VERSION "); Serial2.println(VERSION); 153 154 for (int 155 i = 0; i<NUM_STRIPS; i++) { 156 strip[i].setBrightness(BRIGHTNESS); 157 strip[i].begin(); 158 159 clearStrip(i); // Initialize all pixels to 'off' 160 Serial2.print("strip 161 "); Serial2.println(i); 162 } 163 //delay(1000); 164 165 Serial2.println("Sys 166 Chk complete" ); 167 delay(3000); 168 169 state = IDLE; 170 ch = 0; 171} 172 173void 174 loop() { 175 176 if (Serial.available()) 177 { 178 switch (state) 179 { 180 181 case IDLE: 182 ch = 0; 183 if (Serial.read() == '+') 184 { 185 186 state = DELIM; 187 } 188 else 189 { 190 191 state = IDLE; 192 } 193 break; 194 195 case DELIM: 196 197 ch = 0; 198 if (Serial.read() == '>') 199 { 200 state 201 = READ; 202 } 203 else 204 { 205 state = IDLE; 206 207 } 208 break; 209 210 case READ: 211 chVal[ch++] = 212 Serial.read(); 213 if (ch > MAX_CHANNELS) 214 { 215 ch = 216 0; 217 state = DISP; 218 } 219 break; 220 221 case 222 DISP: 223 state = IDLE; 224 pixelSum = 0; 225 for(int j = 0; 226 j < NUM_STRIPS; j++) { // for all the strips 227 for(long int i = 0; i 228 < NUM_PIXELS[stripDataOrder[j]]; i++) { // for all pixels within a strip 229 strip[stripDataOrder[j]].setPixelColor(i, 230 chVal[3*i+pixelSum], chVal[3*i+1+pixelSum], chVal[3*i+2+pixelSum]); // set pixel 231 to Vixen color 232 /* 233 Serial2.print(3*i+pixelSum);Serial2.print(" 234 ");Serial2.print(chVal[3*i+pixelSum]);Serial2.print(" "); 235 Serial2.print(chVal[3*i+1+pixelSum]);Serial2.print(" 236 "); 237 Serial2.println(chVal[3*i+2+pixelSum]); 238 Serial2.println(); 239 240 */ 241 } 242 strip[stripDataOrder[j]].show(); 243 244 pixelSum = pixelSum + 3 * NUM_PIXELS[stripDataOrder[j]]; 245 } 246 247 //Serial2.println("end of line");Serial2.println(); 248 break; 249 250 } 251 } 252} 253 254void updateErrorDisplay(){ 255 256 if (serialErrorFlag) 257 { 258 Serial2.println("No Serial Data"); 259 } 260 else { 261 Serial2.println("serial 262 bus OK"); 263 } 264} 265 266void clearStrip(byte j) { 267 for( long int i = 0; 268 i<NUM_PIXELS[j]; i++){ 269 strip[j].setPixelColor(i, 0x000000); strip[j].show(); 270 271 } 272} 273 274unsigned long int pzRGB(byte r, byte g, byte b) { 275 return (r*65536 276 + g*256 + b); 277}
Single_Element_Controller
c_cpp
This code is meant to run on an Arduino Nano. It will access all 16 pulse width modulated outputs on the fairy board and is capable of using 14 of the 16 available digital I/O of the Arduino. When interfacing to Vixen, the first 16 channels correspond to the PWM outputs. The next 16 correspond to the digital I/O.
1// 16 Channel Single Element Driver with 16 relay outputs 2// 3// 4 (c) Paul Zavracky, January, 2018, updated August 2020 5 6#define VERSION "1.1" 7#include 8 <Wire.h> // I2C library 9 10#define OE 2 // pca9955 location of Output Enable 11 pin, active low 12#define RESET 3 // pca9955 location of reset pin, active low 13#define 14 MAX_CHANNELS 32 // 16 pca9955 Channels and 16 digital channels (relays) 15#define 16 BAUD_RATE 115200 17 18// Define relay output pins 19#define DIGITAL_OUT_0 5 20#define 21 DIGITAL_OUT_1 6 22#define DIGITAL_OUT_2 7 23#define DIGITAL_OUT_3 8 24 25#define 26 DIGITAL_OUT_4 9 27#define DIGITAL_OUT_5 10 28#define DIGITAL_OUT_6 11 29#define 30 DIGITAL_OUT_7 12 31 32#define DIGITAL_OUT_8 A0 33#define DIGITAL_OUT_9 A1 34#define 35 DIGITAL_OUT_10 A2 36#define DIGITAL_OUT_11 A3 37// A4 and A5 are used for i2c 38 interface 39#define DIGITAL_OUT_12 A6 // not currently accessable on board 40#define 41 DIGITAL_OUT_13 A7 // not currently accessable on board 42#define DIGITAL_OUT_14 43 4 44#define DIGITAL_OUT_15 13 45 46// Display Driver Initialization from Adafruit 47#include 48 <Adafruit_GFX.h> 49#include <Adafruit_SSD1306.h> 50 51#define SCREEN_WIDTH 128 52 // OLED display width, in pixels 53#define SCREEN_HEIGHT 64 // OLED display height, 54 in pixels 55 56// Declaration for an SSD1306 display connected to I2C (SDA, SCL 57 pins) 58#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 59Adafruit_SSD1306 60 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 61 62// Globals 63 64int 65 ch; 66int state; 67int chVal[MAX_CHANNELS] = {0}; 68byte error; 69 // 70 0:success 71 // 1:data too long to fit in transmit buffer 72 // 73 2:received NACK on transmit of address 74 // 3:received NACK on transmit 75 of data 76 // 4:other i2c error 77 // 5:no serial data recieved 78 79const 80 char error_0[] PROGMEM = "i2c Success"; // "String 0" etc are strings to store 81 - change to suit. 82const char error_1[] PROGMEM = "i2c too long"; 83const char 84 error_2[] PROGMEM = "i2c addr NACK"; 85const char error_3[] PROGMEM = "i2c data 86 NACK"; 87const char error_4[] PROGMEM = "other i2c error"; 88const char error_5[] 89 PROGMEM = "no serial data"; 90 91const char *const error_table[] PROGMEM = {error_0, 92 error_1, error_2, error_3, error_4, error_5}; // create a table of strings in PROGMEM 93 94const 95 char messages_0[] PROGMEM = "Welcome to the Looneypoons 16 channel Single Element 96 driver, V"; // this string is 29 character long 97const char messages_1[] PROGMEM 98 = "SSD1306 allocation failed"; 99const char messages_2[] PROGMEM = "Looneypoon"; 100const 101 char messages_3[] PROGMEM = "i2c check"; 102const char messages_4[] PROGMEM = 103 "Scanning..."; 104const char messages_5[] PROGMEM = "No Serial Data"; 105const 106 char messages_6[] PROGMEM = "Sys Chk complete"; 107const char messages_7[] PROGMEM 108 = "serial bus OK"; 109 110const char *const messages_table[] PROGMEM = {messages_0, 111 messages_1, messages_2, messages_3, messages_4, messages_5, messages_6, messages_7}; 112 113char 114 buffer[30]; // length of maximum message at minimum 115 116bool i2cErrorFlag 117 = false; 118byte i2cError[MAX_CHANNELS] = {0}; // initial to 119 zero so that change detection will work 120 121bool serialErrorFlag = false; // 122 set error flags to true to force system to clear error from display 123bool oldSerialErrorFlag 124 = true; // this forces the display of the i2c status and the serial status 125 126byte 127 i2cAddress = 0x5A; 128unsigned int loopCounter = 0; // keeps track of how many times 129 through the main loop collecting no serial data 130 131byte digitalOut[] = {DIGITAL_OUT_0, 132 DIGITAL_OUT_1, DIGITAL_OUT_2, DIGITAL_OUT_3, DIGITAL_OUT_4, DIGITAL_OUT_5, DIGITAL_OUT_6, 133 DIGITAL_OUT_7, DIGITAL_OUT_8, 134 DIGITAL_OUT_9, DIGITAL_OUT_10, 135 DIGITAL_OUT_11, DIGITAL_OUT_12, DIGITAL_OUT_13, DIGITAL_OUT_14, DIGITAL_OUT_15}; 136 137enum 138 states 139{ 140 IDLE, 141 DELIM, 142 READ, 143 DISP 144}; 145 146void setup() 147 { 148 int nDevices; 149 byte address; 150 151 Serial.begin(BAUD_RATE); 152 strcpy_P(buffer, 153 (char *)pgm_read_word(&(messages_table[0]))); // Necessary casts and dereferencing, 154 just copy. 155 Serial.print(buffer); Serial.println(VERSION); 156 157 // SSD1306_SWITCHCAPVCC 158 = generate display voltage from 3.3V internally 159 if(!display.begin(SSD1306_SWITCHCAPVCC, 160 0x3C)) { // Address 0x3C for 128x64 161 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[1]))); 162 // SSD1306 allocation failed 163 Serial.println(buffer); 164 for(;;); // Don't 165 proceed, loop forever 166 } 167 168 // Set up PCA9955 Chip Reset and Output 169 Enable 170 pinMode(RESET, INPUT); // set pin to input 171 digitalWrite(RESET, 172 HIGH); // turn on pullup resistors 173 pinMode(RESET, OUTPUT); 174 digitalWrite(RESET, 175 HIGH); // reset for the PCA9955 is active low 176 delay(1000); // wait 1 second 177 178 digitalWrite(RESET, LOW); // reset for the PCA9955 is active low 179 delay(100); 180 // send 100 ms reset pulse 181 digitalWrite(RESET, HIGH); // reset for the PCA9955 182 is active low 183 delay(100); // provide time for 9955 to reset 184 185 pinMode(OE, 186 INPUT); // set pin to input 187 digitalWrite(OE, HIGH); // turn 188 on pullup resistors 189 pinMode(OE, OUTPUT); 190 digitalWrite(OE, HIGH); // output 191 enable for the PCA9955 is active low 192 delay(1000); // wait 1 second 193 digitalWrite(OE, 194 LOW); // output enable for the PCA9955 is active low 195 delay(100); // provide 196 time for 9955 to settle 197 198 // Set Initial Display 199 display.clearDisplay(); 200 201 display.setTextSize(2); // Normal 1:1 pixel scale 202 display.setTextColor(SSD1306_WHITE); 203 // Draw white text 204 display.setCursor(0,0); // Start at 205 top-left corner 206 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[2]))); 207 // SSD1306 allocation failed 208 display.println(buffer); //"Looneypoon" 209 210 display.display(); 211 delay(1000); 212 213 display.setTextSize(1); 214 display.print(F("V"));display.println(VERSION); 215 216 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[3]))); // SSD1306 allocation 217 failed 218 display.println(buffer); //"i2c check" 219 display.println(); 220 221 display.display(); 222 delay(1000); 223 224 // Start by scanning i2c slave addresses 225 226 Wire.begin(); // I2C library 227 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[4]))); 228 // SSD1306 allocation failed 229 display.println(buffer); //"Scanning..." 230 231 display.display(); 232 233 nDevices = 0; 234 for(address = 1; address < 127; 235 address++ ) 236 { 237 // The i2c_scanner uses the return value of 238 // 239 the Write.endTransmisstion to see if 240 // a device did acknowledge to the address. 241 242 Wire.beginTransmission(address); 243 error = Wire.endTransmission(); 244 245 // Serial.print("error = "); Serial.println(error); 246 if (error == 0) 247 248 { 249 display.print(F("0x")); 250 if (address<16) 251 display.print(F("0")); 252 253 display.print(address,HEX); 254 display.print(", "); 255 nDevices++; 256 257 } 258 else if (error==4) 259 { 260 display.print(F("Unknown error 261 at ")); 262 if (address<16) 263 display.print(F("0")); 264 display.println(address,HEX); 265 266 } 267 } 268 display.display(); 269 delay(1000); 270 if (nDevices == 271 0) 272 display.println(F("No I2C devices found\ 273")); 274 else 275 display.println("done\ 276"); 277 278 display.display(); 279 delay(3000); // delay so one can read screen 280 281 282 //Serial.println("The above lists the device/board addresses found.\ 283\ Address 284 70h and 76h are common to all PCA9955 devices."); 285 286 // Set the output current 287 level 288 display.clearDisplay(); 289 display.setCursor(0,0); // Start 290 at top-left corner 291 display.setTextSize(2); 292 display.println(F("Looneypoon")); 293 294 display.display(); 295 display.setTextSize(1); 296 297 Wire.beginTransmission(byte(i2cAddress)); 298 // transmit to PCA9955 device specific address 299 Wire.write(byte(0x45)); // 300 Selects the IREFALL register (pg 32 of PCA9955B data sheet) 301 Wire.write(255); 302 // sends current value byte 303 error = Wire.endTransmission(); 304 // stop transmitting 305 display.print(" error = "); display.println(error); 306 307 308 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[6]))); 309 display.println(buffer); 310 // "Sys Chk complete" 311 display.println(""); // space 312 display.display(); 313 314 315 Serial.println(buffer); // "Sys Chk complete" 316 delay(3000); 317 318 319 for (int i = 0; i<12; i++) { // set up output ports 320 pinMode(digitalOut[i], 321 OUTPUT); 322 digitalWrite(digitalOut[i], HIGH); // logic low activates relay 323 324 } 325 state = IDLE; 326 ch = 0; 327} 328 329void loop() { 330 if (Serial.available()) 331 332 { 333 switch (state) 334 { 335 case IDLE: 336 ch = 0; 337 338 if (Serial.read() == '+') 339 { 340 state = DELIM; 341 342 } 343 else 344 { 345 state = IDLE; 346 } 347 348 break; 349 350 case DELIM: 351 ch = 0; 352 if (Serial.read() 353 == '>') 354 { 355 state = READ; 356 } 357 else 358 359 { 360 state = IDLE; 361 } 362 break; 363 364 365 case READ: 366 chVal[ch++] = Serial.read(); 367 if (ch >= MAX_CHANNELS) 368 369 { 370 ch = 0; 371 state = DISP; 372 } 373 break; 374 375 376 case DISP: 377 state = IDLE; 378 //Serial.println("made 379 it to disp"); 380 i2cErrorFlag = false; // initialize 381 for (ch=0; 382 ch<16; ch++) // board has 16 PWM channels 383 { 384 Serial.print(ch);Serial.print(" 385 ");Serial.println(chVal[ch]); 386 Wire.beginTransmission(byte(i2cAddress)); 387 // transmit to PCA9955 device specific address 388 Wire.write(byte(0x08 389 + ch)); // Selects the PWM6 register (pg 14 of PCA9955B data sheet) 390 391 Wire.write(chVal[ch]); // sends PWM value byte 392 393 error = Wire.endTransmission(); // stop transmitting 394 395 if (i2cError[ch] != error) {i2cErrorFlag = true;} // set error flag only 396 on a change of error condition 397 i2cError[ch] = error; 398 } 399 400 for (ch=16; ch<MAX_CHANNELS; ch++) // board has 8 digital channels 401 { 402 403 Serial.print(ch);Serial.print(" ");Serial.println(chVal[ch]); 404 405 digitalWrite(digitalOut[ch - 16],(chVal[ch]>0) ? LOW : HIGH ); // sends 406 value as boolean (relays are active low!!) 407 } 408 loopCounter = 409 0; // we got serial data, so can reset the loop counter 410 serialErrorFlag 411 = false; // also reset flag 412 break; 413 } 414 } 415 loopCounter++; 416 417 if (loopCounter == 0) {serialErrorFlag = true; } // we've gone x times without 418 recieving data 419 if((serialErrorFlag != oldSerialErrorFlag) || i2cErrorFlag) 420 {updateErrorDisplay();} // check for errors 421 oldSerialErrorFlag = serialErrorFlag; 422} 423 424void 425 updateErrorDisplay(){ 426 byte j = 1; 427 428 clearDisplayText(); 429 display.setTextSize(1); 430 431 // check i2c errors 432 if (i2cErrorFlag) { 433 for (int i = 0; i < MAX_CHANNELS; 434 i++) { 435 if(i2cError[i] != 0) { 436 display.setCursor(0,16+9*j); // 437 Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, the 438 first two are yellow) 439 display.print("channel ");display.print(ch);display.print(" 440 error = "); display.println(i2cError[i]); 441 j++; 442 } 443 } 444 445 } 446 else { // if the above did not detect an i2c error then... 447 display.setCursor(0,16+9*j); 448 // Start at top-left corner of blue area only ( there are 8 pages 8 pixels high, 449 the first two are yellow) 450 display.print("no I2C errors "); 451 j++; 452 453 } 454 display.setCursor(0,16+9*j); // Start at top-left corner of blue area 455 only ( there are 8 pages 8 pixels high, the first two are yellow) 456 if (serialErrorFlag) 457 { 458 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[5]))); 459 display.print(buffer); 460 // "No Serial Data" 461 //Serial.println(buffer); 462 } 463 else { 464 465 strcpy_P(buffer, (char *)pgm_read_word(&(messages_table[7]))); 466 display.print(buffer); 467 // "serial bus OK" 468 //Serial.println(buffer); 469 } 470 display.display(); 471} 472 473void 474 clearDisplayText() { 475 display.fillRect(0, 16, 127, 47, BLACK); 476 display.display(); 477} 478
Individually Addressable Controller (approach 2)
c_cpp
This is the second approach to individually addressable strips. It assumes that the strip data lines are tied end to end and therefore, only one Arduino pin is required to drive several elements in the series. In this approach it is imperative that the count of LEDs in each element exactly matches that defined in Vixen.
1// Individually Addressable Controller 2// 3// (c) Paul Zavracky, September, 2020; update Nov 2021 4 5#define VERSION "2.0" 6#include <Wire.h> // I2C library 7#include <Adafruit_NeoPixel.h> 8 9// start of strip definition 10#define BRIGHTNESS 64 11 12// strip definitions 13 14#define BNRpin 4 15#define BNRpixels 252 16 17#define MAX_CHANNELS 756 // This is the total number of leds (counting each color) on the nose 18#define BAUD_RATE0 2000000 // this is the rate at which the serial monitor operates - wants to be higher than below 19#define BAUD_RATE3 115200 // this is the rate for data coming in from Vixen (on port 3) 20 21// Display Driver Initialization from Adafruit 22#include <Adafruit_GFX.h> 23 24// Globals 25 26int ch; 27int state; 28int chVal[MAX_CHANNELS] = {0}; 29char serialInput; 30 31bool serialErrorFlag = false; // set error flags to true to force system to clear error 32bool oldSerialErrorFlag = false; // this forces the display of the serial status 33bool firstData; 34 35enum states 36{ 37 IDLE, 38 DELIM, 39 READ, 40 DISP 41}; 42 43Adafruit_NeoPixel strip(BNRpixels, BNRpin, NEO_GRB + NEO_KHZ800); 44 45unsigned long int pixelSum; // used to keep track of the assignment of Vixen data with pixel count and strip 46unsigned long int color; // must convert chVal()s RGB into long integer 47 48void setup() { 49 Serial.begin(BAUD_RATE0); 50 Serial3.begin(BAUD_RATE3); 51 Serial.println(); 52 Serial.print("Individually Addressable Controller, VERSION "); Serial.println(VERSION); 53 54 strip.setBrightness(BRIGHTNESS); 55 strip.begin(); 56 showStrip(); // Turn all lights on white 57 Serial.println("made it to show strip"); 58 delay(1000); 59 clearStrip(); // Initialize all pixels to 'off' 60 Serial.println("made it to clear strip"); 61 delay(1000); 62 63 Serial.println("Sys Chk complete" ); 64 delay(3000); 65 66 state = IDLE; 67 ch = 0; 68 firstData = true; // true for test of first serial data response 69} 70 71void loop() { 72 73 if (Serial3.available()) 74 { 75 if (firstData) { Serial.println("got serial data"); firstData = false;} 76 switch (state) 77 { 78 case IDLE: 79 ch = 0; 80 if (Serial3.read() == '+') 81 { 82 Serial.println("+"); 83 state = DELIM; 84 } 85 else 86 { 87 state = IDLE; 88 } 89 break; 90 91 case DELIM: 92 ch = 0; 93 if (Serial3.read() == '>') 94 { 95 Serial.println(">"); 96 state = READ; 97 } 98 else 99 { 100 state = IDLE; 101 } 102 break; 103 104 case READ: 105 chVal[ch++] = Serial3.read(); 106 if (ch > MAX_CHANNELS) 107 { 108 ch = 0; 109 state = DISP; 110 } 111 break; 112 113 case DISP: 114 state = IDLE; 115 pixelSum = 0; 116 for(long int i = 0; i < BNRpixels; i++) { // for all pixels within a strip 117 strip.setPixelColor(i, chVal[3*i], chVal[3*i+1], chVal[3*i+2]); // set pixel to Vixen color 118 } 119 strip.show(); 120 break; 121 } 122 } 123} 124 125void updateErrorDisplay(){ 126 127 if (serialErrorFlag) { 128 Serial.println("No Serial Data"); 129 } 130 else { 131 Serial.println("serial bus OK"); 132 } 133} 134 135void showStrip() { 136 for( long int i = 0; i < BNRpixels; i++){ 137 strip.setPixelColor(i, 0x111111); 138 } 139 strip.show(); 140} 141 142void clearStrip() { 143 for( long int i = 0; i < BNRpixels; i++){ 144 strip.setPixelColor(i, 0x000000); 145 } 146 strip.show(); 147} 148 149unsigned long int pzRGB(byte r, byte g, byte b) { 150 return (r*65536 + g*256 + b); 151} 152
Downloadable files
Single Element Controller
Single Element Controller
Bipolar Patch Board
Bipolar Patch Board
Bipolar Controller
This controller uses a PCA9955 LED driver to control 8 bipolar light strings or nets.
Bipolar Controller
Fairy Light Patch Board (single element controller patch board)
Fairy Light Patch Board (single element controller patch board)
Bipolar Patch Board
Bipolar Patch Board
Bipolar Controller
This controller uses a PCA9955 LED driver to control 8 bipolar light strings or nets.
Bipolar Controller
Single Element Controller
Single Element Controller
Fairy Light Patch Board (single element controller patch board)
Fairy Light Patch Board (single element controller patch board)
Documentation
Fairy Board Top Support (single element board)
This part is placed on top of a stack of fairy boards. It has corner holes for the standoffs and four small holes onto which protoboards can be attached. These protoboards will hold power supplies and/or USB to Serial interfaces.
Fairy Board Top Support (single element board)
Fairy Board Bottom Support (single element controller board)
Fairy Board Bottom Support (single element controller board)
Fairy Patch Board Support Side 1
This is one of two parts that together assemble to make a support structure for the fairy light patch panel.
Fairy Patch Board Support Side 1
Fairy Patch Board Support Side 2
This is a second component of the fairy light patch panel support structure.
Fairy Patch Board Support Side 2
Fairy Board Extender
This simple part is used to support a fairy board (single element board) when placed in a stack. On one side are two small holes into which screws can be driven to attach to a fairy board. On the other side are two larger holes into which standoffs are secured. Together with the two parts below and appropriately sized standoffs, a fairy board stack can be create.
Fairy Board Extender
Bipolar Patch Board Support
This part together with Fairy Patch Board Support Side 1 create a support structure for the Bipolar Patch Board.
Bipolar Patch Board Support
Bipolar Patch Board Support
This part together with Fairy Patch Board Support Side 1 create a support structure for the Bipolar Patch Board.
Bipolar Patch Board Support
Fairy Patch Board Support Side 1
This is one of two parts that together assemble to make a support structure for the fairy light patch panel.
Fairy Patch Board Support Side 1
Fairy Board Bottom Support (single element controller board)
Fairy Board Bottom Support (single element controller board)
Bipolar Driver Assembly Base Mount
This part creates a base for a stack of bipolar controllers. The part has four holes. The inner holes are used for standoffs while the outer holes are used to screw the assembled stack to a plywood base. The inner hole spacing matches the spacing on the short side of the bipolar controller boards.
Bipolar Driver Assembly Base Mount
Fairy Board Top Support (single element board)
This part is placed on top of a stack of fairy boards. It has corner holes for the standoffs and four small holes onto which protoboards can be attached. These protoboards will hold power supplies and/or USB to Serial interfaces.
Fairy Board Top Support (single element board)
Fairy Board Extender
This simple part is used to support a fairy board (single element board) when placed in a stack. On one side are two small holes into which screws can be driven to attach to a fairy board. On the other side are two larger holes into which standoffs are secured. Together with the two parts below and appropriately sized standoffs, a fairy board stack can be create.
Fairy Board Extender
Fairy Patch Board Support Side 2
This is a second component of the fairy light patch panel support structure.
Fairy Patch Board Support Side 2
Comments
Only logged in users can leave comments
zavracky
0 Followers
•0 Projects
Table of contents
Intro
1
0