Shop OBEX P1 Docs P2 Docs Learn Events
ColorPAL and NeoPixels — Parallax Forums

ColorPAL and NeoPixels

TheZaneTheZane Posts: 2
edited 2015-11-23 21:44 in Accessories
I am designing a lighting rig that will light sculptural paintings from two points, one of each side. The idea is to have a Parallax ColorPAL scan colored objects that the viewer chooses and then send the data to the two points. So scan A will go to Point A and Scan B will go to Point B. For now I will have the selected colors be cast until new inputs are made. I imagine this working like a teeter totter. This is the initial end goal. Maybe later I will add a few idling animations or patterns.

This is my first major project involving Arduino and coding for that matter.

So I have been trying to learn how to merge sketches and have successfully made this one from two others. One is a test for NeoPixels that drives the LED in single colors and the other is a basic adapted example for the ColorPAL for Arduino boards that creates data readings in the form of three digit hex for Red Green and Blue:
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#include <SoftwareSerial.h>

// I/O Pin definitions.
#define PIN 6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      8

int delayval = 0; // delay for half a second

const int sio = 13;
const int unused = 255; // non-existant pin value
const int sioBaud = 4800;

// Received RGB values from ColorPAL.
int red;
int grn;
int blu;

// Set up two software serials on the same pin.
// This mimic PBASIC's serin and serout function.
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

// -----[ Initialization ]--------------------------------------------------
void setup() {
  // initialize serial communication:
  Serial.begin(9600);

  // Reset the ColorPAL and enter direct command mode.
  reset();

  // Program ColorPAL to send $ then color data.
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.print("= (00 $ m) !"); // buffer commmands, loop print $ and data end_loop now execute
  // serout is unused from this point forwards
  serout.end();

  // Now turn the sio pin into an input to read data from the color pal.
  serin.begin(sioBaud);
  pinMode(sio, INPUT);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

// -----[ Program Code ]----------------------------------------------------
void loop() {
  readData();

  // For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.

  for (int i = 0; i < NUMPIXELS; i++) {

    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    strip.setPixelColor(i, strip.Color(red, grn, blu)); // Moderately bright green color.

    strip.show(); // This sends the updated pixel color to the hardware.

    delay(delayval); // Delay for a period of time (in milliseconds).

  }
}

// -----[ Subroutines ]-----------------------------------------------------
// reset: Sends a long break to reset ColorPAL and enter direct command mode.
void reset()
{
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);  // Pull sio low to eliminate any residual charge.
  pinMode(sio, INPUT);     // Return pin to input.
  while (digitalRead(sio) != HIGH); // Wait for pin to be pulled high by ColorPAL.
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);  // Pull pin low.
  delay(80);               // Keep low for 80ms to enter Direct mode.
  pinMode(sio, INPUT);     // Return pin to input.
  delay(10);               // Pause another 10ms
}

// -----[ Read Data ]-------------------------------------------------------
void readData()
{
  char buffer[32];

  if (serin.available() > 0)
  {
    // Wait for a $ and then read three 3 digit hex numbers
    buffer[0] = serin.read();
    if (buffer[0] == '$')
    {
      for (int i = 0; i < 9; i++)
      {
        // Wait for the next input character.
        while (serin.available() == 0);
        buffer[i] = serin.read();

        // every so often the data terminates early.  If this happens return
        if (buffer[i] == '$')
          return;
      }
      parseAndPrint(buffer);
      delay(10);
    }
  }
}

void parseAndPrint(char * data)
{
  // parse the hex data into integers.
  sscanf (data, "%3x%3x%3x", &red, &grn, &blu);

  // format using the format expected by the windows program and output it.
  char buffer[32];
  sprintf(buffer, "R%4.4d G%4.4d B%4.4d", red, grn, blu);
  Serial.println(buffer);
}

Right now the sensor data changes the NeoPixel strip to the correct color based on the hex data created however, this action is erratic and is not adhering to a pattern of time. Along with this, the sensor is creating false readings in the dark. Without the line of code that controls the LEDs the sensor puts out data readings in a correct constant flow (I am assuming at a rate of 10ms?). At this point my thoughts on solutions for the current code would be to change the rate at which the sensor takes in data to a slower rate. I am assuming that the Arduino is having trouble bouncing between read sensor and light LED and is tripping on itself. At some point I realize that I will have to create code that reads color inputs and directs those readings to two separate iso outputs. Not sure where to go at this point.

Basically I am just looking to see if I am on the right track or if there are any suggestions on how to accomplish this project.

Here are the two source sketches that I am pulling from:

ColorPAL
/* ColorPal Sensor Example
 * Author.... Martin Heermance based upon Phil Pilgrim's PBASIC example
 * with some assistance from Gordon McComb.
 * This program drives the Parallax ColorPAL color sensor and provides
 * serial RGB data to the PC-hosted TCS230_ColorPAL_match.exe color
 * matching program.
 */

#include <SoftwareSerial.h>

// I/O Pin definitions.
const int sio = 13;
const int unused = 255; // non-existant pin value
const int sioBaud = 4800;

// Received RGB values from ColorPAL.
int red;
int grn;
int blu;

// Set up two software serials on the same pin.
// This mimic PBASIC's serin and serout function.
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);

// -----[ Initialization ]--------------------------------------------------
void setup()
{
  // initialize serial communication:
  Serial.begin(9600);
 
  // Reset the ColorPAL and enter direct command mode.
  reset();
 
  // Program ColorPAL to send $ then color data.
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.print("= (00 $ m) !"); // buffer commmands, loop print $ and data end_loop now execute
  // serout is unused from this point forwards
  serout.end();

  // Now turn the sio pin into an input to read data from the color pal.
  serin.begin(sioBaud);
  pinMode(sio, INPUT);
}

// -----[ Program Code ]----------------------------------------------------
// SERIN sio, baud, [WAIT("$"), HEX3 red, HEX3 grn, HEX3 blu] ' Receive RGB data back.
void loop()
{
  readData();
} 

// -----[ Subroutines ]-----------------------------------------------------

// reset: Sends a long break to reset ColorPAL and enter direct command mode.
void reset()
{
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);  // Pull sio low to eliminate any residual charge.
  pinMode(sio, INPUT);     // Return pin to input.
  while (digitalRead(sio) != HIGH); // Wait for pin to be pulled high by ColorPAL.
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);  // Pull pin low.
  delay(80);               // Keep low for 80ms to enter Direct mode.
  pinMode(sio, INPUT);     // Return pin to input.
  delay(10);               // Pause another 10ms
}

void readData()
{
  char buffer[32];
 
  if (serin.available() > 0)
  {
    // Wait for a $ and then read three 3 digit hex numbers
    buffer[0] = serin.read();
    if (buffer[0] == '$')
    {
      for(int i = 0; i < 9; i++)
      {
        // Wait for the next input character.
        while (serin.available() == 0);     
        buffer[i] = serin.read();

        // every so often the data terminates early.  If this happens return
        if (buffer[i] == '$')
          return;
      }
      parseAndPrint(buffer);
      delay(10);
    }
  }
}

void parseAndPrint(char * data)
{
  // parse the hex data into integers.
  sscanf (data, "%3x%3x%3x", &red, &grn, &blu);

  // format using the format expected by the windows program and output it.
  char buffer[32];
  sprintf(buffer, "R%4.4d G%4.4d B%4.4d", red, grn, blu);
  Serial.println(buffer);
}

NeoPixels
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      16

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int delayval = 500; // delay for half a second

void setup() {
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
}

void loop() {

  // For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.

  for(int i=0;i<NUMPIXELS;i++){

    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(0,150,0)); // Moderately bright green color.

    pixels.show(); // This sends the updated pixel color to the hardware.

    delay(delayval); // Delay for a period of time (in milliseconds).

  }
}

Also here are links to the Libraries
https://www.parallax.com/product/28380
https://github.com/adafruit/Adafruit_NeoPixel

The ColorPAL code is the way it is because of Martin_H. He was adapting the program to arduino to run ColorPAL Color Matching Program which only ran with .bs files. See his journey here if you want.
http://forums.parallax.com/discussion/138612/colorpal-arduino-problem
Also, thanks Martin_H for your work. It brought the sensor to life a few days ago.

The original code for the ColorPAL never yielded results but here it is: http://learn.parallax.com/colorpal-arduino-demo

Comments

  • Just so people are aware, I'm also trying out the Arduino ColorPAL & NeoPixel code.

    Here's the Arduino forum post on the topic.
  • Thanks Duane. I should have made mention.
  • My attempt to combine the ColorPAL and NeoPixels on the Arduino didn't work very well (that's an understatement).

    I suspect something with the NeoPixel driver and the soft serial conflicted.

    I pulled out a Propeller, and the two drivers went together without any issue.

    Here's the Propeller code.
    CON              
                                                               
      _CLKMODE = XTAL1 + PLL16X                              
      _XINFREQ = 5_000_000
    
      PST_BAUD = 9_600
      MAX_COLOR = 255
      DEFAULT_BRIGHTNESS = MAX_COLOR / 3  ' Use 255 for maximum brightness.
      NEOPIXEL_PIN = 15
      COLORPAL_PIN = 0
            
      LEDS_IN_RING = 10 '16
      RINGS_IN_USE = 1 '3
      TOTAL_LEDS = LEDS_IN_RING * RINGS_IN_USE
    
      MAX_LED_INDEX = TOTAL_LEDS - 1
           
    VAR
    
      long red, green, blue
      
    OBJ
    
      Strip : "jm_ws2812"
      ColorPal  : "ColorPAL"                 
      Pst : "Parallax Serial Terminal"
                       
    PUB Setup 
        
      Strip.Start(NEOPIXEL_PIN, TOTAL_LEDS)
      ColorPal.Start(COLORPAL_PIN)
      Pst.Start(PST_BAUD)
    
      Pst.Str(string(11, 13, "NeoPal"))  
      Pst.Str(string(11, 13, "NeoPixels Controlled by a ColorPAL"))
    
      MainLoop
      
    PUB MainLoop | maxColor, fullBrightnessRed, fullBrightnessGreen, fullBrightnessBlue
    
      repeat
        longmove(@red, ColorPal.GetRGB, 3)    'Get color values.
        if red => green and red => blue
          maxColor := red
        elseif green => red and green => blue
          maxColor := green
        else
          maxColor := blue
           
        Pst.Str(string(11, 13, "R = $"))          
        Pst.Hex(red, 4)
        Pst.Str(string(", G = $"))
        Pst.Hex(green, 4)
        Pst.Str(string(", B = $"))
        Pst.Hex(blue, 4)
        Pst.Str(string(", maxColor = $"))
        Pst.Hex(maxColor, 4)
        fullBrightnessRed := red * MAX_COLOR / maxColor
        fullBrightnessGreen := green * MAX_COLOR / maxColor
        fullBrightnessBlue := blue * MAX_COLOR / maxColor
        Pst.Str(string(", full R = $"))          
        Pst.Hex(fullBrightnessRed, 4)
        Pst.Str(string(", G = $"))
        Pst.Hex(fullBrightnessGreen, 4)
        Pst.Str(string(", B = $"))
        Pst.Hex(fullBrightnessBlue, 4)
        {Strip.Fill(0, last, Strip.Colorx(fullBrightnessRed, fullBrightnessGreen, {
        } fullBrightnessBlue, DEFAULT_BRIGHTNESS))  } 'use "Fill" method to partial fill strip
        Strip.Set_all(Strip.Colorx(fullBrightnessRed, fullBrightnessGreen, fullBrightnessBlue, {
        } DEFAULT_BRIGHTNESS))
    

    I bet if the ColorPAL were moved to the hardware serial on the Arduino, it wouldn't conflict with NeoPixel driver.

    I think the conflict could also be solved by using a Mega. The Mega's additional hardware serial shouldn't conflict with the NeoPixel driver.
Sign In or Register to comment.