Issue with acquiring data from the Parallax ColorPal sensor

Good afternoon, everyone.

I am trying to use the Parallax ColorPal Sensor to acquire color saturation from different colors.

For instance, I need to first turn on the sensor's internal LED to red and acquire the data it sends, then for green, blue, white, and with the LED turned off. Each of these colors should generate an output measured by the sensor.

The problem I'm facing is that while checking the sensor's documentation, it provides some commands for control via serial communication, which I have already implemented for each color. However, I can't find a way to make the sensor send the reading during the interval of time when each color is illuminated.

Documentation https://www.mouser.com/datasheet/2/321/28380-ColorPAL-Documentation-370265.pdf

Could you help me or guide me to find a possible solution for this?

I have attached the code I am using.

#include <SoftwareSerial.h>

#define sio 10 // ColorPAL connected to pin 10
#define unused 255 // Non-existent pin # for SoftwareSerial
#define sioBaud 4800

// Set up software serials on the same pin.
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);

void setup() {
  Serial.begin(9600);
  reset();
  int dato;
}

void loop() {
  for (int i = 1; i <= 5; i++) {
    SoftwareSerial serin(sio, unused);
    SoftwareSerial serout(unused, sio);
    serout.begin(sioBaud);
    pinMode(sio, OUTPUT);
    if (i == 1) {
      serout.println("=($ R)!");
      serout.end();
      delay(500);
      reset();
      readData();
      Serial.println("Entra a rojo");
    } else if (i == 2) {
      serout.print("=(00 $ B)!");
      serout.end();
      delay(500);
      reset();
      Serial.println("Entra a azul");
    } else if (i == 3) {
      serout.print("=(00 $ G)!");
      serout.end();
      delay(500);
      reset();
      Serial.println("Entra a verde");
    } else if (i == 4) {
      serout.print("=(00 $ W)!");
      serout.end();
      delay(500);
      reset();
      Serial.println("Entra en blanco");
    } else if (i == 5) {
      serout.print("=(00 $ X)!");
      serout.end();
      delay(500);
      reset();
      Serial.println("Se apaga el sensor");
    }


  }
}

void readData() {
  char buffer[32];

  if (serin.available() > 0) {
    // Wait for a $ character, then read three 3-digit hex numbers
    buffer[0] = serin.read();
    if (buffer[0] == '$') {
      for (int i = 0; i < 3; i++) {
        while (serin.available() == 0); // Wait for next input character
        buffer[i] = serin.read();
        if (buffer[i] == '$') // Return early if $ character encountered
          return;
      }
      parseAndPrint(buffer);
      delay(10);
    }
  }
}

void reset() {
  delay(200);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  pinMode(sio, INPUT);
  while (digitalRead(sio) != HIGH);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  delay(80);
  pinMode(sio, INPUT);
  delay(200);
}

// Parse the hex data into integers
void parseAndPrint(char * data) {
  int dato;
  sscanf(data, "%3x", &dato); // Pull the R value from the data string

  char buffer[48]; // create a buffer
  sprintf(buffer, "recepcion = %4.4d ", dato); //print the values into a buffer as formatted integers
  Serial.println(buffer);
}

page9 of the datasheet you shared has an example of just that... where you not able to implement it on your arduino?

Hello Sherzaad, thank you for your response.

Indeed, that's regarding the use of the command serout.println("=(00$ m)!"); when all 3 colors are turned on.

The question I have is that if I send a command serout.println("=($ R)!"); to turn on the red, I am unable to acquire the data with that LED on.

This is where the issue lies, and it's the part I can't control.

The direct application of this sensor is to train a neural network for color recognition using this sensor. However, if I use what's mentioned on page 9, I'll only be subtracting the colors to get R, G, and B. But if I want to generate white light or turn off the LED to sense with ambient light, it doesn't generate any output data.

I have attached the example Arduino code.

/* ColorPal Sensor Example for Arduino
  Author: Martin Heermance, with some assistance from Gordon McComb
  This program drives the Parallax ColorPAL color sensor and provides
  serial RGB data in a format compatible with the PC-hosted 
  TCS230_ColorPAL_match.exe color matching program.
*/

#include <SoftwareSerial.h>

const int sio = 2;			// ColorPAL connected to pin 2
const int unused = 255; 		// Non-existant pin # for SoftwareSerial
const int sioBaud = 4800;
const int waitDelay = 200;

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

// Set up two software serials on the same pin.
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);

void setup() {
  Serial.begin(9600);
  reset();				  // Send reset to ColorPal
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.print("= (00 $ m) !"); // Loop print values, see ColorPAL documentation
  serout.end();			  // Discontinue serial port for transmitting

  serin.begin(sioBaud);	        // Set up serial port for receiving
  pinMode(sio, INPUT);
}

void loop() {
  readData();
}  

// Reset ColorPAL; see ColorPAL documentation for sequence
void reset() {
  delay(200);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  pinMode(sio, INPUT);
  while (digitalRead(sio) != HIGH);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  delay(80);
  pinMode(sio, INPUT);
  delay(waitDelay);
}

void readData() {
  char buffer[32];
  
  if (serin.available() > 0) {
    // Wait for a $ character, then read three 3 digit hex numbers
    buffer[0] = serin.read();
    if (buffer[0] == '$') {
      for(int i = 0; i < 9; i++) {
        while (serin.available() == 0);     // Wait for next input character
        buffer[i] = serin.read();
        if (buffer[i] == '$')               // Return early if $ character encountered
          return;
      }
      parseAndPrint(buffer);
      delay(10);
    }
  }
}

// Parse the hex data into integers
void parseAndPrint(char * data) {
  sscanf (data, "%3x%3x%3x", &red, &grn, &blu);
  char buffer[32];
  sprintf(buffer, "R%4.4d G%4.4d B%4.4d", red, grn, blu);
  Serial.println(buffer);
}

The demonstration video is available at the following link.

When the command serout.println("=(00$ m)!"); is used, it enables the cycle of colors, and that's when the measurement is taken. However, when another command is used as shown in the video, data acquisition no longer occurs.

in the video you are changing the 'm' to 'R' 'G' 'B' 'X' respectively.

if from the datasheet that is equivalent to ["= m !"], then in the example code to sample it shows

["= G s !"]

u omitted the 's' in your video trials, so IMHO, for example

serout.println("=(00$ m)!"); should be changed to serout.println("=(00$ G s)!");

hope that helps....

Thank you very much for your help. I've made some progress and now, when I use the correct command for direct mode, serout.print("=$Rs!");, I am able to read the sensor value only once with the selected LED color.

However, a new issue has arisen. Throughout the program, the sensor value is only read once, and if we try to write new code to change the LED color, it doesn't update anymore. I've tried closing the serial port and other methods, but I haven't been able to make it read again until the Arduino is restarted.

I've attached the modified code here:

#include <SoftwareSerial.h>

#define sio 10 // ColorPAL connected to pin 6
#define unused 255 // Non-existant pin # for SoftwareSerial
#define sioBaud 4800

// Received RGB values from ColorPAL
int red;

// Set up two software serials on the same pin.
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);

void setup() {
  Serial.begin(9600);

 
}

void loop() {
  reset();
   serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.print("=$Rs!");
    //serout.end(); // Discontinue serial port for transmitting
  pinMode(sio, INPUT);
  serin.begin(sioBaud);
  char dato[32];
  dato[0] = serin.read();

  if (dato[0] == '$') {
    for (int i = 0; i < 3; i++) {
      while (serin.available() == 0); // Wait for next input character
      dato[i] = serin.read();
      if (dato[i] == '$') // Return early if $ character encountered
        return;
    }
    parseAndPrint(dato);
    delay(10);

  }
  
}

void reset() {
  delay(200);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  pinMode(sio, INPUT);
  while (digitalRead(sio) != HIGH);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  delay(80);
  pinMode(sio, INPUT);
  delay(200);
}

// Parse the hex data into integers and print in integer and mV format
void parseAndPrint(char * data) {
  sscanf (data, "%3x", &red); // Pull the R value from the data string
  
  float redVoltage = (float)red / 255.0 * 3.3;
  int redMV = int(redVoltage * 1000);
  
  char buffer[48]; // create a buffer
  sprintf(buffer, "Valor = %4.4d (En mV: %d)", red, redMV); //print the values into a buffer as formatted integers
  Serial.println(buffer);
}

not sure about that... maybe add some delay at the end of the 'whie' loop to see if that makes any difference....

hope that helps...

Thank you for your responses.

I managed to finish it already. The issue is that all the documents available online only use the macro example where it automatically detects red, green, and blue. The example works wonderfully, but only for color detection, and even so, we are uncertain if the measurements are accurate. It's surprising that there are even theses that directly cite this example and develop complex applications assuming that color detection is correct.

The key point for developing a good sensor application is to verify the documentation. I'll try to explain it briefly:

  1. The documentation explains that color acquisition can be done by turning on a LED on the Parallax ColorPal sensor. However, this is done through direct mode, and it's not possible to extract data through buffer transmission.
  2. Verify that to enter direct mode, an 80 ms pulse is required to change the sensor's function.
  3. Do not program a sensor sequence. This is because it can be stored in EEPROM memory from 00 to 3F. This will only turn on the sensor's LED according to your needs, regardless of whether you try to extract the sensed data.
  4. On the other hand, as I mentioned initially, the documentation indicates that by using the command "00=m!" a macro is executed, which most of the examples online use, and they lack flexibility for modification.
  5. Consider that the command "=COLOR s!" will turn on the internal LED, and then you can acquire the data sensed by ColorPal.

Here, I'm adding remedial programming to turn on the ColorPAL LED for subsequent data acquisition.

As an additional comment, I used Arduino temporarily due to its ease of programming. However, it's important to consider that the sensor uses the same pin for both input and output. Therefore, when using the SoftwareSerial function, you need to set it up for both input and output. Also, don't forget that when you want to send a command to the sensor to turn on any color, you should follow these steps:

  1. Set the baud rate of the transmission port.
  2. Set the pin as an output.
  3. Send the color command.
  4. End the serial communication of the transmission port.
  5. Enable the baud rate of the receiving port.
  6. Set the pin as an input.
  7. Commands for receiving the color (consider that it works with a buffer, and data is received in hexadecimal; I recommend conducting separate tests to become familiar with the sensor).
  8. Print the sensed data.
  9. Close the receiving port.

Thoroughly verify each part of the sensor.

I hope this code is helpful to you, and you can enhance it according to your needs. Later, I will program it on a ZEDBOARD Zynq 7 card. This will help revive the use of the sensor, which is quite old but has significant potential.

Do not forget that the "$" symbol is used to indicate when the sensor is receiving an instruction.

The reason I placed it in the "void setup" is that I only need the data processing code to run once.

#include <SoftwareSerial.h>
const int sio = 10; // ColorPAL connected to pin 2
const int unused = 255; // Non-existant pin # for SoftwareSerial
const int sioBaud = 4800;
const int waitDelay = 200;
int red;
 
SoftwareSerial serin(sio, unused);
SoftwareSerial serout(unused, sio);
void setup() {
 Serial.begin(9600);
rojo();
 delay(1000);
 verde();
 delay(1000);
 azul();
 delay(1000);
 blanco();
 delay(1000);
 sincolor();
 delay(1000);
}

void loop() 
{

  
 
 
}

void rojo()
{
  
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.println("=$Rs!");
  serout.end(); // Discontinue serial port for transmitting
  serin.begin(sioBaud); // Set up serial port for receiving
  pinMode(sio, INPUT);
   reset();
  ////// Comienza la lectura del sensor
 char buffer[32];
  char datojunto[10];
  if (serin.available()>0)
  {
    buffer[0]=serin.read();
    
    if (buffer[0] == '$')
      {
        
      for(int i = 0; i <=3; i++) {
      while (serin.available() == 0); // Wait for next input character
      buffer[i] = serin.read();
     
      if (buffer[i] == '$') // Return early if $ character encountered
      return;
      }
         
  }
        Serial.print ("Rojo: ");
       parseAndPrint(buffer);
       delay(10);
  }
 
  
  serin.end();
}
void verde()
{
  
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.println("=$Gs!");
  serout.end(); // Discontinue serial port for transmitting
  serin.begin(sioBaud); // Set up serial port for receiving
  pinMode(sio, INPUT);
   reset();
  ////// Comienza la lectura del sensor
 char buffer[32];
  char datojunto[10];
  if (serin.available()>0)
  {
    buffer[0]=serin.read();
    
    if (buffer[0] == '$')
      {
        
      for(int i = 0; i <=3; i++) {
      while (serin.available() == 0); // Wait for next input character
      buffer[i] = serin.read();
     
      if (buffer[i] == '$') // Return early if $ character encountered
      return;
      }
         
  }
  Serial.print ("Verde :");
       parseAndPrint(buffer);
       delay(10);
  }
 
  
  serin.end();
}
void azul()
{
  
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.println("=$Bs!");
  serout.end(); // Discontinue serial port for transmitting
  serin.begin(sioBaud); // Set up serial port for receiving
  pinMode(sio, INPUT);
   reset();
  ////// Comienza la lectura del sensor
 char buffer[32];
  char datojunto[10];
  if (serin.available()>0)
  {
    buffer[0]=serin.read();
    
    if (buffer[0] == '$')
      {
        
      for(int i = 0; i <=3; i++) {
      while (serin.available() == 0); // Wait for next input character
      buffer[i] = serin.read();
     
      if (buffer[i] == '$') // Return early if $ character encountered
      return;
      }
         
  }
       Serial.print ("Azul:");
       parseAndPrint(buffer);
       delay(10);
  }
 
  
  serin.end();
}

void blanco()
{
  
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.println("=$Ws!");
  serout.end(); // Discontinue serial port for transmitting
  serin.begin(sioBaud); // Set up serial port for receiving
  pinMode(sio, INPUT);
   reset();
  ////// Comienza la lectura del sensor
 char buffer[32];
  char datojunto[10];
  if (serin.available()>0)
  {
    buffer[0]=serin.read();
    
    if (buffer[0] == '$')
      {
        
      for(int i = 0; i <=3; i++) {
      while (serin.available() == 0); // Wait for next input character
      buffer[i] = serin.read();
     
      if (buffer[i] == '$') // Return early if $ character encountered
      return;
      }
         
  }
       Serial.print ("Blanco:");
       parseAndPrint(buffer);
       delay(10);
  }
 
  
  serin.end();
}

void sincolor()
{
  
  serout.begin(sioBaud);
  pinMode(sio, OUTPUT);
  serout.println("=$Xs!");
  serout.end(); // Discontinue serial port for transmitting
  serin.begin(sioBaud); // Set up serial port for receiving
  pinMode(sio, INPUT);
   reset();
  ////// Comienza la lectura del sensor
 char buffer[32];
  char datojunto[10];
  if (serin.available()>0)
  {
    buffer[0]=serin.read();
    
    if (buffer[0] == '$')
      {
        
      for(int i = 0; i <=3; i++) {
      while (serin.available() == 0); // Wait for next input character
      buffer[i] = serin.read();
     
      if (buffer[i] == '$') // Return early if $ character encountered
      return;
      }
         
  }
       Serial.print ("Apagado:");
       parseAndPrint(buffer);
       delay(10);
  }
 
  
  serin.end();
}





void reset() {
  delay(200);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  pinMode(sio, INPUT);
  while (digitalRead(sio) != HIGH);
  pinMode(sio, OUTPUT);
  digitalWrite(sio, LOW);
  delay(80);
  pinMode(sio, INPUT);
  delay(waitDelay);
}

void parseAndPrint(char * data) {
sscanf (data, "%3x%3x%3x", &red);
char buffer[32];
sprintf(buffer, "valor= %4.4d", red);
Serial.println(buffer);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.