Voltage Control with Arduino

This sketch listens on 115200 baud. You enter the voltage (0 to 5 volts), it reproduces it with PWM.

Pretty simple, yet pretty dramatic.
I tested its accuracy with a multimeter. 8-bit accuracy :stuck_out_tongue:

Note: This requires the Streaming library to compile. You can just use a series of Serial.print and println statements instead, but Streaming just makes it easier. (Face it, I love C++ streams) :slight_smile:

#include <SoftwareSerial.h>
#include <Streaming.h>

// Required for memory functions
#include <stdlib.h>

// Required for modf() and fabs()
#include <math.h>

// Maximum length of serial input
#define MAX_LEN 16

// PWM pin of your choice
#define PWM_PIN 12

// Onboard LED
#define LED 13

// Serial data rate
#define BAUD 115200

// The index for serial to char * conversion
static int a = 0;

// Storage for the serial input
static char * volt_char = NULL;

// Storage for the voltage
static double volts;

void setup()
{
  // Set pin modes
  pinMode( PWM_PIN, OUTPUT );
  pinMode( LED, OUTPUT );
  
  // Turn on PWM output
  digitalWrite( PWM_PIN, HIGH );
  
  // Allocate storage
  volt_char = (char *)calloc( MAX_LEN, sizeof( char ) );
  
  // Start serial
  Serial.begin( BAUD );
  
  // Initial message!
  Serial << "Enter voltage level: ";
}

void loop()
{
  // Poll serial
  while( Serial.available() > 0 ) {
    // Get characters
    if( a < MAX_LEN )
      volt_char[a] = char( Serial.read() );
    else
    {
      // Tell the user and flush serial input
      Serial << "Too long" << endl << "Enter voltage level: ";
      Serial.flush();
      
      // Reset vars and clear memory
      a = 0;
      memset( volt_char, 0x00, MAX_LEN );
      break;
    }
    
    // Increase character index
    a++;
    
    // Wait for serial to catch up. Change as needed.
    delay( 1 );
  }

  if( a > 0 )
  {
    // LED on
    digitalWrite( LED, HIGH );
    
    // No negatives. Convert the char * to a double.
    volts = fabs( atof( (const char *)volt_char ) );
    
    // Sort out invalid voltages
    if( ( volts < 1.00 && ( volt_char[0] == '0' || volt_char[0] == '.' ) ) || ( volts >= 1.00 && volts <= 5.00 && volts != 0.00 && volt_char[0] != '0' ) || ( volts == 0.00 && ( volt_char[0] == '0' || volt_char[0] == '.' ) ) )
    {
      // Set up three vars to hold the parts of the voltage
      double pwm = ( volts / 5.00 ) * 255.00, fractpart, intpart;
      
      // fractpart: Decimal part, intpart: integral part
      fractpart = modf( pwm, &intpart );
      
      // Round the integer part based upon the fractional part
      if( fractpart >= 0.5 )
        intpart += 1.0;
        
      // Set the voltage
      analogWrite( PWM_PIN, floor( intpart ) );
      
      // Output a message
      Serial << volts << "... OK";
    }
    else if( volts > 5.00 )
      Serial << "Too high";
    else
      Serial << "Invalid";

    // Output the standard message
    Serial << endl << "Enter voltage level: ";

    // Clear memory
    memset( volt_char, 0x00, MAX_LEN );
    a = 0;
    
    // LED off
    digitalWrite( LED, LOW );
  }
  
  // Wash, rinse, repeat.
}

I wonder if it would be feasible to use a sigma-delta DAC for this purpose. Have you thought about it ? We could possibly make it work good, provided we can quickly and timely change the pulse.

Álvaro

What is this component you speak of?

In other words, it sounds really interesting. :slight_smile: