7. (Important) Chapter ADC
We have learned how to control the brightness of an LED through PWM and that PWM is not a real analog signal. In this chapter, we will learn how to read analog values via an ADC Module and convert these analog values into digital.
7.1. Project Read the Voltage of Potentiometer
In this project, we will use the ADC function of an ADC Module to read the voltage value of a potentiometer.
7.1.1. Component List
|
|
Rotary potentiometer x1 |
|
ADC module x1 |
|
Jumper Wire |
|
7.1.2. Circuit knowledge
7.1.2.1. ADC
An ADC is an electronic integrated circuit used to convert analog signals such as voltages to digital or binary form consisting of 1s and 0s. The range of our ADC module is 8 bits, that means the resolution is 2^8=256, so that its range (at 3.3V) will be divided equally to 256 parts. Any analog value can be mapped to one digital value using the resolution of the converter. So the more bits the ADC has, the denser the partition of analog will be and the greater the precision of the resulting conversion.
Subsection 1: the analog in range of 0V-3.3/256 V corresponds to digital 0;
Subsection 2: the analog in range of 3.3 /256 V-2*3.3 /256V corresponds to digital 1;
…
The resultant analog signal will be divided accordingly.
7.1.2.2. DAC
The reversing this process requires a DAC, Digital-to-Analog Converter.
The digital I/O port can output high level and low level (0 or 1), but cannot output an intermediate voltage value.
This is where a DAC is useful. The DAC module PCF8591 has a DAC output pin with 8-bit accuracy, which can divide VDD (here is 3.3V) into 28 = 256 parts. For example, when the digital quantity is 1, the output voltage value is 3.3/256 *1 V, and when the digital quantity is 128, the output voltage value is 3.3/256 *128=1.65V, the higher the accuracy of DAC, the higher the accuracy of output voltage value will be.
7.1.3. Component knowledge
7.1.3.1. Potentiometer
Potentiometer is a resistive element with three Terminal parts. Unlike the resistors that we have used thus far in our project which have a fixed resistance value, the resistance value of a potentiometer can be adjusted. A potentiometer is often made up by a resistive substance (a wire or carbon element) and movable contact brush. When the brush moves along the resistor element, there will be a change in the resistance of the potentiometer’s output side (3) (or change in the voltage of the circuit that is a part). The illustration below represents a linear sliding potentiometer and its electronic symbol on the right.
Between potentiometer pin 1 and pin 2 is the resistive element (a resistance wire or carbon) and pin 3 is connected to the brush that makes contact with the resistive element. In our illustration, when the brush moves from pin 1 to pin 2, the resistance value between pin 1 and pin 3 will increase linearly (until it reaches the highest value of the resistive element) and at the same time the resistance between pin 2 and pin 3 will decrease linearly and conversely down to zero. At the midpoint of the slider the measured resistance values between pin 1 and 3 and between pin 2 and 3 will be the same.
In a circuit, both sides of resistive element are often connected to the positive and negative electrodes of power. When you slide the brush “pin 3”, you can get variable voltage within the range of the power supply.
7.1.3.2. Rotary potentiometer
Rotary potentiometers and linear potentiometers have the same function; the only difference being the physical action being a rotational rather than a sliding movement.
7.1.3.3. ADS7830
The ADS7830 is a single-supply, low-power, 8-bit data acquisition device that features a serial I2C interface and an 8-channel multiplexer. The following table is the pin definition diagram of ADS7830.
SYMBOL |
PIN |
DESCRIPTION |
TOP VIEW |
|---|---|---|---|
CH0 |
1 |
Analog input channels (A/D converter) |
|
CH1 |
2 |
||
CH2 |
3 |
||
CH3 |
4 |
||
CH4 |
5 |
||
CH5 |
6 |
||
CH6 |
7 |
||
CH7 |
8 |
||
GND |
9 |
Ground |
|
REF in/out |
10 |
Internal +2.5V Reference,External Reference Input |
|
COM |
11 |
Common to Analog Input Channel |
|
A0 |
12 |
Hardware address |
|
A1 |
13 |
||
SCL |
14 |
Serial Clock |
|
SDA |
15 |
Serial Sata |
|
+VDD |
16 |
Power Supply, 3.3V Nominal |
7.1.3.4. I2C communication
I2C (Inter-Integrated Circuit) has a two-wire serial communication mode, which can be used to connect a micro-controller and its peripheral equipment. Devices using I2C communications must be connected to the serial data line (SDA), and serial clock line (SCL) (called I2C bus). Each device has a unique address which can be used as a transmitter or receiver to communicate with devices connected via the bus.
7.1.4. Circuit
Schematic diagram |
Hardware connection. If you need any support,please feel free to contact us via:
|
7.1.5. Configure I2C and Install Smbus
7.1.5.1. Enable I2C
The I2C interface in Raspberry Pi is disabled by default. You will need to open it manually and enable the I2C interface as follows:
Type command in the Terminal:
$ sudo raspi-config
Then open the following dialog box:
Choose “Interfacing Options” then “I5 I2C” then “Yes” and then “Finish” in this order and restart your RPi. The I2C module will then be started.
Type a command to check whether the I2C module is started:
$ lsmod | grep i2c
If the I2C module has been started, the following content will be shown.
Different models of Raspberry Pi display different contents depending on the CPU installed:
I2C device address detection:
$ i2cdetect -y 1
When you are using the ADS7830 Module, the result should look like this:
Here, 48 (HEX) is the I2C address of ADC Module (ADS7830).
7.1.6. Sketch
In this chapter, we will learn the combined usage of ADC and potentiometer.
7.1.6.1. Sketch_ADC
First, enter where the project is located:
$ cd ~/Freenove_Kit/Pi4j/Sketches/Sketch_07_1_ADC
Enter the command to run the code.
$ jbang ADC.java
When the code is running, rotate the potentiometer marked below.
You can see that the ADC values change with the rotation of the potentiometer. The value 0 means that the potentiometer’s voltage read by ADC is 0V, 255 indicates that the voltage is 5V.
Press Ctrl+C to exit the code.
You can open the code with Geany with the following command to view and edit it.
$ geany ADC.java
Click the icon to run the code.
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;
16
17class ADCDevice {
18 private final I2C adcChip;
19 private final int adcChipAddr;
20
21 public ADCDevice(Context pi4j, I2CProvider provider, int adcChipAddr) throws Exception {
22 this.adcChipAddr = adcChipAddr;
23 I2CConfig i2cConfig = I2C.newConfigBuilder(pi4j).id("ADCDevice").bus(1).device(adcChipAddr).build();
24 this.adcChip = provider.create(i2cConfig);
25 }
26
27 public boolean detectI2C() throws Exception {
28 try {
29 adcChip.write(0);
30 byte[] data = new byte[1];
31 int bytesRead = adcChip.read(data, 0, 1);
32 return bytesRead == 1;
33 } catch (Exception e) {
34 return false;
35 }
36 }
37
38 public int analogRead(int chn) {
39 byte command = (byte) (0x84 | (((chn << 2 | chn >> 1) & 0x07) << 4));
40 adcChip.write(command);
41 byte[] data = new byte[1];
42 int bytesRead = adcChip.read(data, 0, 1);
43 if (bytesRead == 1) {
44 int adcValue = data[0] & 0xFF;
45 return adcValue;
46 } else {
47 return -1;
48 }
49 }
50}
51
52public class ADC {
53
54 public static void myPrintln(String format, Object... args) {
55 Console console = new Console();
56 console.println(String.format("\u001B[32m" + format + "\u001B[0m", args));
57 }
58
59 public static void main(String[] args) throws Exception {
60 Context pi4j = Pi4J.newAutoContext();
61 I2CProvider i2CProvider = pi4j.provider("linuxfs-i2c");
62 try {
63 int ADC_CHIP_ADDR = 0x4B;
64 ADCDevice adcDevice = new ADCDevice(pi4j, i2CProvider, ADC_CHIP_ADDR);
65 if (adcDevice.detectI2C()) {
66 int ADC_CHANNEL = 0;
67 while (true) {
68 int adcValue = adcDevice.analogRead(ADC_CHANNEL);
69 if (adcValue != -1) {
70 myPrintln("ADC Channel %d Value:%d", ADC_CHANNEL, adcValue);
71 }
72 else {
73 myPrintln("Failed to read data from ADC.");
74 }
75 Thread.sleep(100);
76 }
77 }
78 else {
79 myPrintln("ADS7830 device not detected at address 0x" + Integer.toHexString(ADC_CHIP_ADDR));
80 }
81 }
82 finally {
83 pi4j.shutdown();
84 }
85 }
86}
Dependency declaration, these libraries will be automatically downloaded by jbang at runtime and added to the classpath.
1//DEPS org.slf4j:slf4j-api:2.0.12
2//DEPS org.slf4j:slf4j-simple:2.0.12
3//DEPS com.pi4j:pi4j-core:2.6.0
4//DEPS com.pi4j:pi4j-plugin-raspberrypi:2.6.0
5//DEPS com.pi4j:pi4j-plugin-gpiod:2.6.0
6//DEPS com.pi4j:pi4j-plugin-linuxfs:2.6.0
Import I2C library. In this project, we use I2C to read the channel value of ADS7830.
1import com.pi4j.Pi4J;
2import com.pi4j.context.Context;
3import com.pi4j.io.i2c.I2C;
4import com.pi4j.io.i2c.I2CConfig;
5import com.pi4j.io.i2c.I2CProvider;
6import com.pi4j.util.Console;
Constructor of ADCDevice class, which is used to initialize I2C bus to facilitate later reading and writing ADS7830 chip.
1public ADCDevice(Context pi4j, I2CProvider provider, int adcChipAddr) throws Exception {
2 this.adcChipAddr = adcChipAddr;
3 I2CConfig i2cConfig = I2C.newConfigBuilder(pi4j).id("ADCDevice").bus(1).device(adcChipAddr).build();
4 this.adcChip = provider.create(i2cConfig);
5}
Write a byte to the target chip, and then read the data. If the data can be read, it means the target chip exists and communication is successful. If an I2C exception is detected, it means the target chip does not exist.
1public boolean detectI2C() throws Exception {
2 try {
3 adcChip.write(0);
4 byte[] data = new byte[1];
5 int bytesRead = adcChip.read(data, 0, 1);
6 return bytesRead == 1;
7 } catch (Exception e) {
8 return false;
9 }
10}
Write the read command to the ADS7830 and read the corresponding ADC value. It is returned by the return value.
1public int analogRead(int chn) {
2 byte command = (byte) (0x84 | (((chn << 2 | chn >> 1) & 0x07) << 4));
3 adcChip.write(command);
4 byte[] data = new byte[1];
5 int bytesRead = adcChip.read(data, 0, 1);
6 if (bytesRead == 1) {
7 int adcValue = data[0] & 0xFF;
8 return adcValue;
9 } else {
10 return -1;
11 }
12}
Create a pi4j context to get the Raspberry PI i2c interface.
1Context pi4j = Pi4J.newAutoContext();
2I2CProvider i2CProvider = pi4j.provider("linuxfs-i2c");
The I2C address of the ADS7830 is 0x48.
Create an ADCDevice class, associate it with the Raspberry PI I2C interface, and assign a value to the adcDevice.
1int ADC_CHIP_ADDR = 0x4B;
2ADCDevice adcDevice = new ADCDevice(pi4j, i2CProvider, ADC_CHIP_ADDR);
Check whether the chip can communicate normally. If the communication is successful, read channel 0 of the ADS7830 chip and print it out in the terminal.
1if (adcDevice.detectI2C()) {
2 int ADC_CHANNEL = 0;
3 while (true) {
4 int adcValue = adcDevice.analogRead(ADC_CHANNEL);
5 if (adcValue != -1) {
6 myPrintln("ADC Channel %d Value:%d", ADC_CHANNEL, adcValue);
7 }
8 else {
9 myPrintln("Failed to read data from ADC.");
10 }
11 Thread.sleep(100);
12 }
13}
If communication with the chip fails, a prompt message is printed on the terminal.
1else {
2 myPrintln("ADS7830 device not detected at address 0x" + Integer.toHexString(ADC_CHIP_ADDR));
3}
When the code finishes running, close the Pi4J context.
1finally {
2 pi4j.shutdown();
3}
7.2. Project Soft Light
7.2.1. Component List
|
||
Rotary potentiometer x1 |
Resistor 220Ω x1 |
|
ADC module x1 (Only one)
|
LED x1 |
|
Jumper Wire M/M x17 |
||
7.2.2. Circuit
Schematic diagram
|
Hardware connection. If you need any support,please feel free to contact us via:
|
7.2.3. Sketch
In this project, we learn how to control the brightness of LED with the potentiometer.
7.2.3.1. Sketch_Softlight
First, enter where the project is located:
$ cd ~/Freenove_Kit/Pi4j/Sketches/Sketch_07_2_Softlight
Enter the command to run code.
$ jbang Softlight.java
When the code is running, turn the potentiometer marked below and you can see the brightness of the LED change.
On the Terminal, you can see the printed ADC values and the calculated voltage values.
Press Ctrl+C to exit the program.
You can open the code with Geany with the following command to view and edit it.
$ geany Softlight.java
Click the icon to run the code.
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 Softlight{
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 of the potentiometer is obtained every 100 milliseconds and printed on the terminal. Meanwhile, the ADC value is converted into the duty cycle value of the LED to control the brightness of the LED.
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}
7.3. Project Colorful Light
In this project, 3 potentiometers are used to control the RGB LED and in principle, it is the same as with the ‘Soft Light’ project. Namely, read the voltage value of the potentiometer and then convert it to PWM used to control LED brightness. Difference is that the previous soft light project needed only one LED while this one required (3) RGB LEDs.
7.3.1. Component List
|
||
Rotary potentiometer x1 |
Resistor 220Ω x1 |
|
ADC module x1 (Only one)
|
RGB LED x1 |
|
Jumper Wire M/M x17 |
||
7.3.2. Circuit
Schematic diagram
|
Hardware connection. If you need any support,please feel free to contact us via:
If circuit above doesn’t work, please try following wiring.
|
7.3.3. Sketch
In this project, we learn to use the potentiometer to control the color and brightness of the RGB LED.
7.3.4. Sketch_ColorfulSoftlight
First, enter where the project is located:
$ cd ~/Freenove_Kit/Pi4j/Sketches/Sketch_07_3_ColorfulSoftlight
Enter the command to run the code.
$ jbang ColorfulSoftlight.java
When the code is running, rotate the three potentiometers marked below, you will see the RGB LED’s color and brightness change.
The ADC value is printed on the terminal.
Press Ctrl+C to exit the program.
You can open the code with Geany with the following command to view and edit it.
$ geany ColorfulSoftlight.java
Click the icon to run the code.
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 ColorfulSoftlight{
121 private static int LED_PIN = 17;
122 private static int ADC_CHIP_ADDR = 0x4B;
123 private static final Context pi4j = Pi4J.newAutoContext();
124 private static final Map<Integer, PWMController> pwmControllers = new HashMap<>();
125
126 public static void setPwmConfig(int pin) throws Exception {
127 DigitalOutput pwm = pi4j.dout().create(pin);
128 PWMController pwmController = new PWMController(pwm);
129 Thread pwmThread = new Thread(pwmController, "PWM Controller " + pin);
130 pwmControllers.put(pin, pwmController);
131 pwmThread.start();
132 Runtime.getRuntime().addShutdownHook(new Thread(() -> {
133 pwmController.requestStop();
134 try {
135 pwmThread.join();
136 } catch (InterruptedException e) {
137 Thread.currentThread().interrupt();
138 }
139 }));
140 }
141
142 public static void myPrintln(String format, Object... args) {
143 Console console = new Console();
144 console.println(String.format("\u001B[32m" + format + "\u001B[0m", args));
145 }
146
147 public static void main(String[] args) throws Exception {
148 Context pi4j = Pi4J.newAutoContext();
149 I2CProvider i2CProvider = pi4j.provider("linuxfs-i2c");
150
151 int[] ADC_CHN = {0, 1, 2};
152 int[] LED_PINS = {17, 27, 22};
153 for (int pin : LED_PINS) {
154 setPwmConfig(pin);
155 }
156 PWMController red_led = pwmControllers.get(LED_PINS[0]);
157 PWMController green_led = pwmControllers.get(LED_PINS[1]);
158 PWMController blue_led = pwmControllers.get(LED_PINS[2]);
159
160 try {
161 ADCDevice adc = new ADCDevice(pi4j, i2CProvider, ADC_CHIP_ADDR);
162 if (adc.detectI2C()) {
163 while (true) {
164 int val_Red = adc.analogRead(ADC_CHN[0]);
165 int val_Green = adc.analogRead(ADC_CHN[1]);
166 int val_Blue = adc.analogRead(ADC_CHN[2]);
167
168 red_led.setPwmDutyCycle(1-(double)(val_Red/255.0));
169 green_led.setPwmDutyCycle(1-(double)(val_Green/255.0));
170 blue_led.setPwmDutyCycle(1-(double)(val_Blue/255.0));
171
172 myPrintln("ADC value val_Red:%d, val_Green:%d, val_Blue:%d", val_Red, val_Green, val_Blue);
173 Thread.sleep(100);
174 }
175 } else {
176 myPrintln("ADS7830 device not detected at address 0x" + Integer.toHexString(ADC_CHIP_ADDR));
177 }
178 } finally {
179 pi4j.shutdown();
180 }
181 }
182}
Initialize the pins that control the RGB LED.
1int[] LED_PINS = {17, 27, 22};
2for (int pin : LED_PINS) {
3 setPwmConfig(pin);
4}
5PWMController red_led = pwmControllers.get(LED_PINS[0]);
6PWMController green_led = pwmControllers.get(LED_PINS[1]);
7PWMController blue_led = pwmControllers.get(LED_PINS[2]);
Get the ADC values corresponding to the 3 rotentiometers every 100 milliseconds; convert the values into duty cycle values corresponding to PWM, and print prompt information on the terminal.
1while (true) {
2 int val_Red = adc.analogRead(ADC_CHN[0]);
3 int val_Green = adc.analogRead(ADC_CHN[1]);
4 int val_Blue = adc.analogRead(ADC_CHN[2]);
5
6 red_led.setPwmDutyCycle(1-(double)(val_Red/255.0));
7 green_led.setPwmDutyCycle(1-(double)(val_Green/255.0));
8 blue_led.setPwmDutyCycle(1-(double)(val_Blue/255.0));
9
10 myPrintln("ADC value val_Red:%d, val_Green:%d, val_Blue:%d", val_Red, val_Green, val_Blue);
11 Thread.sleep(100);
12}










