Transfering photodiode reading from a Slave spectrophotometer to a remote Master

I made a simple diode array cell by mounting different colored LED's side-by-side in a drilled-out PVC block. Each LED is paired to a photodiode. So 3 LEDs (blue, green and red) are illuminated in sequence. Only one is lit at a time to prevent cross "contamination". The PD output is scaled to 1 byte and, presently, printed on the IDE monitor. I would like to send to another board by I2C.

Why? ...We got into dyeing garments and just about anything else we could catch. But often what we thought we'd get came out quite different hue/intensity, hard to explain but if you've ever done much fabric dying you'll know what i mean. The purpose of this project is to try quantifing the color intensity of dyes and mixtures of dyes so that we can have a better way of predicting final colors and intensity of the final product.

Now that it is working, I'd like to go one step further and send the data by I2C to a Master Uno located 2 meters away from the Slave.

The reason I've come here is to ask how to structure the Slave sender sketch to send the data. Here is my Mini spectrophotometer sketch of which I am unduly proud:

/*Sequentially turns on 3 different color leds mounted in a plastic cell.  Each LED burns for 1s 
 * then the output from a photodiode (digikey SHF213) mounted opposite the LED
 * is read via A1 through A3 respectively. The output is mapped to 255 (1 byte).
 * The data is sent to the serial monitor.
 */

// declare constants that won't change
const int LED476 = 9;  //i/o pin connect to 476nm LED
const int LED538 = 10;  //i/o pin connect to 538 LED
const int LED600 = 11;  //i/o pin connect to 600 LED

int bluePD = A1;    //  analog input pin A1 to 476 detector PD 
int blueIN = 0;      //  establish the variable to hold 476 readings
int greenPD = A2;  //  analog input pin A2 to 538 detector PD
int greenIN = 0;    //  variable to hold 538 readings
int redPD = A3;     //  analog input pin A3 to 600 detector PD
int redIN = 0;        //  variable to hold 600 readings

// variables that will change
int LED476STATE = LOW;
int LED538STATE = LOW;
int LED600STATE = LOW;

int sval;
int Analogpin;

void setup() {
  
 //set LED pin as output
 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  Serial.begin(9600);
 
}

void loop() {
  
Analogpin = 1;
LightLEDs(100,0,0); //476 light intensity is proportional to PWM setting 0 to 255
delay (1000);
int sens = ReadSensor();
Serial.print("476 PD = ");
Serial.println(sval);


LightLEDs(0,100,0); //540 lights
delay (1000);
Analogpin = (Analogpin + 1);
sens = ReadSensor();
Serial.print("538 PD = ");
Serial.println(sval);
LightLEDs(0,0,100); //600 lights
delay (1000);
Analogpin = (Analogpin + 1);
sens = ReadSensor();
Serial.print("600 PD = ");
Serial.println(sval);
 
}

void LightLEDs(int blue, int green, int red) {
 analogWrite(LED476, blue); analogWrite(LED538, green); analogWrite(LED600, red);
 }

int ReadSensor() {

  int i =0;

  for (i = 0; i < 3; i++){
    sval =  analogRead(Analogpin);  // read PD sensors (on analog pins 1,2 and 3 total);
    /*Serial.print("first readsensor sval = ");  //debugging prints
    Serial.println(sval);
    Serial.print("  and Analogpin = ");
    Serial.println(Analogpin);
   */
   sval = map(sval, 0, 1023, 0, 255);
    return sval;
}

}

I have read all of the 2 arduino board I2C tutorials that I could google including Mr. Gammon's. I used 2 meters of good quality signal wire to connect the SCL, SDA and GND pins together (Master is Uno R3 and Slave is Leonardo). I loaded Master/Slave examples and they worked just fine.

If you're still with me, my problem is trying to work out where to plug the Wire.write and other instructions in this existing sketch.

The Loop sequence first calls function LightLEDs and passes the PWM factors. I use PWM because I want to be able to change the incident light intensity to suit a given dye concentration. The readings will be quantified by comparison to standards

The LED burns for a full second to ensure it has "settled down". Does that seem sufficient time? In practice, using standard the readings are very consistent so I think 1 second is good.

Then the ReadSensor routine maps the analog values to 0 to 255.

Could you advise how to handle sending this data, the 3 readings to Master? I first thought to send after each reading. Now I think it would be best to save the 3 reading in an array and then send them as a group. I wonder if I need another function just for the Wire instructions.

I seem to be chasing my tail. It seems I2c likes characters so I've spent a lot of time trying to convert integers into char with the idea to revert and then back again with the Master. But others send integers via I2C... I need some guidance as to the right way to handle this task, please.

Perhaps a few lines of code to get me started would be very helpful too.

Procyan:
Only one is lit at a time to prevent cross "contamination".

It does not look like that in your code. You start them separately but then they are all lighting at the same time. And you don't start sensing them until after they are all lit.

By the way, don't put several statements on one line. The compiler won't care but it makes it hard for us poor humans.

You seem to be printing the values to the Serial Monitor. Immediately after that (or instead of it) would be a good place to send the data using I2C.

It is easier to help you if you make an attempt at the program code first.

...R
Planning and Implementing a Program

Thanks Robin,

There is no problem with the sketch running. I've been using it with calibration standards and it sequences through the 3 LEDS by changing the PWM outputs of pins 9,10, 11 by changin lightLED (100,0, 0) ...it allows me to control brightness. I can see the LEDs light up in 1s sec steps.

You said "You seem to be printing the values to the Serial Monitor. Immediately after that (or instead of it) would be a good place to send the data using I2C."

Those prints are just for debugging.

Yes I wish to send there, just where the values presently go to monitor...and I have written many tries but I'm missing something important,

My problem is to coordinate the I2C part with the readings.

I thought there should be one Master controlling a Slave cell

But that means the Master must first be told there is data to send after
the first PD reading (476)

Then the data needs to be sent/received.

Then the next part of loop runs, we read the next PD (538) and so on

but its coordinating this thats got me stumped. I don't understand how to do this with a void funtion that steps out of the Loop and sends the data...how to coordinate that with the Master so it receives each of the 3 PD readings?

Maybe I should have the spectrometer board the Master and it sends to Slave when its darn good and ready?

I would really like to get a little more handle on the logic if you can help before copying more code from others, thanks. One thing this problem has taught me that I still have a long way to go...I thought I was King because I learned what void meant :slight_smile:

Also, I don't know what you mean about multiple statements in one line?

Three statements in one line:

 analogWrite(LED476, blue); analogWrite(LED538, green); analogWrite(LED600, red);

I fixed the multiple statements and added code to send from Slave to Master that I lifted from
http://forum.arduino.cc/index.php?topic=48477

His last post has a Unsigned 7 bit sender/receiver. 7 bits doesn't sound right...shouldn't it be 8 for the 1 byte that I wish to send?
Here is my best effort so far for Slave:

// declare constants that won't change
const int LED476 = 9;  //i/o pin connect to 476 LED
const int LED538 = 10;  //i/o pin connect to 538 LED
const int LED600 = 11;  //i/o pin connect to 600 LED

int bluePD = A1; // sets analog input pin A1 to 476 detector PD
int blueIN = 0; // establish the variable to hold 476 readings
int greenPD = A2; // sets analog input pin A2 to 538 detector PD
int greenIN = 0; // establish the variable to hold 538 readings
int redPD = A3; // sets analog input pin A3 to 600 detector PD
int redIN = 0; // establish the variable to hold 600 readings

// variables that will change
int LED476STATE = LOW;
int LED538STATE = LOW;
int LED600STATE = LOW;
int sens;
int sval;
int Analogpin = 1;
#include<Wire.h>
void setup() {
  // put your setup code here, to run once:
 //set LED pin as output
 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  Serial.begin(9600);
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(Sendval); // register event
}

int byteSending = 1;     //Wayne note: these are outside braces?
int toTransfer = sval;
int Shift = toTransfer;
int mask = 0xFF;
unsigned char toSend = 0;

void loop() {
  // put your main code here, to run repeatedly:
  
Analogpin = 1;
LightLEDs(1,0,0); //476 lights
delay (1000);
int sens = ReadSensor();
Serial.print("476 PD = ");
Serial.println(sval);
Sendval(); //call the I2C send subroutine

LightLEDs(0,1,0); //540 lights
delay (1000);
Analogpin = (Analogpin + 1);
sens = ReadSensor();
Serial.print("538 PD = ");
Serial.println(sval);
LightLEDs(0,0,1); //600 lights
delay (1000);
Analogpin = (Analogpin + 1);
sens = ReadSensor();
Serial.print("600 PD = ");
Serial.println(sval);
 
}

void LightLEDs(int blue, int green, int red) {
 analogWrite(LED476, blue); 
 analogWrite(LED538, green); 
 analogWrite(LED600, red);
 }

int ReadSensor() {

  int i =0;

  for (i = 0; i < 3; i++){
    sval =  analogRead(Analogpin);  // read PD sensors (on analog pins 1,2 and 3 total);
    Serial.print("first readsensor sval = ");
    Serial.println(sval);
    Serial.print("  and Analogpin = ");
    Serial.println(Analogpin);
    
    //Analogpin = (Analogpin +1);     // index to next pin 
    
    return sval;
}

}

void Sendval(){
  //int toTransfer = sval;
//int byteSending = 1;     
int toTransfer = sval;
int Shift = toTransfer;
int mask = 0xFF;
unsigned char toSend = 0;
 if (byteSending == 1) //send packet 1
 {
   toSend = Shift & mask;
   Shift = Shift >> 8;
   Wire.write(toSend);
   byteSending = 2;
 }
 else if (byteSending == 2) //send packet 2
 {
   toSend = Shift & mask;
   Shift = Shift >> 8;
   Wire.write(toSend);
   byteSending = 3;
 }
 else if (byteSending == 3) //send packet 3
 {
   toSend = Shift & mask;
   Shift = Shift >> 8;
   Wire.write(toSend);
   byteSending = 4;
 }
 else if (byteSending == 4) //send packet 4
 {
   toSend = Shift & mask;
   Shift = Shift >> 8;
   Wire.write(toSend);
   byteSending = 1;
   //initialization for next turn
   Shift = toTransfer;
   mask = 0xFF;
   toSend = 0;
 }

}

I think this is designed to send the biggest posssible number...I only need 255 or less (just remember I forgot to map to that range...but I will do). I didn't want to mess with his code because it worked when I
tried his examples neat.

The Master is directly from the last post Unsigned 7 bit receiver:

#include <Wire.h>
// MAX INT 32767
void setup()
{
 Wire.begin();        // join i2c bus (address optional for master)
 Serial.begin(9600);  // start serial for output
}
int output = 0;

void loop()
{
 Wire.requestFrom(2, 1);   // the first byte
 while(Wire.available())
 { 
   unsigned char received = Wire.receive();
   output = received;
 }
 
 for (int i = 0 ; i < 3 ; i++) // next 3 bytes
 {
    Wire.requestFrom(2, 1);   
    while(Wire.available())
    { 
       unsigned char received = Wire.receive();
       output |= (received << 8);
    }
 }
 Serial.print(output);
 Serial.println(".");

 delay(500);
}

I flashed these to the boards and something happened....

I've attached an annotated image of the Master serial monitor. the top zeros are while I plugged in the Slave. Then the negative numbers came while the cell was in ambient light. Then I directed a torch onto the open cell and the number shifted postive...but I can't figure this out. I do see that the final "." gets transfered so there is...erm a ray of light. Also, FWIW, the Slave monitor prints show that the interrupts are being called....perhaps it is time to remove all needless Serial.prints?

Closer yes, but i am not in control...need to nut out how to coordinate Slave work reading Analogs with Masters requirement to step out of loop to wirewrite function. Please stay with me.

I've made a mistake. The Master serial output as annotated is wrong...the negative sign alternates on/off regardless of the light intensity in the cell. my mistake. Here is the output from the Slave monitor

first readsensor sval = 908
and Analogpin = 3
600 PD = 908
first readsensor sval = 893
and Analogpin = 1
476 PD = 893
first readsensor sval = 900
and Analogpin = 2
538 PD = 900
first readsensor sval = 909
and Analogpin = 3
600 PD = 909
first readsensor sval = 901
and Analogpin = 1
476 PD = 901
first readsensor sval = 900
and Analogpin = 2
538 PD = 900
first readsensor sval = 907
and Analogpin = 3
600 PD = 907
first readsensor sval = 901
and Analogpin = 1
476 PD = 901
first readsensor sval = 902
and Analogpin = 2
538 PD = 902
first readsensor sval = 910
and Analogpin = 3
600 PD = 910

Please stand by. I seem to be making progress. I will follow up soon, thanks