Read analog sensors and convert raw values to meaningful measurements
Analog inputs allow you to read continuously varying signals from sensors like potentiometers, temperature sensors, light sensors, and pressure sensors. This example covers:
use pokeys_lib::*;
use std::{thread, time::Duration};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("📊 PoKeys Analog Input Example");
println!("==============================");
// Connect to device
let device_count = enumerate_usb_devices()?;
if device_count == 0 {
println!("❌ No PoKeys devices found!");
return Ok(());
}
let mut device = connect_to_device(0)?;
println!("✅ Connected to: {}", device.get_device_name()?);
// Configure pins for analog input
device.set_pin_function(3, PinFunction::AnalogInput)?;
device.set_pin_function(7, PinFunction::AnalogInput)?;
println!("⚙️ Configured pins 3 and 7 for analog input");
// Set ADC reference voltage (3.3V for most PoKeys devices)
device.set_analog_reference_voltage(3.3)?;
println!("📈 Starting continuous reading (Ctrl+C to stop)...");
println!("Pin 3 (Pot) | Pin 7 (Temp) | Voltage | Temperature");
println!("------------|--------------|---------|------------");
loop {
// Read raw ADC values (0-4095 for 12-bit ADC)
let pot_raw = device.get_analog_input(3)?;
let temp_raw = device.get_analog_input(7)?;
// Convert to voltage (0-3.3V)
let pot_voltage = raw_to_voltage(pot_raw, 3.3);
let temp_voltage = raw_to_voltage(temp_raw, 3.3);
// Convert temperature sensor voltage to Celsius (LM35: 10mV/°C)
let temperature_c = temp_voltage * 100.0;
println!("{:>11} | {:>12} | {:>7.3}V | {:>8.1}°C",
pot_raw, temp_raw, pot_voltage, temperature_c);
thread::sleep(Duration::from_millis(500));
}
}
/// Convert raw ADC value to voltage
fn raw_to_voltage(raw_value: u16, reference_voltage: f32) -> f32 {
(raw_value as f32 / 4095.0) * reference_voltage
} use pokeys_lib::*;
use std::{collections::VecDeque, thread, time::Duration};
struct AnalogSensor {
pin: u8,
name: String,
samples: VecDeque<u16>,
max_samples: usize,
calibration_offset: f32,
calibration_scale: f32,
}
impl AnalogSensor {
fn new(pin: u8, name: &str, max_samples: usize) -> Self {
Self {
pin,
name: name.to_string(),
samples: VecDeque::new(),
max_samples,
calibration_offset: 0.0,
calibration_scale: 1.0,
}
}
/// Add a new sample and maintain rolling average
fn add_sample(&mut self, raw_value: u16) {
self.samples.push_back(raw_value);
if self.samples.len() > self.max_samples {
self.samples.pop_front();
}
}
/// Get filtered (averaged) raw value
fn get_filtered_raw(&self) -> f32 {
if self.samples.is_empty() {
return 0.0;
}
let sum: u32 = self.samples.iter().map(|&x| x as u32).sum();
sum as f32 / self.samples.len() as f32
}
/// Get calibrated value in physical units
fn get_calibrated_value(&self, reference_voltage: f32) -> f32 {
let voltage = (self.get_filtered_raw() / 4095.0) * reference_voltage;
(voltage + self.calibration_offset) * self.calibration_scale
}
/// Set calibration parameters
fn set_calibration(&mut self, offset: f32, scale: f32) {
self.calibration_offset = offset;
self.calibration_scale = scale;
}
}
fn advanced_sensor_reading() -> Result<(), Box<dyn std::error::Error>> {
let mut device = connect_to_device(0)?;
// Configure analog inputs
device.set_pin_function(3, PinFunction::AnalogInput)?;
device.set_pin_function(7, PinFunction::AnalogInput)?;
device.set_pin_function(8, PinFunction::AnalogInput)?;
device.set_analog_reference_voltage(3.3)?;
// Create sensor objects with filtering
let mut potentiometer = AnalogSensor::new(3, "Potentiometer", 10);
let mut temperature = AnalogSensor::new(7, "Temperature", 20);
let mut light_sensor = AnalogSensor::new(8, "Light Level", 15);
// Set calibrations
temperature.set_calibration(0.0, 100.0); // LM35: 10mV/°C
light_sensor.set_calibration(0.0, 100.0); // Convert to percentage
potentiometer.set_calibration(0.0, 100.0); // Convert to percentage
println!("🔬 Advanced Sensor Reading with Filtering");
println!("==========================================");
// Collect initial samples for filtering
println!("📊 Collecting initial samples...");
for _ in 0..25 {
potentiometer.add_sample(device.get_analog_input(3)?);
temperature.add_sample(device.get_analog_input(7)?);
light_sensor.add_sample(device.get_analog_input(8)?);
thread::sleep(Duration::from_millis(50));
}
println!("📈 Filtered sensor readings:");
println!("Time | Pot (%) | Temp (°C) | Light (%)");
println!("---------|---------|-----------|----------");
for i in 0..100 {
// Read new samples
potentiometer.add_sample(device.get_analog_input(3)?);
temperature.add_sample(device.get_analog_input(7)?);
light_sensor.add_sample(device.get_analog_input(8)?);
// Get calibrated values
let pot_percent = potentiometer.get_calibrated_value(3.3);
let temp_celsius = temperature.get_calibrated_value(3.3);
let light_percent = light_sensor.get_calibrated_value(3.3);
println!("{:>8} | {:>7.1} | {:>9.1} | {:>8.1}",
i, pot_percent, temp_celsius, light_percent);
thread::sleep(Duration::from_millis(200));
}
Ok(())
} Different sensors require different calibration approaches:
// LM35 Temperature: 10mV/°C
let temp_c = voltage * 100.0;
// Pressure sensor: 0.5-4.5V = 0-100 PSI
let pressure = (voltage - 0.5) * (100.0 / 4.0);
// Potentiometer: 0-3.3V = 0-100%
let percentage = (voltage / 3.3) * 100.0; // Thermistor (requires lookup table)
let resistance = 10000.0 * voltage / (3.3 - voltage);
let temp_k = 1.0 / (A + B * resistance.ln() +
C * resistance.ln().powi(3));
let temp_c = temp_k - 273.15;
// Light sensor (logarithmic response)
let lux = 10.0_f32.powf((voltage - 1.0) * 2.0);