8. Chapter Photoresistor & LED

Earlier we learned how to use ADC and PWM. In this chapter, we learn to control the brightness of an LED by using a potentiometer.

8.1. Project Soft Light

In this project, we will make a soft light. We will use an ADC Module to read ADC values of a potentiometer and map it to duty cycle ratio of the PWM used to control the brightness of an LED. Then you can change the brightness of an LED by adjusting the potentiometer.

8.1.1. Component List

  1. Raspberry Pi (with 40 GPIO) x1

  2. GPIO Extension Board & Ribbon Cable x1

  3. Breadboard x1

Rotary potentiometer x1

Rotary-potentiometer

Resistor 10kΩ x2

Resistor-10kΩ

ADC module x1

ADC-module-2

LED x1

red-led

Jumper Wire M/M x15

jumper-wire

8.1.2. Circuit with ADS7830

Schematic diagram

ADS7830-Schematic-2

Hardware connection. If you need any support,please feel free to contact us via:

support@freenove.com

ADS7830-fritizing-2

8.1.3. Sketch

In this project, we will change the brightness of the LED based on the light intensity received by the photoresistor.

8.1.3.1. Sketch_08_Nightlamp

First, enter where the project is located:

$ cd ~/Freenove_Kit/Pi4j/Sketches/Sketch_08_Nightlamp
../../../_images/java_nightlamp.png

Enter the command to run the code.

$ jbang Nightlamp.java
../../../_images/java_nightlamp_run.png

When the code is running, use light to illuminate the photosensitive module, or cover the photosensitive module with your hand, and you can observe that the brightness of the LED on the board changes accordingly.

../../../_images/ADS7830-fritizing-2.png

On the terminal, you can see the ADC and voltage values of the photoresistor are printed.

../../../_images/java_nightlamp_mes.png

Press Ctrl+C to exit the program.

../../../_images/java_nightlamp_exit.png

You can open the code with Geany with the following command to view and edit it.

$ geany Nightlamp.java

Click the icon to run the code.

../../../_images/java08_00.png

If the code fails to run, please check Geany Configuration.

The following is program code:

  1///usr/bin/env jbang "$0" "$@" ; exit $?  
  2
  3//DEPS org.slf4j:slf4j-api:2.0.12  
  4//DEPS org.slf4j:slf4j-simple:2.0.12  
  5//DEPS com.pi4j:pi4j-core:2.6.0  
  6//DEPS com.pi4j:pi4j-plugin-raspberrypi:2.6.0  
  7//DEPS com.pi4j:pi4j-plugin-gpiod:2.6.0  
  8//DEPS com.pi4j:pi4j-plugin-linuxfs:2.6.0  
  9
 10import com.pi4j.Pi4J;  
 11import com.pi4j.context.Context;  
 12import com.pi4j.io.i2c.I2C;  
 13import com.pi4j.io.i2c.I2CConfig;  
 14import com.pi4j.io.i2c.I2CProvider;  
 15import com.pi4j.util.Console;  
 16import com.pi4j.io.gpio.digital.DigitalOutput;    
 17import java.util.HashMap;  
 18import java.util.Map;  
 19
 20class PWMController implements Runnable {  
 21    private DigitalOutput pwm;  
 22    private int pwmFrequency;
 23    private double pwmDutyCycle;
 24    private boolean running = true;  
 25    private long period;  
 26    private long highTime;  
 27    private long lowTime;  
 28
 29    public PWMController(DigitalOutput pwm) {  
 30        this.pwm = pwm;  
 31        this.pwmFrequency = 1000;
 32        this.pwmDutyCycle = 0.5;
 33        this.period = (int) (1000000 / pwmFrequency);  
 34        this.highTime = (int) (period * pwmDutyCycle);  
 35        this.lowTime = (int) (period - highTime);  
 36    }  
 37
 38    @Override  
 39    public void run() {  
 40        while (running) {    
 41            if(highTime!=0){
 42                pwm.high();  
 43                delayUs(highTime); 
 44            }           
 45            if(lowTime!=0){
 46                pwm.low();  
 47                delayUs(lowTime);  
 48            }
 49        }  
 50    }  
 51
 52    public void setPwmFrequency(int frequency) {  
 53        if(frequency!=0){
 54            this.pwmFrequency = frequency;
 55            this.period = (int) (1000000 / pwmFrequency);  
 56            this.highTime = (int) (period * pwmDutyCycle);  
 57            this.lowTime = (int) (period - highTime); 
 58        }
 59        else{
 60            this.pwmFrequency = 0;
 61            this.period = (int) (1000);  
 62            this.highTime = (int) (0);  
 63            this.lowTime = (int) (period - highTime); 
 64        }
 65    }  
 66
 67    public void setPwmDutyCycle(double dutyCycle) {  
 68        this.pwmDutyCycle = dutyCycle;
 69        this.highTime = (int) (period * pwmDutyCycle);  
 70        this.lowTime = (int) (period - highTime); 
 71    } 
 72
 73    private void delayUs(long us) {  
 74        long startTime = System.nanoTime();  
 75        long endTime = startTime + (us * 1000);  
 76        while (System.nanoTime() < endTime) {  
 77        }  
 78    }  
 79
 80    public void requestStop() {  
 81        running = false;  
 82    }  
 83}
 84
 85class ADCDevice {  
 86    private final I2C adcChip;  
 87    private final int adcChipAddr;  
 88
 89    public ADCDevice(Context pi4j, I2CProvider provider, int adcChipAddr) throws Exception {  
 90        this.adcChipAddr = adcChipAddr;  
 91        I2CConfig i2cConfig = I2C.newConfigBuilder(pi4j).id("ADCDevice").bus(1).device(adcChipAddr).build();  
 92        this.adcChip = provider.create(i2cConfig);  
 93    }  
 94
 95    public boolean detectI2C() throws Exception {  
 96        try {  
 97            adcChip.write(0);  
 98            byte[] data = new byte[1];  
 99            int bytesRead = adcChip.read(data, 0, 1);  
100            return bytesRead == 1;  
101        } catch (Exception e) {  
102            return false;  
103        }  
104    }  
105
106    public int analogRead(int chn) {  
107        byte command = (byte) (0x84 | (((chn << 2 | chn >> 1) & 0x07) << 4));  
108        adcChip.write(command);  
109        byte[] data = new byte[1];  
110        int bytesRead = adcChip.read(data, 0, 1);  
111        if (bytesRead == 1) {  
112            int adcValue = data[0] & 0xFF;  
113            return adcValue;  
114        } else {  
115            return -1;  
116        }  
117    }  
118}
119
120public class Nightlamp{
121    private static int   LED_PIN = 17;
122    private static int   ADC_CHIP_ADDR = 0x4B; 
123    private static int   ADC_CHANNEL = 0;
124
125    private static final Context pi4j = Pi4J.newAutoContext();  
126    private static final Map<Integer, PWMController> pwmControllers = new HashMap<>();  
127
128    public static void setPwmConfig(int pin) throws Exception {  
129        DigitalOutput pwm = pi4j.dout().create(pin);  
130        PWMController pwmController = new PWMController(pwm);  
131        Thread pwmThread = new Thread(pwmController, "PWM Controller " + pin);  
132        pwmControllers.put(pin, pwmController);  
133        pwmThread.start();  
134        Runtime.getRuntime().addShutdownHook(new Thread(() -> {  
135            pwmController.requestStop();  
136            try {  
137                pwmThread.join();  
138            } catch (InterruptedException e) {  
139                Thread.currentThread().interrupt();  
140            }  
141        }));  
142    } 
143
144    public static void myPrintln(String format, Object... args) {    
145        Console console = new Console();  
146        console.println(String.format("\u001B[32m" + format + "\u001B[0m", args));   
147    }
148
149    public static void main(String[] args) throws Exception {  
150        Context pi4j = Pi4J.newAutoContext();  
151        I2CProvider i2CProvider = pi4j.provider("linuxfs-i2c");
152        setPwmConfig(LED_PIN);  
153        PWMController led = pwmControllers.get(LED_PIN); 
154
155        try {  
156            ADCDevice adc = new ADCDevice(pi4j, i2CProvider, ADC_CHIP_ADDR);  
157            if (adc.detectI2C()) {  
158                while (true) {  
159                    int adcValue = adc.analogRead(ADC_CHANNEL);  
160                    if (adcValue != -1) {  
161                        led.setPwmDutyCycle(((double)adcValue/255.0));
162                        double voltage = (double)adcValue / 255.0 * 5.0;
163                        myPrintln("ADC value:%d, Voltage:%.2fV", adcValue, voltage); 
164                    } else {  
165                        myPrintln("Failed to read data from ADC.");  
166                    }  
167                    Thread.sleep(100);  
168                }  
169            } else {  
170                myPrintln("ADS7830 device not detected at address 0x" + Integer.toHexString(ADC_CHIP_ADDR));  
171            }  
172        } finally {  
173            pi4j.shutdown();  
174        }  
175    }  
176}

The ADC value at the photosensor is obtained every 100 milliseconds, converted into a PWM duty cycle value for controlling the LED, and then a prompt message is printed on the terminal.

 1while (true) {  
 2    int adcValue = adc.analogRead(ADC_CHANNEL);  
 3    if (adcValue != -1) {  
 4        led.setPwmDutyCycle(((double)adcValue/255.0));
 5        double voltage = (double)adcValue / 255.0 * 5.0;
 6        myPrintln("ADC value:%d, Voltage:%.2fV", adcValue, voltage); 
 7    } else {  
 8        myPrintln("Failed to read data from ADC.");  
 9    }  
10    Thread.sleep(100);  
11}