Arduino AccelStepper Library: Instant speed never reaches set speed - arduino

I am trying to develop a arduino code which runs a stepper motor with C# program via serial communication. I also use Accelstepper library, especially moveTo() and run() functions. I sent maxSpeed and step values as 3500 and 200.000 from C# and motor start to run immediately. I sure that it completes all steps, but after a while, I noticed that stepper motor never reaches its max Speed and it stuck at 3200-3300 range. So because of that finish time is increased. If I give steps more than 200.000, the gap between estimated finish time and real finish time is increased exponentially. If I sent speed as 1000, real speed more or less 970. I have to use acceleration function by the reason of needed torque. Then I search the problem and some people said that it occurs because of Accelstepper library which consist run() function and other stuff that I wrote in the loop section. Especially I could not ensure the reason of the problem is Arduino, AccelStepper library or code that I wrote. Can you please help me to solve problem?
NOTE: Arduino Mega 2560 is used.
Arduino code is below:
#include <AccelStepper.h>
#include <stdio.h>
#define STEP_PIN_C 5 //31
#define DIRECTION_PIN_C 23 //32
#define ENABLE_PIN_C 24 //33
#define SET_ACCELERATION 600.0
AccelStepper stepper(1, STEP_PIN_C, DIRECTION_PIN_C);
unsigned long oldTime=0;
unsigned long now;
float newSpeed;
float maxSpeed = 3500.0;
bool newDataBit, runAllowed = false,addingProg=false,mainProg=false;
char commandChar;
long currentPosition;
long int steps = 0, mainNewStep, addedNewStep,memMainStep;
void checkSerial();
void checkRunning();
void stopMotor();
void runMotor();
void sendInfo();
const unsigned long delayTime = 1000;
unsigned long timer;
int count = 0;
bool running = false;
void setup()
{
Serial.begin(9600);
pinMode(ENABLE_PIN_C, OUTPUT);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.setCurrentPosition(0); //initial value
stepper.setMaxSpeed(0.0); //initial value
stepper.setAcceleration(SET_ACCELERATION); //initial value
}
void loop()
{
sendInfo();
checkRunning();
checkSerial();
}
void checkRunning()
{
if (runAllowed == true)
{
if (stepper.distanceToGo() == 0)
{
stopMotor();
checkSerial();
}
else
{
runMotor();
checkSerial();
}
}
}
void checkSerial()
{
if (Serial.available())
{
newDataBit = true;
commandChar = Serial.read();
}
if (newDataBit == true)
{
///DoStuff depends on what is received as commandChar via serial port
mainProgram(stepper.currentPosition(),newSpeed,mainNewStep);
newDataBit = false;
}
}
void runMotor(){
digitalWrite(ENABLE_PIN_C, LOW);
stepper.run();
running = true;
}
void stopMotor(){
stepper.setCurrentPosition(0);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.stop();
running = false;
timer = millis() + delayTime;
}
void mainProgram(long currentPositionValue,float maxSpeedValue,long stepValue)
{
mainProg = true;
if (stepper.distanceToGo() == 0) //YOLUMU TAMAMLADIM
{
addingProg = false;
steps = stepValue;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(maxSpeedValue);
stepper.moveTo(steps);
}
else
{
steps = stepValue + steps;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(newSpeed);
stepper.moveTo(steps);
}
}
void sendInfo(){
now = millis();
if(now-oldTime > 1000){ //saniyede 1
Serial.print(stepper.currentPosition());
Serial.print(" ");
Serial.print(stepper.isRunning());
Serial.print(" ");
Serial.println(stepper.speed());
oldTime = now;
}
}

From AccelStepper documentation:
The fastest motor speed that can be reliably supported is about 4000
steps per second at a clock frequency of 16 MHz on Arduino such as Uno
etc.
This is if you do nothing else but running the stepper.
You check your serial interface and send multiple lines every second. Both is quite expensive.

Related

Arduino send bad signal to interrupt pin

I have connected coin hopper and coin acceptor to one arduino uno, coin acceptor connected to pin 2, coin hopper to pin 3 - sensor and pin 7 - relay. When coin hopper switch relay, it is executing coininterrupt
for coin hopper I am using this script link
coin acceptor script: link
I need this 2 scripts working on 1 arduino
my code:
#define SENSOR 3
#define RELAY 7
#define ACCEPTOR 2
volatile boolean insert = false;
int pulse=0,count;
char sen;
int temp=0;
unsigned long int timer;
void setup()
{
Serial.begin(9600);
pinMode(SENSOR,INPUT_PULLUP);
pinMode(RELAY,OUTPUT);
sen=digitalRead(SENSOR);
digitalWrite(RELAY, HIGH);
attachInterrupt(digitalPinToInterrupt(ACCEPTOR), coinInterrupt, RISING);
}
void loop()
{
if (insert) {
insert = false;
Serial.println("coin");
delay(1000);
}
if(Serial.available())
{
timer=millis();
// temp is amount to dispense send to arduino
temp=Serial.parseInt();
if(temp>0){
digitalWrite(RELAY,LOW);}
}
sen=(sen<<1)|digitalRead(SENSOR);
// if hopper sensor read drop coin
if(sen==1)
{
timer=millis();
pulse++;
sen&=0x03;
Serial.println("out 1");
//if dispensed coins equal with coins to dispense stop engine
if(pulse==temp)
{
digitalWrite(RELAY,HIGH);
pulse=0;
temp=0;
}
}
// if amount dispensed is not equal with amount to dispense and engine running, stop
if((digitalRead(RELAY)==LOW)&(millis()-timer>2000))
{
digitalWrite(RELAY,HIGH);
pulse=0;
temp=0;
}
}
void coinInterrupt() {
insert = true;
}
I was trying to change pins (arduino uno support interrupts on pin 2 and 3 only) but problem still appears so I guess there is issue in the code
your sketch does not run in this state :
first fix errors :
declare insert as volatile
remove cpulse (not used anywhere)
change 'if()' to (I suppose) 'if (insert) ....'
remove stuff with 'sen' var : simply use if(digitalRead(SENSOR)) or if(!digitalRead(SENSOR))
except if you need to store relay state.
use logical operators like || or && unless you really need bitwise operations
example of result sketch :
#define SENSOR 3
#define RELAY 7
volatile boolean insert = false;
byte amountToDispense = 0;
int pulse = 0;
int temp = 0;
unsigned long int timer;
void setup()
{
Serial.begin(9600);
pinMode(SENSOR, INPUT_PULLUP);
pinMode(RELAY, OUTPUT);
digitalWrite(RELAY, HIGH);
attachInterrupt(digitalPinToInterrupt(2), coinInterrupt, RISING);
}
void loop()
{
if (insert ) {
insert = false;
Serial.println("coin");
delay(1000);
}
if (Serial.available())
{
timer = millis();
temp = Serial.parseInt();
if (temp > 0) {
//amountToDispense = Serial.read() - 48;
digitalWrite(RELAY, LOW);
}
}
if (digitalRead(SENSOR))
{
timer = millis();
pulse++;
Serial.println("out 1");
if (pulse >= temp)
{
digitalWrite(RELAY, HIGH);
pulse = 0;
temp = 0;
}
}
if (!digitalRead(RELAY) && (millis() - timer > 2000))
{
digitalWrite(RELAY, HIGH);
pulse = 0;
temp = 0;
}
}
void coinInterrupt() {
insert = true;
}
What is this supposed to do?
sen=(sen<<1)|digitalRead(SENSOR);
You init sen with digitalRead(SENSOR);
Assuming that pin is LOW when you start the sketch and turns HIGH, sen will become 1.
Next you do sen &= 0x03 so sen is still 1.
Again sen=(sen<<1)|digitalRead(SENSOR); , sen will either be 2 or 3.
Next loop run sen=(sen<<1)|digitalRead(SENSOR); sen is now 4 or 6. and so on...
I don't have time to think about what you want to achieve but this is definitely a problem as you'll only enter if (sen == 1) once and never again.
If this is not sufficient you should probably improve your post as it is unclear what arduino sends bad signal to interrup pin is supposed to mean. That doesn't make sense. Explain the expected behaviour of your program and how it behaves instead. add more comments so it becomes clear what you intend to do with each block of code so we don't have to interpret

How to increase beacon scan sample rate use esp32

I using the esp32 to detection beacon through rssi of beacon, but i meet some problem, first is rssi value is no stable so i need more sample of rssi, the second problem is esp32 scan sample rate is too slow, that faster speed only have one second scan rate, below is my used code
#include <BLEAdvertisedDevice.h>
#include <BLEDevice.h>
#include <BLEScan.h>
const int PIN = 2;
const int CUTOFF = -60;
void setup() {
pinMode(PIN, OUTPUT);
BLEDevice::init("");
}
void loop() {
BLEScan *scan = BLEDevice::getScan();
scan->setActiveScan(true);
BLEScanResults results = scan->start(1);
int best = CUTOFF;
for (int i = 0; i < results.getCount(); i++)
{
BLEAdvertisedDevice device =
results.getDevice(i);
int rssi = device.getRSSI();
if (rssi > best) {
best = rssi;
}
}
digitalWrite(PIN, best > CUTOFF ? HIGH :
LOW);
}
but i want to mor rssi sample, i tried change scan->start(1) to scan->start(0), but result is not return, how to do can solve lower sample problem, or use another board?
The problem is that you do all of the "work" in the loop.
Are you aware of the fact that an ESP32 microcontroller runs at 240 MHz
I guess your loop time is about 400 Nano seconds.
So how would your device know if and how much devices are found.
You are trying to do something with the scan results just after scan start.
To get around this problem there is a BLEAdvertisedDeviceCallbacks in the Arduino for ESP32 code.
This callback will return a result for every device it finds.
Here is an example from GITHUB (ESP32_BLE_Arduino by Neil Kolban)https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/examples/BLE_scan/BLE_scan.ino
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets
/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 5; //In seconds
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results
//faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
}
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
This to give you an idea on how it works, tweak it to meet your own needs.

vw_get_message return false when running motors

I am using Arduino for the first time, my project consists of RF transmitter connected with arduino UNO and a RF receiver connected to Arduino Mega.
I'm try to send data from transmitter and print it on receiver serial using VirtualWire library and every thing is okey for this receiver code:
#include <VirtualWire.h>
int x=9;
int y=8;
int z=10;
int r=7;
void setup()
{
Serial.begin(9600);
pinMode(x,OUTPUT);
pinMode(y,OUTPUT);
pinMode(z,OUTPUT);
pinMode(r,OUTPUT);
vw_setup(2000);
vw_rx_start();
}
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
// Message with a good checksum received, print it.
Serial.print("Got: ");
for (i = 0; i < buflen; i++)
{
Serial.print(buf[i], HEX);
Serial.print(' ');
}
Serial.println();
}
}
Then i add some if statments to run 2 motors (connected to x,y,z,r pins) based on recrived values :
#include <VirtualWire.h>
int x=9;
int y=8;
int z=10;
int r=7;
void setup()
{
Serial.begin(9600);
pinMode(x,OUTPUT);
pinMode(y,OUTPUT);
pinMode(z,OUTPUT);
pinMode(r,OUTPUT);
vw_setup(2000);
vw_rx_start();
}
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
// Message with a good checksum received, print it.
Serial.print("Got: ");
for (i = 0; i < buflen; i++)
{
if (buf[i]==0x77)//Stop motors
{
digitalWrite(x,LOW);
digitalWrite(y,LOW);
digitalWrite(z,LOW);
digitalWrite(r,LOW);
}
else
{
if(buf[i]==0x80)//2 motors clockwise
{
digitalWrite(x,LOW);
digitalWrite(y,HIGH);
digitalWrite(z,HIGH);
digitalWrite(r,LOW);
}
if (buf[i]==0x90)//counter clockwise
{
digitalWrite(x,HIGH);
digitalWrite(y,LOW);
digitalWrite(z,LOW);
digitalWrite(r,HIGH);
}
}
}
Now the problem is that when motors is stop working and I am sending the values that will run it either with or counterclockwise the motor works in the right direction but then does not respond to any data sent.
In short, when the motor stops working and I send data, the receiver receives the values and runs the motor violin is required, but then for example if the motor was working clockwise and sent the order which is running counterclockwise or even stop work, it does not respond and continues to move It was.
I noticed that this bacause when motors runs this function returns false
vw_get_message(buf, &buflen)
But i don't no why!
In VirtualWire library every time you send a new character or a set of characters your buffer will be overwritten. So the problem in this program is with your for loop checking. It will work fine if you just use the following
For example if you are sending characters like 'A', 'B' etc then
if (vw_get_message(buf, &buflen))
{
if(buf[0]=='A')
{
//move forward
}
if(buf[0]=='B')
{
//move backward
}
.... and so on
Hope this helps

Ignore interrupt within time range arduino (lowpass filter)

I'm trying to attach interrupts to the rising edge of a signal (PWM). However, the signal is somewhat noisy when it's HIGH which causes the code to register another interrupt when it should not. I obviously tried to fix this in my circuit but that's not quite working so I moved to the software part.
The question is how I can filter out interrupts within a given frequency range? I need to apply a lowpass filter so that the interrupts do not get triggered when the signal is HIGH. My idea was detach the interrupt for a given amount of time or simply ignore the interrupt if it happens within a certain time range.
I'm just not sure how to achieve this.
This is my code:
unsigned long tsend = 0;
unsigned long techo = 0;
const int SEND = 2;
const int ECHO = 3;
unsigned long telapsed = 0;
unsigned long treal = 0;
void setup() {
Serial.begin(115200);
Serial.println("Start");
pinMode(SEND, INPUT);
pinMode(ECHO, INPUT);
attachInterrupt(digitalPinToInterrupt(SEND), time_send, RISING);
attachInterrupt(digitalPinToInterrupt(ECHO), time_echo, RISING);
}
void loop() {
telapsed = techo - tsend;
if (telapsed > 100 && telapsed < 10000000) {
treal = telapsed;
Serial.println(treal);
}
}
void time_send() {
tsend = micros();
}
void time_echo() {
techo = micros();
}
Below is the signal (yellow) which has a lot of noise. I need to ignore the interrupts when the signal is HIGH. Here is an image of the PWM Signal
I would try the following:
#define DEBOUNCE_TIME 100
void time_send() {
static long last = micros() ;
if (last-tsend > DEBOUNCE_TIME)
tsend = last;
}
void time_echo() {
static long last = micros() ;
if (last-techo > DEBOUNCE_TIME)
techo = last;
}
And adjust DEBOUNCE_TIME until I get a satisfactory result.
const byte intrpt_pin = 18;
volatile unsigned int count = 0;
#define DEBOUNCE_TIME 5000
void isr()
{
cli();
delayMicroseconds(DEBOUNCE_TIME);
sei();
count++;
}
void setup()
{
pinMode(intrpt_pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(intrpt_pin), isr, FALLING);
}
void loop()
{
}
cli() : Disables all interrupts by clearing the global interrupt mask.
sei() : Enables interrupts by setting the global interrupt mask.
So, basically this program will ignore all the interrupt that occurs between these two lines, that is for DEBOUNCE_TIME.
Check your your interrupt bouncing time and adjust DEBOUNCE_TIME accordingly for the best result.

arduino interrupts with servo motor

currently am working on project to open a door with access code using arduino UNO and a servo motor. Normal operation requires entering access code using keypad which is working fine. Another option requires pressing a button that causes an interrupt to rotate the servo motor. My problem is my interrupt only works once and never works again. Plus how do i put the for-loop to rotate the servo motor inside the interrupt function with a delay. I know that is not possible but am calling another function that has the delayMicroseconds but all this is not working. Below is my implementation please help
#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Servo.h>
Servo servo;
const int openButtonPin = 2;
void setup() {
// put your setup code here, to run once:
servo.attach(5);
pinMode(openButtonPin, INPUT); //Pin 2 is input
attachInterrupt(0, enforceOpenAccess, HIGH); // PIN 2
}
void(* resetFunc)(void) = 0;
void loop()
{
//My other keypad implementations go here
}
void myDelay(int x) // function to cause delay in the interrupt
{
for(int i = 0; i<x; i++)
{
delayMicroseconds(1000);
}
}
void enforceOpenAccess() // ISR
{
for(int k =0; k<=180; k+=2)
{
servo.write(k); //rotate the servo
myDelay(30); //delay the rotation of the servo
}
}
The code above is run on arduino UNO being simulated in proteus and the interrupt button is a push button. Please if there is other ways of implementing that but with the same behaviour as I have described above help out. Thanks a lot
There are a couple of problems in the slice of code you posted. Just for completeness, you should post the loop function, since we can't guess what you wrote inside.
Just one comment: did you put a pullup? Otherwise use INPUT_PULLUP instead of INPUT for the button pinmode.
The main one is that you attached the interrupt for the HIGH mode, which will trigger the interrupt any time the pin is up, not on the rising edge. And please use the macro digitalPinToInterrupt to map to the correct pin:
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
Then.. Let's improve the code. You really should use the interrupts only when strictly necessary when you have to respond IMMEDIATELY (= less than a couple of milliseconds) to an input. Here you don't have to, so it's MUCH better to check for the button in the loop (more on turning the motor following)
uint8_t lastState;
void setup()
{
...
lastState = LOW;
}
void loop()
{
uint8_t currentState = digitalRead(openButtonPin);
if ((currentState != lastState) && (currentState == HIGH))
{
// Start turning the motor
}
lastState = currentState;
...
}
This will enable you to properly debounce the button too:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT); //Pin 2 is input
debouncer.attach(openButtonPin);
debouncer.interval(5); // interval in ms
}
void loop()
{
debouncer.update();
if (debouncer.rose())
{
// Start turning the motor
}
...
}
If, on the other way, you REALLY want to use the interrupts (because waiting for a couple of milliseconds is too much for you), you should do something like this:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT);
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
}
void loop()
{
...
}
void enforceOpenAccess() // ISR
{
// Start turning the motor
}
It looks like your code? No, because now we'll speak about turning the motor
You should NOT use delays to make steps, because otherwise you will wait for 30ms * 180 steps = 5.4s before being able to do anything else.
You can, however, make a sort of reduced state machine. You want your servo to move from 0 to 180 in steps of 1. So let's code the "don't move" state with any value greater than 180, and consequently we can do something like this in the loop:
unsigned long lastServoTime;
uint8_t servoPosition = 255;
const int timeBetweenSteps_in_ms = 30;
void loop()
{
...
if (servoPosition <= 180)
{ // servo should move
if ((millis() - lastServoTime) >= timeBetweenSteps_in_ms)
{
lastServoTime += timeBetweenSteps_in_ms;
servoPosition++;
if (servoPosition <= 180)
servo.write(servoPosition);
}
}
}
Then, using any of the previous examples, instead of // Start turning the motor write
lastServoTime = millis();
servoPosition = 0;
servo.write(servoPosition);
This way you won't block the main loop even when the button is pressed
This is what is in my loop()
char key = keypad.getKey();
if(key)
{
if(j < 10)
{
studentNumber[j] = key;
//holdMaskedNumber[j] = '*';
lcd.setCursor(0,2);
lcd.print(String(studentNumber));
if(j == 9)
{
studentNumber[9] = '\0';
//holdMaskedNumber[9] = 0;
lcd.clear();
//String number = String(studentNumber);
//lcd.print(number);
//delay(1000);
//lcd.clear();
lcd.print("Access Code");
}
j++;
}
else
{
if(i < 5)
{
accessCode[i] = key;
holdMaskedCode[i] = '*';
lcd.setCursor(1,2);
lcd.print(String(holdMaskedCode));
if(i == 4)
{
holdMaskedCode[5] = '\0';
accessCode[5] = '\0';
//lcd.clear();
//lcd.setCursor(0,0);
//accessCodeString = String(accessCode);
//lcd.print(accessCodeString);
//delay(1000);
lcd.clear();
for(int i =0; i<6; i++)
{
lcd.print("Please wait.");
delay(500);
lcd.clear();
lcd.print("Please wait..");
delay(500);
lcd.clear();
lcd.print("Please wait...");
delay(500);
lcd.clear();
}
digitalWrite(4, HIGH);
lcd.print("Access Granted");
for(int k =0; k<=180; k+=2)
{
servo.write(k);
delay(30);
}
resetFunc();
}
i++;
}
}
}

Resources