First a bit of background. I am attempting to make an LED glow and a buzzer produce a tone that sweeps smoothly up and down in frequency, like an air raid siren. I am using an Arduino Uno, connected to an ATTiny85 chip operating at 8hz clock speed. An SPDN contact switch is used to provide input on 4, while 0 and 1 go out to the positive legs of the buzzer and LED respectively. Suitable resistors are being used to limit current, which is 5v from the Arduino board.
Now, my problem. I can produce a constant tone at any frequency I like. I can produce a tone that goes back and forth between two tones like a UK police siren (Dee-Daa-Dee-Daa etc) but I am unable to generate a smooth transition between two tones. The LED works as expected.
What I actually observe is a single tone that does not vary. Once or twice I've managed to produce a tone that varies, but randomly within the given range rather than smoothly.
I am not using the tone() Arduino command and would prefer not to, as it is not best suited for what I am trying to accomplish.
Here is my code:
const float pi2 = 6.28318530717;
const int buzzer = 0;
const int light = 1;
const int button = 4;
// Set up the pins as input and output
void setup() {
pinMode(buzzer, OUTPUT);
pinMode(light, OUTPUT);
pinMode(button, INPUT);
}
bool buzzerState = LOW;
float nextFlip = 0;
// Generates a sine wave for the given uptime, with a period and offset (in milliseconds).
float sineWave(float uptime, float period, float offset, float minimum, float maximum) {
float s = sin(((uptime + offset) * pi2) / period);
// Normalise the result between minimum and maximum
return (s + 1) / 2 * (maximum - minimum) + minimum;
}
// Returns the time between buzzer inversions based on a given system uptime.
float frequency(float uptime) {
return sineWave(uptime, 5000, 0, 1, 10);
}
// Main loop
void loop() {
// Check button state and turn the light on or off
bool buttonDown = digitalRead(button);
digitalWrite(light, buttonDown);
// Check to see if it's time for the next buzzer inversion
float m = micros();
if (!buttonDown || m < nextFlip) return;
// Get the inverse of the current buzzer state
if (buzzerState == HIGH) {
buzzerState = LOW;
} else {
buzzerState = HIGH;
}
// Write the new buzzer state
digitalWrite(buzzer, buzzerState);
// Decide when the next inversion will occur
nextFlip = m + frequency(m);
}
Silly mistake! I finally noticed: I'm reading micros() where I meant to read millis() - in other words, it was oscillating, just a thousand times faster than I intended it to! Multiplying all values up by a factor of 1000 in the sine wave function produced a lovely oscillation.
Related
I am working with an hall sensor for counting the RPM of my wheel. I am using following sensor :
My code as follows :
int hall_pin = 3; // digital pin
float hall_threshold = 5.0;
float count = 0;
void setup() {
pinMode(hall_pin,INPUT);// put your setup code here, to run once:
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(hall_pin),hall_ISR,RISING);
}
void loop() {
count = 0.0;
float start = micros();
while(1){
if(count >= hall_threshold){
break;
}
}
float end_time = micros();
float time_passed = (end_time - start)/1000000.0; // in seconds
float rpm = (count/time_passed)*60.0;
Serial.println(rpm);
delay(10000);
}
void hall_ISR()
{
count+=1.0;
}
I am using digital pin 3 to read from sensor and count the number of times it detects magnetic field using interrupt. Since the sensor outputs 1 on detecting, I have used a RISING interrupt. Once the count is greater than 5 then the control comes out of the infinite loop and calculates the RPM. The problem is this interrupt is getting triggered multiple times. I have tried using detachInterrupt(hall_pin) but it is not working. I also tried to decrease the sensitivity of hall sensor using the trimmer provided.
I am sure the problem is not with the sensor, it is with the interrupt, I guess. Where am I going wrong?
Any help is very much appreciated!
Thank you.
I am using a Remote Control from FlySky. For my robotics project, I want to read PWM from the receiver on an Arduino. I came across 2 options:
pulseIn() arduino function
ISR(PCINTx_vect) (interrupt)
I cant use the first option of pulseIn() because I want my robot to continue with the operation if receiver signal are not coming (Tx not available etc.) So I used ISR.
Most reliable source : Mr. Brookings channel on YouTube.
Here is what I did (Only the required part for 1 axis):
// [R] where R is defined as 0 => [R] == [0]
volatile long CH[4]; //4 pwms to read so array of 4
float IN[3]={0,0,0}; // throttle is directly written
unsigned long timer[4],curr_time;
byte last[4];
void setup(){
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
PCMSK0 |= (1 << PCINT1);
PCMSK0 |= (1 << PCINT2);
PCMSK0 |= (1 << PCINT3);
/* There is some more code here */
Serial.begin(115200);
}
void loop(){
/* There is some more code here */
IN[R] = ((CH[ROLL] - (1500 + R_TRIM))/11.0); // eg.: (1200 - (1500 + 8))/11.0 = -28 (interpreted as setpoint of -28° by the robot)
Serial.println(IN[R]);
}
ISR(PCINT0_vect){
curr_time = micros();
//channel 1 roll
if(PINB & B00000001){
if(last[ROLL] == 0){
last[ROLL] = 1;
timer[ROLL] = curr_time;
}
}
else if(last[ROLL] == 1){
last[ROLL] = 0;
CH[ROLL] = ((curr_time - timer[ROLL]));
}
}
I can read the PWM actually, but the robot keeps showing random twitches in its control at a given set point. I managed to trace the reason and found out that the PWM is insanely ridden by noise. Its not stable like it should be - steady. I have a MATLAB plot I used for analysis:
Signal (IN[R]):
Close up (when Tx stick was in the middle w/o movement) :
There are such spikes coming which is adding up to the control signal eventually making my robot to twitch. I tried some filtering techniques like 'moving average' and '1st and 2nd order exponential filters'. Also checked if it was due to power supplied to it - tried putting a capacitor or an iron core to the power lines but in vain. I can figure out how to remove them as their some constrains :
platform is Arduino Uno (slower in heavy computation)
Control loop shall not go below 100Hz (Currently its at 108Hz exponential filters on 4 axes took it to
~85Hz)
I would appreciate some guidance!
There's no way of telling from this if the input is noisy, or if your code is reading the PWM wrong, of if something else is going on, like external noise on the line, the Arduino's clock jitter, or other interrupts taking time. Also note that micros() on an Arduino Uno only has a resolution of 4µs, not 1µs.
You should check the input for jitter and noise, and try fast code that isn't influenced by other interrupts.
A fairly simple and fast way of getting the PWM pulse width is something like this, preferably without using anything else that uses interrupts:
volatile int pwmPulseWidth = 0;
volatile unsigned long int previousTime = 0;
void setup() {
attachInterrupt(0, rising, RISING);
}
void loop() {
// pwmPulseWidth is available here.
}
void rising() {
attachInterrupt(0, falling, FALLING);
previousTime = micros();
}
void falling() {
attachInterrupt(0, rising, RISING);
pwmPulseWidth = micros() - previousTime;
}
Untested, but it should give you an idea. This will return the width of the PWM pulse.
There are other ways of doing this, of course, like using a timer in capture mode.
Knowing the PWM frequency and the width of the PWM pulse is enough to reconstruct the PWM signal, should you want to.
I'm using Arduino Micro to read from 5 flex sensors and display the corresponding angles to the Serial monitor. I am currently having quite some problems with the oscillating values I am getting from the analogRead(). It doesn't seem to matter whether the pin is connected to a flex sensor or just grounded - the output is oscillating a lot.
Originally everything was being read and outputted just fine but I wanted to have an exact 100Hz sampling frequency and tried to play a bit with Timer Interrupts. And that's when this oscillating behaviour started. I reversed to my original code, which just uses some delay(), and simplified to only read from two pins, but cannot seem to shake off the oscillations.
I think I may have messed up something about ADC when trying to implement Interrupts, but I don't know how to check it or fix it. Please, help me figure out how to fix this!
This is the raw output of analogRead. The drop in values occurs when I bend the flex sensor
And this is the resulting calculated angle. Also oscillating.
Here is my code minimal working example:
int fin;
const int input[5] = {A0,A1,A2,A3,A4}; // the analog pins
int flex[5]; // analog signal read
float flexV;
float flexR[5]; // resistance on the 47k resistor
int angle[5]; // joint angles
const float VCC = 4.98; // Measured voltage of Arduino 5V line
// Measured resistance of the 47k resistors R1-R5
const float R[5] = {45900.0,45900.0,45900.0,45900.0,45900.0};
// Calibration values of resistance measured during straight phase and 90 deg bend phase
const float R_STRAIGHT[5] = {37651.0,37651.0,37651.0,37651.0,37651.0};
const float R_BEND[5] = {71783.0,71783.0,71783.0,71783.0,71783.0};
void setup() {
}
void loop() {
for(fin = 0; fin <= 4; fin++) {
flex[fin] = analogRead(input[fin]);
flexV = flex[fin]*VCC/1023.0;
flexR[fin] = R[fin] * (VCC/flexV - 1.0);
angle[fin] = map(flexR[fin],R_STRAIGHT[fin],R_BEND[fin],0,90.0);
delay(1);
}
Serial.print(angle[0]);
Serial.print(" ");
Serial.print(angle[1]);
Serial.print(" ");
Serial.print(angle[2]);
Serial.print(" ");
Serial.print(angle[3]);
Serial.print(" ");
Serial.print(angle[4]);
Serial.print(" ");
Serial.println(millis());
delay(6);
}
ok, analogReads normally do have a little oscillation, its normal! they are measuring voltage values and depending on the sensor you are using they will oscillate, same idea of measuring voltage using a multi meter. if you want to learn a bit more about this, ADC's conversor is a good way to start.
What you need to do in order to prevent those oscillations is to develop a filter. this could be done on hardware or software. Obviously the software is the easiest way to go.
My tip for you would be to a average filter! it's a simple concept, you will get X readings at the same time of that sensors (values would go up and down on variation) and you would get the avarage value out of it.
Here is a simple example using your code:
int fin;
const int input[5] = {A0,A1,A2,A3,A4}; // the analog pins
int flex[5]; // analog signal read
float flexV;
float flexR[5]; // resistance on the 47k resistor
float average; //Variable to store the sum of measurements
int nSamples = 4; //Number of reading you are going to use
int angle[5]; // joint angles
const float VCC = 4.98; // Measured voltage of Arduino 5V line
// Measured resistance of the 47k resistors R1-R5
const float R[5] = {45900.0,45900.0,45900.0,45900.0,45900.0};
// Calibration values of resistance measured during straight phase and 90 deg bend phase
const float R_STRAIGHT[5] = {37651.0,37651.0,37651.0,37651.0,37651.0};
const float R_BEND[5] = {71783.0,71783.0,71783.0,71783.0,71783.0};
void setup() {
}
void loop() {
for(fin = 0; fin <= 4; fin++) {
/* A new for here to make readings and store them on the average variable */
for(int x = 0; x <= nSamples; x++){
flex[fin] = analogRead(input[fin]);
average = average + flex[fin];
}
/*Do de avarage and clear the value on this variable*/
flex[fin] = average/nSamples;
avarage = 0;
flexV = flex[fin]*VCC/1023.0;
flexR[fin] = R[fin] * (VCC/flexV - 1.0);
angle[fin] = map(flexR[fin],R_STRAIGHT[fin],R_BEND[fin],0,90.0);
delay(1);
}
Serial.print(angle[0]);
Serial.print(" ");
Serial.print(angle[1]);
Serial.print(" ");
Serial.print(angle[2]);
Serial.print(" ");
Serial.print(angle[3]);
Serial.print(" ");
Serial.print(angle[4]);
Serial.print(" ");
Serial.println(millis());
delay(6);
}
The idea here is simple, to smooth the values by doing this average, leading to more consistent values. Obviously, Higher number of samples improve the results.
It's simple math, if you are getting 4 values like: 45, 50, 55, 50, your average would be 50 (45+50+55+50 = 200/nSamples = 50)
I would like to apply an increasing voltage and hold to my output from my arduino UNO. I realize that the arduino does not allow me to output analog values, and thus I decided to use an R2R ladder (with R- 22kohms and 2R- 47kohms). This would allow me to convert to an analog voltage. I made use of the eight digital pins on the arduino, to set up an 8 bit R2R ladder. I am able to output a sine wave, with my current setup, but a little bit unsure on how to output a wave which goes up to the maximum value and stops. (i.e. a wave like given in the picture below).
This wave is basically a triangle wave or even a sine wave which goes up to a max value and stays there (with 200 micro second pulse duration).
I have created a visual of my circuit to better demonstrate my problem:
I also attempted my problem, by outputting a sine wave. My code is as follows:
void setup() {
//set pins 0-7 as outputs
for (int i=0; i<8; i++){
pinMode(i, OUTPUT);
}
}
void loop() {
double value =0;
int check=0; int t=0;
while(check==0){
if (value<254){
value = 127+127*sin(2*3.14*t/100);
//this sends a sine wave centered around (127/255 * 5)=2.5V
//max will reach when t=25
PORTD=value;
delayMicroseconds(4); //wait 4 micro seconds
//this means that the max value will reach at ~25*6 =150 microseconds
}
else{
value =255;
PORTD=value; //just output the max of the sine wave (i.e. 255)
delayMicroseconds(50); //delay to ensure total duration is 150+50=200 microseconds
PORTD=0; //output back a 0
check=1; //condition to exit the loop
}
t=t+1;
}
}
For some reason, the pulse generated is not exactly what I am looking for. Is there something I am doing wrong? Or is there a better implementation for something like this? Additionally, if there is something I am missing in my question, please let me know.
I realize that the arduino does not allow me to output analog values
In order to output analog values use one of the analog outputs of the Arduino.
They are marked with a ~
Here' an example from the Arduino reference:
int ledPin = 9; // LED connected to digital pin 9
int analogPin = 3; // potentiometer connected to analog pin 3
int val = 0; // variable to store the read value
void setup() {
pinMode(ledPin, OUTPUT); // sets the pin as output
}
void loop() {
val = analogRead(analogPin); // read the input pin
analogWrite(ledPin, val / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}
So i'm making a "turret" type thing using two servo motors, controlled by a joystick. The code i'm running works, however it's very jerky and doesn't move that well, especially in diagonal lines. My code is as follows:
#include <Servo.h>
#define LASER 11
int x = 0;
Servo servo_1; // create servo object to control a servo
Servo servo_2;
// Arduino pin numbers
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output
int butt;
int joy_val;
void setup() {
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
servo_1.attach(9);// attaches the servo on pin 9 to the servo object
servo_2.attach(10);
pinMode(LASER, OUTPUT);
digitalWrite(LASER, HIGH);
Serial.begin(9600);
}
void loop() {
joy_val = analogRead(X_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_1.write(joy_val); // sets the servo position according to the joystick value
delay(150);
joy_val = analogRead(Y_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_2.write(joy_val); // sets the servo position according to the joystick
value
delay(150);
delay(15);
butt = digitalRead(SW_pin);
if (butt == LOW){
x = true;
}
if (x == true){
digitalWrite(LASER, LOW);
Serial.print(x);
}
}
I would really appreciate any advice or help, I'm fairly new to arduino :)
Servos are small and light and attempt to move to the position you tell them as quickly as they can. Joysticks can also change values very quickly, and they can also be glitchy. As a result, your servos are constantly making lots of small, quick movements, which can make the turret seem jerky.
I can think of two options, and you might want to do both:
Smooth out the joystick inputs with some low-pass filtering. This generally just means using a weighted average of the current and previous values. The idea is to eliminate a bad reading or two that might happen because of dirty contacts in the potentiometer.
Smooth out the motion. Instead of instantly trying to move the servos directly to the joysticks' current positions, move them toward the target position. On each iteration of the loop, they'll get closer to the target position instead of trying to jump there almost instantaneously.
For #2, there are a couple approaches I like to use.
One is to simply use a weighted average of the servo's current position and the target position. If you move the joystick a fair distance, the turret will swivel quickly but slow down as it approaches the target position.
The other is to use a physical model. Imagine creating a force vector that points from the servos' current position to the joysticks' target position and is proportional to the distance between them. Apply that force to the current point. Also apply a "friction" force that resists the current point's velocity. Numerically integrate the velocity and the position in the loop. If you move the joystick suddenly to a new position, then the turret will accelerate toward it and then slow down as it approaches it. Adjusting the constants used to compute the forces will let you control how "heavy" the mechanism appears to be.
I put a delay proportional to the speed of the servo. Try this (taken from my tutorial: Arduino Servo Motor Basics and Control):
#include <Servo.h>
#include <math.h>
Servo servo_1; // servo controller (multiple can exist)
int servo_pin = 3; // PWM pin for servo control
int joy_pin_x = A0; // pin for x-dir joystick
int joy_pin_y = A1; // pin for y-dir joystick
int offset_x = 0; // subtracting the initial joystick x-location
int offset_y = 0; // subtracting the initial joystick y-location
int pos = 90; // servo starting position aligned with joystick
int prev_deg = 0; // bound for reducing jitter
int x_prev = 0; // bound for reducing jitter
int y_prev = 0; // reducing jitter
void setup() {
servo_1.attach(servo_pin); // start servo control
Serial.begin(9600);
servo_1.write(pos); // move to center (joystick neutral)
Serial.println("Positioned at 90 Degrees");
offset_x = analogRead(joy_pin_x); // initial joystick x-val
offset_y = analogRead(joy_pin_y); // initial joystick y-val
}
void loop() {
int x_val = analogRead(joy_pin_x)-offset_x; // relative joystick x
int y_val = analogRead(joy_pin_y)-offset_y; // relative joystick y
if (abs(x_prev-x_val)<10 and abs(y_prev-y_val)<10){
// reduce jitter
} else {
x_prev = x_val;
y_prev = y_val;
float deg = 180-(int(atan2(x_val,y_val)*(180.0/PI))+90); // angle calc
if (abs(deg-prev_deg)>2 and deg>0 and deg<180){
servo_1.write(deg); // move servo to joystick location
delay(abs(deg-prev_deg)*(10.0/6.0));
prev_deg = deg;
Serial.println(deg); // print out degree
}
}
}
Notice the delay that is functionally dependent on the angle it's moving to - this will 'smooth' the servo and reduce jitter (though not completely remove it).