Help with code to interact with a photo transistor analog input values

I am an inexperienced coder but am trying to learn. I'm sure my code is awkward but I would really appreciate any help. I hope I am asking the question properly.

I am working on a fun Arduino project for cosplay which has an LCD screen, a mp3 player and an old battery test meter attached. The mp3 player works fine. Currently the text on the LCD display and the needle on the meter are controlled by the analog input from a photo transistor. The code below is working ok but the issue I am having is that when there are different lighting conditions in the room, the values from the photo transistor are no longer 1024 and 0.

I am trying to map the actual values to say "2.8" (I need to include one decimal place) on the LCD screen as the top end and 0.0 as the low value (that's what I need for the prop to say on the screen). What I really need is for the code to do is take the analog values from the photo resistor (edited, should say photo transistor) that are greater or equal to 500 as the top number and less than 20 as the lower so it will work in varying light conditions when I surreptitiously interact with the photo resistor.

#include <LiquidCrystal.h> // include the library codes
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <JQ6500_Serial.h>
#define phototransistorPin A1
#define meterPin 5

SoftwareSerial mySerial(3, 2);  // talk to mp3 player
JQ6500_Serial mp3(mySerial);
LiquidCrystal lcd(13, 12, 11, 10, 9, 8); // initialize the library with the numbers of the interface pins

void setup() 
{
  delay(2000); // allow time to cover the trigger with finger.
  mySerial.begin(9600);
  mp3.reset();
  mp3.setVolume(20); // sets mp3 player's volume
  mp3.setLoopMode(MP3_LOOP_ALL); // plays the sound effect on loop
  mp3.play();
  lcd.begin(16, 2); // set up the LCD's number of columns and rows
}

void loop() 
{
  float Percentage = (analogRead(phototransistorPin) / 1024.0) * 2.9;
  float PercentageRound = Percentage;
  int meterPower = (analogRead(phototransistorPin) / 1024.0) * 255; //the power range of the meter
  lcd.setCursor(5, 0); // set the cursor to column 5, line 0
  lcd.print(PercentageRound, 1); //print value to 1 decimal place on LCD Display
  lcd.setCursor(9, 0); // set the cursor to column 9, line 0
  lcd.print("MJ");
  analogWrite(meterPin, meterPower);
}

You are talking about photo transistor and at other places you write photo-resistor

photo-transistor / photo-resistor are completely different devices.
Simply connecting them to an analog-IO-pin does not work good. The minimum would be to have a voltage-divider. The value of the second resistor highly depends on the type of photo-transistor / photo-resistor.

Some photo-resistors have a pretty limited range of light where they change resistance.
So the minimum is that you provide a datasheet of exact that component that you are using.

If you can't provide a datasheet post a picture where the device can be seen in detail
to at least decide photo-transistor or photo-resistor.

If no datasheet is available in both cases analog measurings must be done to analyse the characteristics of the component.

The microcontroller-world is not super-standardised like USB-devices. You have to take care of more details than just "does the plug fit into the socket".

You should provide a pure functional description.
pure-functional means no code no numbers just normal words that explain the principle

with words like "environment with low light-level" "environment with high light-level"
etc.

best regards Stefan

yes

a photo transistor is more than likely to be for a specific wavelength (e.g. infrared, IR) paired with an LED that emits that wavelength.

the output of a phototransistor will be exponentially proportional to the intensity of the light it receives, but incandescent lights and sunlight contain a lot of IR

the resistance of a photo resistor changes more proportional to the light. it would typically be put in series with another fixed resistor and analogRead() can be used to measure the change in resistance

Thank you for taking the time to reply. It is a phototransistor. It came in a kit with a book about learning Arduino so it I didn't receive a datasheet with it. Below is how I have it hooked up to my Pro Mini (5V).


phototransistor

Usually NPN-transistors have the Emitter connected to ground.
So a typical NPN-circuit looks like this
grafik

position of phototransistor and resistor changed.

This means the resistor would work as a pull-up-resistor.

As long as the foto-transistor receives no light the transistor is non-conductand.
In this case the analog input senses Vcc. The resistor "pull-up" the analog pin to Vcc

If the foto-transistor receives light the transistor becomes conductant.
The voltage "drops" across the resistor and the analog input will sense (almost) groundNot exactly 0,00 V maybe 0,1V.

best regards Stefan

Thank you very Stefan much for the correction. Would you happen to know the answer to the initial code question?

I don't understand this explanation.
mapping values to a single number?
values are no longer 1024 or 0

what???

please write a functional description what you want to do

or alternativly
clearly name which variable has values 1024 or 0 or different ones
and explain what "top numbers" and what "lower numbers" are

best regards Stefan

Hi Stefan, sorry, I am inexperienced as is obvious I assume. I will try to write a better explanation.

So if you use Stefan's schematics, with no light, you read 1023 at A1 (not 1024). With a lot of light, A1 drops to near zero.

So 1023 would map to 0.0 and 0 to 2.8? Map it first to an integer between 0 and 28. Then divide it into a float and you'll get one decimal place.

Hi, I appreciate the help very much, I will try to explain properly. The code shown below and my circuit work the way I initially wanted. When a bright light hits the photo transistor, the LCD displays "2.8" which is the number I need for the prop. When there is no light, it displays "0.0". The problem is that the ambient lighting in the room varies. So I would like to use upper and lower limits that are anything greater than or equal to about 500 to (instead of 1023) prints "2.8" and anything less than or equal to 30 prints "0.0". I need the one decimal place.

The mp3 part of the code works fine, please ignore.

#include <LiquidCrystal.h> // include the library codes
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <JQ6500_Serial.h>
#define phototransistorPin A1
#define meterPin 5

SoftwareSerial mySerial(3, 2);  // talk to mp3 player
JQ6500_Serial mp3(mySerial);
LiquidCrystal lcd(13, 12, 11, 10, 9, 8); // initialize the library with the numbers of the interface pins

void setup() 
{
  delay(2000); // allow time to cover the trigger with finger.
  mySerial.begin(9600);
  mp3.reset();
  mp3.setVolume(20); // sets mp3 player's volume
  mp3.setLoopMode(MP3_LOOP_ALL); // plays the sound effect on loop
  mp3.play();
  lcd.begin(16, 2); // set up the LCD's number of columns and rows
}

void loop() 
{
  float Percentage = (analogRead(phototransistorPin) / 1024.0) * 2.8;
  float PercentageRound = Percentage;
  int meterPower = (analogRead(phototransistorPin) / 1024.0) * 255; //the power range of the meter
  lcd.setCursor(5, 0); // set the cursor to column 5, line 0
  lcd.print(PercentageRound, 1); //print value to 1 decimal place on LCD Display
  lcd.setCursor(9, 0); // set the cursor to column 9, line 0
  lcd.print("MJ");
  analogWrite(meterPin, meterPower);
}
void loop() 
{
  int reading = analogRead(phototransistorPin);
  int display = map(constrain(reading, 30, 500), 30, 500, 0, 280);
  int meterPower = reading / 4; //the power range of the meter
  lcd.setCursor(5, 0); // set the cursor to column 5, line 0
  lcd.print(display/10);
  lcd.print(".");
  lcd.print(display%10); //print value to 1 decimal place on LCD Display
  lcd.setCursor(9, 0); // set the cursor to column 9, line 0
  lcd.print("MJ");
  analogWrite(meterPin, meterPower);
}

Thank you very much Paul, I will try it. :grinning:

If it does not work, please describe in detail what happens and what you wanted to happen.

If it works, please try to understand the changes I made and ask about any detail you do not understand.

Also I just realised I made an error and corrected it, so please copy it again.

More improvements!

void loop() 
{
  static int lastDisplay = -1;
  int reading = analogRead(phototransistorPin);
  int display = map(constrain(reading, 30, 500), 30, 500, 0, 280);
  int meterPower = reading / 4; //the power range of the meter

  if (display != lastDisplay) {
    lastDisplay = display;
    char buffer[21];
    sprintf(buffer, "%2d.%1dMJ", display/10, display%10);
    lcd.setCursor(5, 0); // set the cursor to column 5, line 0
    lcd.print(buffer);
  }
  analogWrite(meterPin, meterPower);
}

Hi Paul, thank you for your efforts.

I loaded it up and got the following results. It registered 28.0 (not 2.8) on the LCD display when exposed to light. When covered, it displayed numbers close to zero in rapid succession without stopping. Unfortunately, about 15 seconds into the test, the whole thing stopped working, even the mp3 sound stopped. Fortunately, I when I reloaded my previous code and it works again.

Hello,

I finally solved the problem, I am posting the code in case it helps anyone else with a similar issue.

Thank you to all who took time to help. :grinning:

#include <LiquidCrystal.h> // include the library codes
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <JQ6500_Serial.h>
#define phototransistorPin A1
#define meterPin 5

SoftwareSerial mySerial(3, 2);  // talk to mp3 player
JQ6500_Serial mp3(mySerial);
LiquidCrystal lcd(13, 12, 11, 10, 9, 8); // initialize the library with the numbers of the interface pins

void setup() 
{
  delay(2000); // allow time to cover the trigger with finger.
  mySerial.begin(9600);
  mp3.reset();
  mp3.setVolume(20); // sets mp3 player's volume
  mp3.setLoopMode(MP3_LOOP_ALL); // plays the sound effect on loop
  mp3.play();
  lcd.begin(16, 2); // set up the LCD's number of columns and rows
}

void loop() 
{
  float pinVal = analogRead(phototransistorPin);
  float Percentage = (pinVal / 1024.0) * 2.8;
  if (pinVal < 30) Percentage = 0;
  if (pinVal > 500) Percentage = 2.8;

  int meterPower = (analogRead(phototransistorPin) / 1024.0) * 255; //the power range of the meter
  lcd.setCursor(5, 0); // set the cursor to column 5, line 0
  lcd.print(Percentage, 1); //print value to 1 decimal place on LCD Display
  lcd.setCursor(9, 0); // set the cursor to column 9, line 0
  lcd.print("MJ");
  analogWrite(meterPin, meterPower);
}

My bad, the 280 figure in my suggested code should have been 28.

How is that different from what you wanted it to do, or what your code above does?

There is nothing in my suggested code that would cause that to happen. Was the crash consistent, repeatable? Otherwise the crash sounds like an unrelated coincidence.

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