Raspberry Pi Pico 2 Servo Control Using ADS1115 ADC | MicroPython Tutorial

Raspberry Pi Pico 2 Servo Control Using ADS1115 ADC | MicroPython Tutorial

Controlling a servo motor with precision is a fundamental skill in robotics and automation. In this tutorial I will demonstrates how to achieve accurate servo positioning using a Raspberry Pi Pico 2, an ADS1115 16-bit Analog-to-Digital Converter (ADC), and a simple potentiometer for input. We'll leverage MicroPython to bridge the analog world of potentiometers with the digital control of servos.

Understanding the Circuit: Analog Input to Servo Output

The core of this project involves reading a variable analog voltage and converting it into a precise digital signal to drive a servo motor. Here’s a breakdown of the components and their roles in our circuit:

Raspberry Pi Pico 2 servo motor ads1115 circuit diagram
  • Raspberry Pi Pico 2: This powerful microcontroller acts as the brain. It communicates with the ADC via I2C, processes the analog data, and generates the Pulse Width Modulation (PWM) signal required to control the servo.

  • ADS1115 16-bit I2C ADC+PGA: The ADS1115 is crucial because the Raspberry Pi Pico 2 lacks built-in high-resolution analog input. This module converts the analog voltage from the potentiometer into a 16-bit digital value, offering excellent precision. It communicates with the Pico 2 using the I2C protocol, connecting its SDA pin to Pico's GP2 and SCL to Pico's GP3. The default I2C address for the ADS1115 is 0x48.

  • RV1 (10k Potentiometer): This variable resistor serves as our analog input device. One outer pin is connected to +3.3V, the other to Ground, and the center wiper pin outputs a variable voltage (from 0V to 3.3V) as the knob is turned. This output voltage feeds directly into the ADS1115's A0 input channel.

  • Servo Motor: The output actuator. Servos are controlled by PWM signals, where the width of the pulse dictates the angular position of the motor shaft. Standard servos typically operate at 50Hz, with pulse widths ranging from about 1ms (for 0 degrees) to 2ms (for 180 degrees).

  • Power and Ground (+3.3V): All components are powered by a common 3.3V supply, usually provided by the Pico 2 itself, and share a common ground reference.

How the Circuit Operates Step-by-Step

When you turn the potentiometer, its wiper generates a varying analog voltage. This voltage is fed into the ADS1115, which then converts it into a precise 16-bit digital value. The Pico 2 reads this digital value via its I2C interface. Our MicroPython code then takes this raw digital data, converts it back to a representative voltage, and maps this voltage to a specific PWM duty cycle range suitable for the servo motor. Finally, the Pico 2 generates the corresponding PWM signal on a dedicated GPIO pin, causing the servo to rotate to the desired position.

Important Note on Pin Assignments: The provided schematic and code show the Pico 2's GP3 pin being used for both I2C SCL and the servo PWM signal. In a real-world implementation, a single GPIO pin cannot serve both functions simultaneously. For correct operation, the servo PWM signal would need to be assigned to a different available PWM-capable GPIO pin (e.g., GP0, GP1, GP4, GP5, etc.) to avoid conflicts and ensure reliable control.

MicroPython Code for Servo Control

Here's the MicroPython code I used to bring this circuit to life. Ensure you have the `ads1115` library (or a compatible one like `adafruit_ads1x15`) installed on your Pico 2. You can often find these in the MicroPython community or by searching for "MicroPython ADS1115 library".


import time
from machine import Pin, PWM, I2C
# You will need a MicroPython ADS1115 library, such as 'ads1x15' or 'adafruit_ads1x15'
# For this script, we use a standard MicroPython ADS1115 implementation structure
import ads1115 

# --- Hardware Setup ---
SERVO_PIN = 3
servo_pwm = PWM(Pin(SERVO_PIN))
servo_pwm.freq(50) # 50Hz for standard servos

# MicroPython PWM uses 16-bit duty cycle (0 to 65535)
# 2.5% duty cycle = 0.025 * 65535 = 1638
# 12.5% duty cycle = 0.125 * 65535 = 8192
MIN_DUTY = 1638
MAX_DUTY = 8192

# Initialize I2C on Pico 2 (Using I2C0: GP8 = SDA, GP9 = SCL)
i2c = I2C(1, sda=Pin(2), scl=Pin(3), freq=400000)

# Initialize ADS1115 (Address 0x48 is default)
adc = ads1115.ADS1115(i2c, address=0x48)

print("Reading from ADS1115 on Raspberry Pi Pico 2...")

try:
    while True:
        # Read from Channel 0 (Gain 1 maps to +/-4.096V max range)
        # Note: In MicroPython libraries, read(0) typically handles this.
        raw_val = adc.read(0, gain=1)
        
        # Convert raw bits to approximate voltage 
        # ADS1115 is a 16-bit signed ADC, so max single-ended value is 32767
        voltage = (raw_val * 4.096) / 32767.0
        
        # Constrain voltage between 0.0V and 3.3V (Pico 2 operating voltage)
        if voltage > 3.3: voltage = 3.3
        if voltage < 0.0: voltage = 0.0
        
        # Map voltage to 16-bit Servo Duty Cycle (1638 to 8192)
        duty_cycle_16bit = int(MIN_DUTY + (voltage / 3.3) * (MAX_DUTY - MIN_DUTY))
        servo_pwm.duty_u16(duty_cycle_16bit)
        
        # Calculate percentage for console printout similarity
        duty_percent = (duty_cycle_16bit / 65535.0) * 100
        
        print(f"Raw: {raw_val} | Volt: {voltage:.2f}V | Servo Duty: {duty_percent:.2f}%")
        time.sleep(0.1)

except KeyboardInterrupt:
    print("\nStopping PWM and exiting...")
    servo_pwm.deinit() # Cleanly releases the PWM generator

This code first initializes the servo PWM on GP3 (though, as noted, this needs adjustment for a real circuit to avoid conflict with I2C SCL). It then sets up the I2C communication with the ADS1115. In the main loop, it continuously reads the analog value from the potentiometer via ADC channel 0, converts this raw digital value into a voltage, and then maps that voltage to a 16-bit duty cycle for the servo. The `duty_u16()` function is used to apply this duty cycle, smoothly controlling the servo's position. The MIN_DUTY and MAX_DUTY values are crucial for defining the servo's physical range.

Practical Use Cases and Applications

This setup is fundamental to many projects:

  • Robotics: Precise control of robotic arm joints or wheel steering.
  • Automated Systems: Controlling valves, dampers, or camera gimbals based on sensor inputs.
  • User Interfaces: Creating custom control knobs for various parameters, similar to our potentiometer input.
  • Prototyping: Rapidly testing control algorithms for actuators.

Understanding how to interface analog inputs with digital outputs via ADCs is a cornerstone of embedded systems development. For more Raspberry Pi Pico 2 tutorials, explore topics like motor driver integration, button and LED interaction, or auditory alerts with buzzers. You can even delve into creating interactive boards.

Conclusion

By combining the Raspberry Pi Pico 2 with an ADS1115 ADC, we've demonstrated a robust method for precise servo control using analog input. This project highlights the power of MicroPython for quick prototyping and the versatility of external ADCs for expanding microcontroller capabilities. Experiment with different potentiometers or even other analog sensors to drive your servo motors in innovative ways!


Post a Comment

Previous Post Next Post