Hello there. I need to build a simple Voltage-to-Frequency (VFO) converter using my Arduino UNO R3.
I need to convert 0-5VDC into 0-100 Hz (square wave). Any help will be highly appreciated.
You could use the tone library to generate the desired freqence
http://arduino.cc/en/Reference/Tone
And analogRead() to measure the 0-5V
Thanks Erni. I just tried the following code but I am getting garbage at pin 5
// Voltage-To-Frequency Oscillator (VFO)
int outPin = 5; // Output Frequency 0 - 100 Hz
int AnalogPin = 3; // potentiometer wiper (middle terminal) connected to analog pin 3
// outside leads to ground and +5V
unsigned int volt = 0; // variable to hold the voltage read
void setup()
{
pinMode(outPin, OUTPUT); // sets the pin 5 as output
pinMode(AnalogPin, INPUT); // sets the pin 3 as input
}
void loop()
{
volt = analogRead(AnalogPin) / 10; // read the input pin and divide by 10 to get 102
tone(outPin, volt);
}
Does changing the 3 to A3 help
int AnalogPin = A3; // potentiometer wiper (middle terminal) connected to analog pin 3
// outside leads to ground and +5V
You would have a easier time finding your problem if you used the serial monitor.
Try putting Serial.print statements in your sketch to see variables like "volt" change with your pot.
Hi cyclegadget. I have my own scope and only see garbage. I also tried what you said (Serial.print(volt)) and get fixed values.
http://imageshack.us/a/img211/2169/vtf1.jpg
I would recommend getting your analogread working then go from there. Try this http://arduino.cc/en/Tutorial/AnalogInput
Hello Riva and cyclegadget. It is working now. I just changed 3 for A3 and started to work.
By the way, I am using an OSEPP Uno R3 Plus (I said wrongly at the beginning of my post).
After some tests, I've found that the tone() command generates good square waves from 102 to 31 Hz, but, below that, the output changes abruptly to 192 Hz!
tone(outPin, 102); //Equal to 102 Hz volt = 102
...
tone(outPin, 31); //Equal to 31 Hz volt = 31
tone(outPin, 30); //Equal to 190 Hz volt = 30
tone(outPin, 20); //Equal to 59 Hz volt = 20
tone(outPin, 10); //Equal to 600 Hz volt = 10
tone(outPin, 0); //Equal to 30 Hz volt = 0
Why this occurs?
I have noticed in the pitches.h file inside of: ...\arduino-1.0.1-windows\arduino-1.0.1\examples\02.Digital\toneMultiple
that the first #define is NOTE_B0 31. Is it a coincidence here? So, if the tone() command is not capable to generate lower notes, is there another approach to generate frequencies below 31 Hz? or I should go with the false/true and delay routine playing with the digital outputs? Thanks.
The link I posted before would be a start for making your own frequency output.
You will need to use map. http://arduino.cc/en/Reference/Map
delaytime = map(volt, 0, 1023, 600, 10000); //analogread to delay ms desired
You will need to figure out the delay for the desired frequency. For 100hz you need a delay of a little less than 600ms delay. You may also need an "if" statement to set your 0hz frequency which is either "on" or "off" only.
Once you have mastered the above, I would rewrite the program to use the "blink without delay" style.
My UNO system is working now very good as a Voltage-to-Frequency converter (0-5VDC : 1-100Hz). Here the code:
// Voltage-To-Frequency Oscillator (VFO)
// Board: OSEPP UNO R3 Plus (Arduino Compatible)
// 100K ohm Potentiometer
// LED L shows output
int outPin = 13; // Output Frequency 0 - 100 Hz
int AnalogPin = 3; // Potentiometer wiper (middle terminal) connected to analog pin 3
// outside leads to ground and +5V
unsigned int volt = 0; // Variable to hold the voltage read
int volt1 = 0; //Scalar
void setup()
{
pinMode(outPin, OUTPUT); // Sets the pin 13 as output
pinMode(AnalogPin, INPUT); // Sets the pin 3 as input
Serial.begin(115200);
}
void loop()
{
volt = analogRead(AnalogPin) / 10; // Reads the input pin and divide by 10
// to get volt values between 0 to 102
volt1 = volt * 5; // delay(5) between HIGH and LOW corresponds aprox. to 100 Hz
// delay(500) between HIGH and LOW corresponds aprox. to 1 Hz
digitalWrite(13,HIGH);
delay(volt1);
digitalWrite(13,LOW);
delay(volt1);
Serial.println(volt1);
}
// The linearity is good
Good job getting your code to work!
How accurate does your code need to be? If you want, we could improve the code by using the map() function and also by using the millis() function to time your pulses so that you can get rid of delay and do other functions or activate other pins.
Also, if you don't mind me asking what are you using your code for besides a VFO?
I work in the fuel additive injection business. I am exploring the capabilities of the Arduino technology as a tool to our lab bench for testing and calibrating some of our systems and..Yes; any support improving my code to increase the accuracy and reliability of this VFO will really help.
As a base, I need to simulate the fuel flow (10Hz = 1GMP) to feed our additive systems. A good VFO would be valuable specially in those cases where the ratio/blend is low (2-5 ppm). The current code has an error around 3%. If it can be reduced below 1% would be great. Thank you cyclegadget!
Your welcome, look at your PM and continue posting your progress.
Glad I could help,
Mark
Finally, I made my VFO work with good response. Thank you to cyclegadget for his support.
I still need to make as linear as possible the response for low frequencies. It takes almost 7 turns of the pot
to get 10 Hz. Here a picture and the code used.
/*
/ Voltage-To-Frequency Oscillator (VFO) with LCD. Converts 0-5VDC to 1-100 Hz
// Board: OSEPP UNO R3 Plus (Arduino Compatible)
// 100K ohm 10 turn Potentiometer
// LCD (24 x 2) and LED L show output
Modifications based on: Blink without Delay
http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
Turns on and off a light emitting diode(LED) connected to a digital
pin, without using the delay() function. This means that other code
can run at the same time without being interrupted by the LED code.
*/
// Big help and support provided by cyclegadget
// LCD library code
#include <LiquidCrystal.h>
// initializing the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 6, 4, 3, 2);
int DigiPin13 = 13; // Output Frequency 0 - 100 Hz
int AnalogPin3 = 3; // Potentiometer wiper (middle terminal) connected to analog pin 3
// outside leads to ground and +5V
unsigned int volt = 0; // Variable to hold the voltage read
// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
unsigned long previousSample = 0; //last time we counted the pulses
unsigned long volt1 = 0; //Scalar
long pulsecount = 0; //number to store pulses
float Hz = 0.00; //result for Hz
int frecuency; //frequency
int gallons; //gallons obtained scaling x5 the frequency
//unsigned long interval = 5; // interval at which to blink (milliseconds)
unsigned long sampletime = 2000; //time for pulses counted.
void setup() {
Serial.begin(115200);
pinMode(DigiPin13, OUTPUT); // Sets the pin 13 as output
pinMode(AnalogPin3, INPUT); // Sets the pin 3 as input
// set up the LCD's number of columns and rows (24 x 2):
lcd.begin(24, 2);
// Print a messages to the LCD.
lcd.print("Fuel Flow (GPM): ");
lcd.setCursor(0, 1);
lcd.print("Frequency (Hz): ");
}
void loop()
{
volt = analogRead(AnalogPin3); // Reads the input pin to get volt values between 0 to 1023
volt1 = map(volt, 0, 1023, 1, 500); // delay(1) between HIGH and LOW corresponds aprox. to 100 Hz
// delay(500) between HIGH and LOW corresponds aprox. to 1 Hz
//Serial.println(volt1); //print the interval of led toggle
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > volt1) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(DigiPin13, ledState);
pulsecount = pulsecount++; //count the pulse and add to the counted pulses
}
if(currentMillis - previousSample > sampletime) {
// save the last time you calculated Hz
previousSample = currentMillis;
Serial.print("HZ");
Hz = pulsecount / 4; // pulses counted divided by 2 seconds as sampletime = 2 seconds * 2 for each cycle
Serial.println(Hz); //print the Hz
pulsecount = 0; //reset the pulse counter
}
frecuency = Hz;
gallons = Hz * 5;
if (gallons < 10)
{
lcd.setCursor(20, 0);
lcd.print(gallons);
lcd.setCursor(18, 0);
lcd.print(" ");
lcd.setCursor(21, 0);
lcd.print(" ");
}
if ((gallons) > 9 && (gallons < 99))
{
lcd.setCursor(19, 0);
lcd.print(gallons);
lcd.setCursor(18, 0);
lcd.print(" ");
lcd.setCursor(21, 0);
lcd.print(" ");
}
if (gallons > 99)
{
lcd.setCursor(18, 0);
lcd.print(gallons);
lcd.setCursor(21, 0);
lcd.print(" ");
}
if (frecuency < 10)
{
lcd.setCursor(20, 1);
lcd.print(frecuency);
lcd.setCursor(18, 1);
lcd.print(" ");
lcd.setCursor(21, 1);
lcd.print(" ");
}
if ((frecuency) > 9 && (frecuency < 99))
{
lcd.setCursor(19, 1);
lcd.print(frecuency);
lcd.setCursor(18, 1);
lcd.print(" ");
lcd.setCursor(21, 1);
lcd.print(" ");
}
if (frecuency > 99)
{
lcd.setCursor(18, 1);
lcd.print(frecuency);
lcd.setCursor(21, 1);
lcd.print(" ");
}
}
Nice job on the working project! Thank you for giving me credit for helping you!
More fine tuning can be added if you use a second pot and add the micros() function. It will take more work and some thinking to be able to add micros() and milis() together to get proper timing but, it can be done.
If you wanted to you could print Hz in float format to help improve the accuracy.
Good job,
Mark