instead wire the button to the reset switch, read the position, move the elevator, stop when it gets to the other position. then put it into an infinite loop until you reset again.
of course learning to do it properly would be a better idea.
I used a similar solution without changing the wiring, calling a reset function
That seemed to work.
{
asm volatile (" jmp 0");
}
The problem now is that when I enter the moveServo() and the first "if function" (myservo.writeMicroseconds function) I keep staying in that if cycle.
The version of the code I posted has the stopServo() function called by the moveServo(). Previously it was called by the main loop together with the moveServo() but with the same results...
I am trying to verify if the moveServo() should be inside a "while distance" cycle instead of a "if cycle" so to exit to the stopServo as soon as the distance is reached (that would also mean to split the moveServo in two functions, moveServoUp and moveServoDown to manage the right distances).
Since the OP (ax) is not a student and this is for a present, I went ahead and did some recoding.
This below compiles on IDE 1.6.9. It is not as clean as I'd like and no doubt needs some changes.
I don't know which way the servo is oriented, for example. I used the write() command to set the up and down speed (it is a continuous rotation servo) and may have them backward.
I don't have hardware to test this but I hope it has a better chance to be done before it needs to be.
Only way to tell is try it and debug. Sorry.
#include "Arduino.h"
#include <Servo.h>
#include <NewPing.h>
#define TRIGGER_PIN 7 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 7 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
// servo
Servo myservo; // continuous rotation servo
const byte servoStopSpeed = 90; // you may not need to change this
const byte servoUpSpeed = 60; // you may need to change this
const byte servoDownSpeed = 120; // you may need to change this
byte currentSpeed;
char elevatorMoveState;
char previousMoveState;
// button
#define CURRENT_1 1
#define PREVIOUS_2 2
#define BOUNCE_4 4
typedef class button
{
private:
byte arduPin;
byte buttonState; // bit 0 = current, bit 1 = previous, bit 2 = bounce
byte startMs;
byte debounceMs;
public:
button(); // default constructor
void setButton( byte, byte ); // pin, debounce millis
button( byte, byte ); // pin, debounce millis
void setUpButton( void );
byte runButton( void ); // returns buttonState as below
// buttonState: bit 0 = current, bit 1 = previous, bit 2 = bounce
byte buttonRead(); // returns buttonState as above
};
byte buttonState;
const byte elevatorButtonPin = 2; // const won't change
button elevatorButton( elevatorButtonPin, 5 ); // button object created
// sonar
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
const unsigned long sonarReadInterval = 20;
unsigned long sonarReadStart;
int sonarDistance;
const int elevatorTopDistance = 28;
const int elevatorBottomDistance = 7;
const int elevatorCenterDistance = ( elevatorTopDistance - elevatorBottomDistance ) / 2;
void setup()
{
Serial.begin( 250000 ); // set Serial Monitor to match or change this
elevatorButton.setUpButton(); // sets pin mode, must do
myservo.attach(9); //keep the servo stopped
}
void loop()
{
// sonar input task
if ( millis() - sonarReadStart >= sonarReadInterval ) // repeating timed event
{
readSonar();
sonarReadStart += sonarReadInterval;
}
// button input task
buttonState = elevatorButton.runButton();
// elevator button response task, sets elevatorMoveState
if ( buttonState == 2 ) // button just pressed
{
if ( elevatorMoveState != 0 ) // elevator already moving, stop
{
elevatorMoveState = 0;
}
else
{
if ( sonarDistance > elevatorCenterDistance ) // elevator at/near top
{
elevatorMoveState = -1; // down
}
else
{
elevatorMoveState = 1; // up
}
}
}
// servo task
if ( elevatorMoveState != previousMoveState )
{
servo();
}
}
void readSonar()
{
sonarDistance = sonar.ping_cm();
Serial.print("Ping: ");
Serial.print( sonarDistance ); // print distance
Serial.println("cm");
}
void servo()
{
if ( elevatorMoveState == 0 )
{
myservo.write( servoStopSpeed );
currentSpeed = servoStopSpeed;
previousMoveState = 0;
}
else if ( elevatorMoveState > 0 )
{
if ( sonarDistance < elevatorTopDistance ) // servo reached limit
{
if ( currentSpeed != servoUpSpeed ) // so not to send same
{
myservo.write( servoUpSpeed );
currentSpeed = servoUpSpeed;
}
}
else
{
elevatorMoveState = 0;
}
}
else
{
if ( sonarDistance > elevatorBottomDistance ) // servo reached limit
{
if ( currentSpeed != servoDownSpeed ) // so not to send same
{
myservo.write( servoDownSpeed );
currentSpeed = servoDownSpeed;
}
}
else
{
elevatorMoveState = 0;
}
}
}
button::button() // default constructor for arrays, needs the full setup
{
buttonState = CURRENT_1;
}
button::button( byte ap, byte dbm )
{
arduPin = ap;
debounceMs = dbm;
buttonState = CURRENT_1;
};
void button::setButton( byte ap, byte dbm ) // pin, debounce millis
{
arduPin = ap;
debounceMs = dbm;
pinMode( arduPin, INPUT_PULLUP );
};
void button::setUpButton( void )
{
pinMode( arduPin, INPUT_PULLUP );
};
byte button::buttonRead()
{
return buttonState;
};
byte button::runButton( void )
{
// static byte msNow;
buttonState &= BOUNCE_4 | CURRENT_1; // clears previous state bit
buttonState |= ( buttonState & CURRENT_1 ) << 1; // copy current state to previous
buttonState &= BOUNCE_4 | PREVIOUS_2; // clears current state bit
buttonState += digitalRead( arduPin ); // current state loaded into bit 0
// msNow = (byte) millis(); // gets the low byte of millis
if ( buttonState & 3 == CURRENT_1 || buttonState & 3 == PREVIOUS_2 ) // state change detected
{
buttonState ^= BOUNCE_4; // toggles debounce bit
// on 1st and odd # changes since last stable state, debounce is on
// on bounces back to original state, debounce is off
if ( buttonState & BOUNCE_4 )
{
// startMs = msNow; // starts/restarts the bounce clock on any change.
startMs = (byte) millis(); // starts/restarts the bounce clock on any change.
}
}
else if ( buttonState & BOUNCE_4 ) // then wait to clear debounce bit
{
// if ( msNow - startMs >= debounceMs ) // then stable button state achieved
if ( (byte)((byte)millis()) - startMs >= debounceMs ) // then stable button state achieved
{
// understand that stable state means no change for debounceMs. When time
// is over the state bits are manipulated to show a state change.
buttonState &= CURRENT_1; // clear all but the current state bit
if ( buttonState == 0 ) buttonState = PREVIOUS_2; // HIGH->LOW
else buttonState = CURRENT_1; // LOW->HIGH
// buttonState now appears as a debounced state change in bits 0 and 1
}
}
return buttonState;
};
Thank you GoforSmoke!
This works at first try, except for the stopping part (I use my hand over the sensor: when I go near it inverts direction). I will think about that single part.
I am not a student (well...given my nickname, that would be a problem, since I am forty) but I like to learn,
Continuing on my badly begun coding experience, I arrived to a fully functional version of my bad code (I compared mine to yours...)
I understood why I was stuck in the if cycle: that was because, when entering the start servo if, there was nothing that could change the values of the variables that led me in that if cycle.
So - as christmas is coming - I decided to go for a work around.
I searched and used the "while" cycle, and so - as the distance was not read while in the while cycle - I read it again when running the servo.
At the end (and that's another workaround) I did'nt understand why the microseconds write I used seemed to block the code after (rightly) stopping the code. So I decided to detach the servo and apply the reset code trick after each stop.
The main thing I want to implement now is your button managing. I understand now what you meant by "tricky"
well, here is my code: ugly and reduntant, but it works!
#include <Servo.h>
#include <NewPing.h>
Servo myservo;
#define TRIGGER_PIN 7 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 7 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
int distance = 0;
int buttonInput = 2;
int sensorpin = 7; // Pin for SRF05
int buttonState = 0;
int buttonHIGH = 0;
int whatDirection = 1500;
boolean startRunning = false;
boolean runningUp = false;
boolean runningDown = false;
boolean runs = false;
boolean bottonePremuto = false;
unsigned long currentMillis = 0;
const unsigned long moveInterval = 100;
const unsigned long distInterval = 100;
const unsigned long stopInterval = 500;
unsigned long previousMoveMillis = 0;
unsigned long previousDistanceMillis = 0;
unsigned long previousStopMillis = 0;
void setup()
{
Serial.begin(9600);
if (millis() - previousMoveMillis >= moveInterval) {
previousMoveMillis = millis();
previousDistanceMillis = millis();
previousStopMillis = millis();
}
}
void loop()
{
readDistance();
buttonState = digitalRead(buttonInput);
currentMillis = millis();
if ((buttonState == HIGH) && (bottonePremuto == true)) {
bottonePremuto = false;
}
if ((buttonState == HIGH) && (bottonePremuto == false)) {
bottonePremuto = true;
}
if ((bottonePremuto == true) && (startRunning == false)) {
if ((distance >= 28) && (bottonePremuto == true)) {
Serial.println("DECIDED TO GO UP");
myservo.attach(9);
whatDirection = 1200;
runningUp = true;
startRunning = true;
}
else {
Serial.println("DECIDED TO GO DOWN");
myservo.attach(9);
whatDirection = 1800;
runningDown = true;
startRunning = true;
}
moveServo();
}
}
void readDistance() {
if (millis() - previousDistanceMillis >= distInterval) {
Serial.print("Ping: ");
Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range)
currentMillis = millis();
Serial.println("cm");
distance = sonar.ping_cm();
previousDistanceMillis += distInterval;
}
}
void moveServo() {
if ((millis() - previousMoveMillis >= moveInterval) && (startRunning == true)) {
if (whatDirection>1500) {
while (distance<35) {
distance = sonar.ping_cm();
myservo.writeMicroseconds(whatDirection); }
Serial.println("distance is ");
Serial.print(distance);
myservo.writeMicroseconds(1500);
myservo.detach();
asm volatile (" jmp 0");
}
if (whatDirection<1500) {
while (distance>7) {
distance = sonar.ping_cm();
myservo.writeMicroseconds(whatDirection); }
Serial.println("distance is ");
Serial.print(distance);
myservo.writeMicroseconds(1500);
myservo.detach();
asm volatile (" jmp 0");
}
}
previousMoveMillis += moveInterval;
}
What happens if you press the button while the servo is moving?
If you add other things, what will happen when the board is reset?
I wrote:
I don't know which way the servo is oriented, for example. I used the write() command to set the up and down speed (it is a continuous rotation servo) and may have them backward.
These are the servo control constants:
const byte servoStopSpeed = 90; // you may not need to change this
const byte servoUpSpeed = 60; // you may need to change this
const byte servoDownSpeed = 120; // you may need to change this
These are the sonar direction constants:
const int elevatorTopDistance = 28;
const int elevatorBottomDistance = 7;
const int elevatorCenterDistance = ( elevatorTopDistance - elevatorBottomDistance ) / 2;
The button code is ripped from my button library. I didn't want to explain how to put the two files into the project folder or IDE library folder. Make sure to get all the parts if you want to use it, make a simple test sketch to mess with it. It is capable of supporting arrays of buttons.
My code has a good behaviour in terms of button control while the elevator is moving (nothing happens) and you are right: the reset is not a solution. If I put some lights or other they would all turn off. It was just a quick workaround to see the thing working.
I will try some modification to your code in the parts you underlined -
The servoxxxSpeed variables (I am more used to the writeMicroseconds, so the stop speed will become 1500).
Change my servo write() commands to writeMicroseconds(), the servo library docs say that for continuous rotation servos, both commands act the same, but they use different values.
I'd be happier if you set up a servo test sketch that you can set or feed values (with feedback on serial monitor) and see what the servo does. If you need help, sing out!
Best way to troubleshoot or even develop is by parts, reduce variables/unknowns & interactions and bugs/answers get easier to find.
Can you follow the code I wrote?
I have the sonar on a timer to run 50x a second, easily changed.
The button runs all the time to debounce.
The servo is not on any timer, it runs until the sonar shows it should stop. Note how that is done.
The servo is not protected against burning out in cases where it may be held/stuck. A fuse/polyfuse/breaker and/or temperature sensor may be warranted there.
This should allow a user to feed servo values from serial monitor.
It allows servo.write and servo writeMicroseconds, looks for letter M or W then digit string entries.
Usage prints on Serial Monitor.
This compiles but I cannot test it, no servo.
Disconnect the servo from the elevator to use this.
#include "Arduino.h"
#include <Servo.h>
// servo
Servo myservo; // continuous rotation servo
char servoCommand = 'M'; // M for writeMilliseconds
int currentServoSpeed = 1500; // default is zero
void setup()
{
Serial.begin( 250000 ); // set Serial Monitor to match or change this
Serial.println( F( "\nServo test to see what values and commands do." ));
Serial.println( F( "\nEnter W or M followed by digits for send value." ));
Serial.println( F( "W### for servo.write(###)" ));
Serial.println( F( "M#### for servo.writeMicroseconds(####)\n" ));
Serial.println( F( "Make sure that Serial Monitor sends newlines.\n" ));
myservo.attach(9); //keep the servo stopped
}
byte entryState;
void loop()
{
if ( Serial.available())
{
char ch = Serial.read();
switch ( entryState )
{
case 0 : // waiting for serial data entry
if ( ch == 'W' || ch == 'M' )
{
servoCommand = ch;
currentServoSpeed = 0;
entryState = 1;
}
break;
case 1 : // reading serial numeric entry
if ( ch >= '0' && ch <= '9' )
{
if ( currentServoSpeed > 3275 ) // 3276 could overflow on the next operation
{ // it is also more than the 2300 maximum writeMilliseconds() value.
entryState = 2;
}
else // read the available digit into the current value
{
currentServoSpeed *= 10;
currentServoSpeed += ch - '0';
}
}
else
{
entryState = 2;
}
break;
case 2 : // run the command
if ( servoCommand == 'M' )
{
myservo.writeMicroseconds( currentServoSpeed );
}
else if ( servoCommand == 'W' )
{
myservo.write( currentServoSpeed );
}
servoCommand = 0;
entryState = 0;
}
}
}