An analog input allows converting a variable voltage into a value the program can process. For this we use the microcontroller’s ADC (Analog-to-Digital Converter).
To read temperature sensors, light sensors (LDR), or simply the position of a rotary knob, we need to use the Analog Inputs and ADC (Analog-to-Digital Converter) of our microcontroller.
In Arduino, analogRead() returns an integer whose range depends on the configured resolution and the microcontroller. Often we have to convert it to a percentage or another useful unit.
DeviceScript can expose the reading normalized between 0 and 1, so the logic doesn’t depend on the specific ADC resolution.
The Potentiometer service
Although we could try to read the raw voltage, DeviceScript encourages us to use Services. For any generic analog input (like a potentiometer, an analog soil moisture sensor, or an LDR), we typically use the Potentiometer service.
This service has a wonderful feature: It returns the normalized value between 0.0 and 1.0.
0.0: Minimum voltage (GND).1.0: Maximum voltage (typically 3.3V).0.5: Half the range.
This normalization makes your code portable. If you write a program for an ESP32 (12-bit ADC) and move it to a microcontroller with a 10-bit ADC, your code still works the same because you always work between 0 and 1. Say goodbye to recalculating ranges!
Connecting the potentiometer
For this example, we will connect a classic potentiometer:
- Left pin: GND.
- Right pin: 3.3 V. Do not apply 5V to a non-tolerant input.
- Center pin (Wiper): To an ADC-compatible pin.
Not all GPIOs support ADC and the assignment depends on the board and chip. Check your model’s pinout before choosing the pin; also, remember that some ESP32s share ADC resources with the WiFi radio.
Reading the value
Let’s write a script that reads the potentiometer position and displays it on the console.
import { delay } from "@devicescript/core"
import { startPotentiometer } from "@devicescript/servers"
import { pins } from "@dsboard/esp32_c3_devkit_m" // Or your board
// Initialize the service on the appropriate pin
const myPotentiometer = startPotentiometer({
pin: pins.GPIO4 // Adjust to your ADC pin
})
console.log("Reading potentiometer...")
setInterval(async () => {
// Read the current reading (from 0 to 1)
const value = await myPotentiometer.reading.read()
// Display formatted with 2 decimal places
console.log(`Value: ${value.toFixed(2)}`)
}, 500) // Read every half secondRunning the example in the simulator will show a slider. Move it with the mouse and the console will display values like 0.15 or 0.89.
Example: controlling an LED’s brightness
This is where the DeviceScript philosophy shines.
We want the brightness of an LED to be proportional to the potentiometer’s rotation.
- In Arduino:
analogWrite(led, map(analogRead(pot), 0, 4095, 0, 255))-> A mess of ranges. - In DeviceScript: Since the LED (
LightBulb) expects intensity from 0 to 1, and the Potentiometer outputs 0 to 1… We connect the values directly!
import { startPotentiometer, startLightBulb } from "@devicescript/servers"
import { pins } from "@dsboard/esp32_c3_devkit_m"
// 1. Define Hardware
const pot = startPotentiometer({ pin: pins.GPIO4 })
const led = startLightBulb({ pin: pins.GPIO2 })
// 2. Reactive logic
// Subscribe to changes in the reading
pot.reading.subscribe(async (currentValue) => {
// Write the value directly to the LED
await led.intensity.write(currentValue)
})Try it in the simulator: moving the slider will change the bulb’s brightness. Both services use the same normalized range.
Reading other analog sensors
What if I don’t have a potentiometer, but an LDR (Photoresistor) or a capacitive soil moisture sensor?
The principle is the same. Physically, these sensors are often arranged in a voltage divider to provide a variable voltage. For DeviceScript, they are still an analog signal.
We can still use startPotentiometer (even if the name isn’t semantically perfect) or look for specific drivers if they exist. In the end, we will receive a 0..1 value proportional to the voltage.
Note about ESP32: The ADC is not perfectly linear, especially near the extremes. If you need precision, calibrate the combination of board, attenuation, and sensor.