i have used arduinos for some time for other small projects (car glow plug timers, based on temperature sensor input), but now I am trying do do something different.
I have an old motorcycle with YPVS (exhaust power valve). my original servo, was controlled by the original CDI wich isn't working... I fitted a universal CDI because i couldn't find one that would work with the YPVS controller.
So I came up with this ideia and I built a "bench" valve controller based on RPM input (sent by hall effect sensor 3144).
I wrote this program (quite simple actually...) using the example of freqmeasure library to get frequency.
#include <ServoTimer2.h>
#include <FreqMeasure.h>
ServoTimer2 servo1;
int YPVS=3;
int angle; // angle in degrees
int anglem; // angle in servotimer2 value 750 to 2200
float frequency;
int rpm;
void setup() {
Serial.begin(57600);
pinMode(YPVS,OUTPUT);
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
servo1.attach(YPVS);
FreqMeasure.begin();
}
double sum=0;
int count=0;
void loop() {
if (FreqMeasure.available()) {
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 5) {
float frequency = FreqMeasure.countToFrequency(sum / count);
rpm=frequency*60;
angle=(18./500.)*(rpm-4500.); // equation to find valve position in degrees, based in rpm input
if(rpm<1700) {
angle=180;
}
if(rpm>1700 && rpm<4500)
{
angle=0;
}
if(rpm>9500)
{
angle=180;
}
Serial.print("frequency - ");
Serial.print(frequency);
Serial.print(" - RPM -");
Serial.print(rpm);
Serial.print(" - angleval - ");
Serial.println(anglem);
sum = 0;
count = 0;
}
anglem=2200-angle*(145./18.); //equation to convert degrees to sertimver2 value
servo1.write(anglem);
}
}
works 100% in bench but I'm only having one problem... when the bike is at working temperature, in order to start, the valve must be openned ... wich means i need it to go do 0 degrees when no frequency is detected!.
I tried using "else" in the end of the first condition, but when i add else the program wont work at all and the servo goes mad... any ideias how to overcome this?
another problem is the start, it should close valve, and then open, but the commands in void setup don't seem to be working... other than that everything works fine!
#include <ServoTimer2.h>
#include <FreqMeasure.h>
ServoTimer2 servo1;
int YPVS=3;
int angle; // angle in degrees
int anglem; // angle in servotimer2 value 750 to 2200
float frequency;
int rpm;
void setup() {
Serial.begin(57600);
pinMode(YPVS,OUTPUT);
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
servo1.attach(YPVS);
FreqMeasure.begin();
}
double sum=0;
int count=0;
void loop() {
if (FreqMeasure.available()) {
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 5) {
float frequency = FreqMeasure.countToFrequency(sum / count);
rpm=frequency*60;
angle=(18./500.)*(rpm-4500.); // equation to find valve position in degrees, based in rpm input
if(rpm<1700) {
angle=180;
}
if(rpm>1700 && rpm<4500)
{
angle=0;
}
if(rpm>9500)
{
angle=180;
}
Serial.print("frequency - ");
Serial.print(frequency);
Serial.print(" - RPM -");
Serial.print(rpm);
Serial.print(" - angleval - ");
Serial.println(anglem);
sum = 0;
count = 0;
}
anglem=2200-angle*(145./18.); //equation to convert degrees to sertimver2 value
servo1.write(anglem);
}
else {
angle=180;
}
}
this way wont work at all, servo goes mad betwheen 0 and 180 a hundred times per second...
Maybe it doesn't like being set to 180 several thousand times a second... just a guess. That, or possibly, it is actually the case that the frequency you are sampling is unstable, flip flopping in and out just as it behaves.
You could put in a serial debug statement to find out more about that. And/or put a 'delay(1000);' in the loop() to gain some visibility to what is going on.
sorry because english is not my native language, but i think i understood what you're saying!
when i put a delay in the loop(), somehow it affects the readings! even with a smaller delay (50,100,500,1000, the values I tried) inside the loop, the measured frequency won't be correct...
freqmeasure measures time betwheen pulses, and somehow messing with delay seems to affect readings...?
That would indicate a very poor program design. By the way, your formatting is awkward to read, use ctrl-T in the IDE to format it in a standard way:
#include <ServoTimer2.h>
#include <FreqMeasure.h>
ServoTimer2 servo1;
int YPVS = 3;
int angle; // angle in degrees
int anglem; // angle in servotimer2 value 750 to 2200
float frequency;
int rpm;
void setup() {
Serial.begin(57600);
pinMode(YPVS, OUTPUT);
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
servo1.attach(YPVS);
FreqMeasure.begin();
}
double sum = 0;
int count = 0;
void loop() {
if (FreqMeasure.available()) {
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 5) {
float frequency = FreqMeasure.countToFrequency(sum / count);
rpm = frequency * 60;
angle = (18. / 500.) * (rpm - 4500.); // equation to find valve position in degrees, based in rpm input
if (rpm < 1700) {
angle = 180;
}
if (rpm > 1700 && rpm < 4500)
{
angle = 0;
}
if (rpm > 9500)
{
angle = 180;
}
Serial.print("frequency - ");
Serial.print(frequency);
Serial.print(" - RPM -");
Serial.print(rpm);
Serial.print(" - angleval - ");
Serial.println(anglem);
sum = 0;
count = 0;
}
anglem = 2200 - angle * (145. / 18.); //equation to convert degrees to sertimver2 value
servo1.write(anglem);
}
else {
angle = 180;
}
}
Are you sure that the servo library is compatible with the frequencyMeasure library? They likely both use hardware timers, you may have a timer conflict.
@vinniept I believe this is a direction that you will like to go with your program
using a blink without delay timer in this manner allows the default 0 RPM mode to be detected after a failure to get a reading within a short time period.
using blink without delay I added a 20ms delay between servo write.
I also moved the serial print to a standalone blink without delay loop to prevent spamming of the serial port
Z
#include <ServoTimer2.h>
#include <FreqMeasure.h>
ServoTimer2 servo1;
int YPVS = 3;
int angle; // angle in degrees
int anglem; // angle in servotimer2 value 750 to 2200
float frequency;
int rpm;
unsigned long Timeout;
void setup() {
Serial.begin(57600);
pinMode(YPVS, OUTPUT);
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
servo1.attach(YPVS);
FreqMeasure.begin();
}
double sum = 0;
int count = 0;
void loop() {
if (FreqMeasure.available()) {
Timeout = millis();
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 5) {
float frequency = FreqMeasure.countToFrequency(sum / count);
rpm = frequency * 60;
angle = (18. / 500.) * (rpm - 4500.); // equation to find valve position in degrees, based in rpm input
if (rpm < 1700) {
angle = 180;
}
if (rpm > 1700 && rpm < 4500)
{
angle = 0;
}
if (rpm > 9500)
{
angle = 180;
}
sum = 0;
count = 0;
}
}
if ((millis() - Timeout) >= (200)) { // After 200 MS has passed without a reading taking place set angle to 180.
angle = 180;
}
//These following sections can stand alone and have different durations as the angle, frequency, rpm, and angle variables are global and will hold their last values between loops.
static unsigned long ServoWriteDelayTimer;
if ((millis() - ServoWriteDeaayTimer) >= (20)) { // a 20 ms delay between servo writes should be appropriate
ServoWriteDeaayTimer = millis();
anglem = 2200 - angle * (145. / 18.); //equation to convert degrees to sertimver2 value
servo1.write(anglem);
}
// Serial print becomes an issue if you try to spam the serail port.
static unsigned long SpamTimer;
if ((millis() - SpamTimer) >= (50)) {
SpamTimer = millis();
Serial.print("frequency - ");
Serial.print(frequency);
Serial.print(" - RPM -");
Serial.print(rpm);
Serial.print(" - angleval - ");
Serial.println(anglem);
}
}
that is why I am using servotimer2, because i tried using servo.h in the first place, but wouldn't work at all! wouldn't even get an output at pin3...
thank you for the Ctrl+T tip about format, like I said earlier, I'm newbie
thank you a lot for that! got my program better for sure! but still wont show me 0... i have just tested it, and it will keep printing the last value...
@vinniept thank you a lot for that! got my program better for sure!
Welcome
but still won't show me 0... I have just tested it, and it will keep printing the last value...
Try Changing this to an "unsigned long"
int count = 0;
to unsigned long count = 0;
uint32_t is identical to unsigned long
the definition of FreqMeasureClass::read
shows it returns a uint32_t in outer words an unsigned int with 32 bits or an unsigned long integer uint32_t FreqMeasureClass::read(void);
While this may not solve the problem it is a step in the right direction.
Z
so we can see if the FreqMeasure.available(); returns true even when there is nothing triggering it.
Also you will want to change this line:
// average several reading together
unsigned long value;
while((value = FreqMeasure.read()) != 0xFFFFFFFF ){ // clear out all readings in the buffer
sum = sum + value ;
count = count + 1;
}
The source code shows why this is necessary the frequency counter buffers up to 12 readings
#define FREQMEASURE_BUFFER_LEN 12
// and
uint32_t FreqMeasureClass::read(void)
{
uint8_t head, tail; // FIFO Buffer Pointers
uint32_t value;
head = buffer_head;
tail = buffer_tail;
if (head == tail) return 0xFFFFFFFF; // <<< when no reading are available: return 0xFFFFFFFF
tail = tail + 1;
if (tail >= FREQMEASURE_BUFFER_LEN) tail = 0;
value = buffer_value[tail];
buffer_tail = tail;
return value;
}
again, TOP, thank you for your time!!! its working like a champ now!!! the "while" function seems to solve it!
i only have one problem now, in void setup, when i reset arduino, servo should go from 0 to 180 (called cleaning process) but for some reason, when i reset it, it stays in the last position, having the commands in setup, or not having them, seems to make no difference... is there any way to overcome thjis? or what is wrong?
again, thank you !!!
current code
#include <ServoTimer2.h>
#include <FreqMeasure.h>
ServoTimer2 servo1;
int YPVS = 3;
int angle; // angle in degrees
int anglem; // angle in servotimer2 value 750 to 2200
float frequency;
int rpm;
unsigned long Timeout;
void setup() {
Serial.begin(57600);
pinMode(YPVS, OUTPUT);
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
servo1.attach(YPVS);
FreqMeasure.begin();
}
double sum = 0;
int count = 0;
void loop() {
if (FreqMeasure.available()) {
Timeout = millis();
// average several reading together
unsigned long value;
while ((value = FreqMeasure.read()) != 0xFFFFFFFF ) { // clear out all readings in the buffer
sum = sum + value ;
count = count + 1;
}
if (count > 5) {
frequency = FreqMeasure.countToFrequency(sum / count);
rpm = frequency * 60;
angle = (18. / 500.) * (rpm - 4500.); // equation to find valve position in degrees, based in rpm input
if (rpm < 1700) {
angle = 180;
}
if (rpm > 1700 && rpm < 4500)
{
angle = 0;
}
if (rpm > 9500)
{
angle = 180;
}
sum = 0;
count = 0;
}
}
if ((millis() - Timeout) >= (200)) { // After 200 MS has passed without a reading taking place set angle to 180.
angle = 180;
}
static unsigned long ServoWriteDelayTimer;
if ((millis() - ServoWriteDelayTimer) >= (20)) { // a 20 ms delay between servo writes should be appropriate
ServoWriteDelayTimer = millis();
anglem = 2200 - angle * (145. / 18.); //equation to convert degrees to sertimver2 value
servo1.write(anglem);
}
// Serial print becomes an issue if you try to spam the serail port.
static unsigned long SpamTimer;
if ((millis() - SpamTimer) >= (20)) {
SpamTimer = millis();
Serial.print("frequency - ");
Serial.print(frequency);
Serial.print(" - RPM -");
Serial.print(rpm);
Serial.print(" - angleval - ");
Serial.print(anglem);
Serial.print(" - Timeout Timer- ");
Serial.println(millis() - Timeout);
Serial.println();
}
}
void setup() {
Serial.begin(57600);
servo1.attach(YPVS); // you don't need pinMode the ServoTimer2 library takes care of that
while(!(servo1.attached())); // Wait here till we know servo1 is attached
servo1.write(2200); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(750);
delay(800);
FreqMeasure.begin();
}
EDIT: edited code
Also I would do this:
int convertAngle(int angle) {
return 2200 - angle * (145. / 18.);
}
void setup() {
Serial.begin(57600);
servo1.attach(YPVS); // you don't need pinMode Aervo takes care of that
while(!(servo1.attached)); // Wait here till we know servo1 is attached
servo1.write(convertAngle(0)); // called cleaning process, first closes valve to 0 degrees, and then goes full open
delay(800);
servo1.write(convertAngle(180));
delay(800);
FreqMeasure.begin();
}
now i have more problems , not really related to arduino -.-'''
I was using a hall effect sensor, to measure frequency... mounted it on the bike flywheel near the small outside magnet of the flywheel (the one that triggers the pickup coil).
it works 100% until 5000/6000rpm, but above that it switches on ... the flywheel is full of magnets on the inside, and with rpm I think the magnetic field is affecting the hall effect sensor!