I'm trying to build a cat sprayer gadget and have borrowed Squirts code and trying to re write it to do what I need it to do.
I'm no coding king, and have just cut and pasted bits from everywhere.
Basically I'm after the servo to do sweep one motion has been detected from the PIR, and have mocked 3 possible codes up. I was just wondering if anyone could look them over and suggest the best one or maybe point out any mistakes I've made.
Thanks, John
#include <ServoTimer1.h>
/*
Code one
*/
int trnPin = 8; // transistor base connected to pin 8
int pirPin = 5; // pir connected to pin 5
ServoTimer1 servo1; // defines the servo
long svrPos = 1; // Initialise a variable for servo position
void setup()
{
servo1.attach(10); //servo on pin 10
pinMode(trnPin, OUTPUT); // set the transistor pin 8 as output to pump
pinMode(pirPin, INPUT); // set the PIR pin 5 as input
digitalWrite(trnPin, LOW); // defines LOW as movement
}
int pinin = 0;
long countint = 0;
void loop()
{
int svrPos = 1;
pinin = digitalRead(pirPin); // reads the PIR sensor
while (pinin == 0){
pinin = digitalRead(pirPin);
}
while(svrPos < 181){
digitalWrite(trnPin, HIGH); // turns pump on
svrPos = svrPos +2; // add 2 to svrPos
delay(50);
servo1.write(svrPos); // sets servo position to svrPos
digitalWrite(trnPin, LOW); // turns pump off
delay(50);
}
delay(3000);
//end of code
#include <ServoTimer1.h>
/*
Code two
*/
int trnPin = 8; // transistor base connected to pin 8
int pirPin = 5; // pir connected to pin 5
ServoTimer1 servo1; // defines the servo
long svrPos = 1; // Initialise a variable for servo position
void setup()
{
servo1.attach(10); //servo on pin 10
pinMode(trnPin, OUTPUT); // set the transistor pin 8 as output to pump
pinMode(pirPin, INPUT); // set the PIR pin 5 as input
digitalWrite(trnPin, LOW); // defines LOW as movement
}
int pinin = 0;
int svrPos = 1;
long countint = 0;
void loop()
{
int svrPos = 1;
pinin = digitalRead(pirPin); // reads the PIR sensor
while (pinin == 0){
pinin = digitalRead(pirPin);
}
for (int i = 1; i > -1; i = i + x){
if (i > 0){
servo1.write(svrPos, i);
digitalWrite(trnPin, HIGH); // turns pump on
delay(15);
if (i == 180) x = -1; // switch direction at peak
digitalWrite(trnPin, LOW); // turns pump off
delay(10);
}
}
delay(3000);
//end of code
/*
Code three
*/
int trnPin = 8; // transistor base connected to pin 8
int pirPin = 5; // pir connected to pin 5
ServoTimer1 servo1; // defines the servo
long svrPos = 1; // Initialise a variable for servo position
void setup()
{
servo1.attach(10); //servo on pin 10
pinMode(trnPin, OUTPUT); // set the transistor pin 8 as output to pump
pinMode(pirPin, INPUT); // set the PIR pin 5 as input
digitalWrite(trnPin, LOW); // defines LOW as movement
}
int pinin = 0;
int svrPos = 1;
void loop()
{
int svrPos = 0;
pinin = digitalRead(pirPin); // reads the PIR sensor
while (pinin == 0){
pinin = digitalRead(pirPin);
}
for(svrPos = 0; svrPos < 180; svrPos += 1){ // goes from 0 degrees to 180 degrees in steps of 1 degree
digitalWrite(trnPin, HIGH); // turns pump on
servo1.write(svrPos); // tell servo to go to position in variable 'svrPos'
delay(15); // waits 15ms for the servo to reach the position
digitalWrite(trnPin, LOW); // turns pump off
delay(15);
}
for(svrPos = 180; svrPos>=1; svrPos-=1){ // goes from 180 degrees to 0 degrees
digitalWrite(trnPin, HIGH); // turns pump on
servo1.write(svrPos); // tell servo to go to position in variable 'svrPos'
delay(15); // waits 15ms for the servo to reach the position
digitalWrite(trnPin, LOW); // turns pump off
delay(15);
}
delay(3000);
//end of code
#include <ServoTimer1.h>
/*
Original code
"Squirt". Jonathan Robson Feb 2009.
A PIR activates a servo & pump. Servo moves its arm to a random position between 60 and
120 degrees, fires a pump for half second and returns to center (90 degrees). Cycle repeats
3 times at random intervals between half and 3 seconds then waits to detect further movement.
Circuit based on http://itp.nyu.edu/physcomp/Tutorials/HighCurrentLoads
PIR code adapted from http://www.liquidware.org/view.php?id=63
Random code adapted from www.arduino.cc/en/Tutorial/Blink & www.arduino.cc/en/Reference/Random
Servo code adapted from http://www.ladyada.net/make/mshield/use.html
*/
int transistorPin = 8; // transistor base connected to pin 8
ServoTimer1 servo1; // defines the servo
long randOff = 0; // Initialise a variable for the OFF time between shots
long randNumber; // Initialise a variable for servo position angle and delay between shots
void setup()
{
servo1.attach(10); //servo on pin 10
pinMode(8, OUTPUT); // set the transistor pin 8 as output to pump
pinMode(5, INPUT); // set the PIR pin 5 as input
digitalWrite(8, LOW); // defines LOW as movement
randomSeed (analogRead (0)); // randomize
}
int pinin = 0;
long countint = 0;
void loop()
{
pinin = digitalRead(5); // reads the PIR sensor
while (pinin == 0)
{
pinin = digitalRead(5);
}
servo1.write(90); //sets servo to center
randOff = random (500, 3000); // generate OFF time between 1/2 and 3 seconds
delay(randOff); // waits for a random time while OFF
servo1.write(randNumber = random(60, 120)); // servo to random position within 30 degrees of center
delay(400); //gives servo time to get there
digitalWrite(transistorPin, HIGH); // turns pump on
delay(500); //fires pump for 1/2 second
digitalWrite(transistorPin, LOW); // turns pump off
servo1.write(90); // moves servo back to center
randOff = random (500, 3000); // generate new OFF time between 1/2 and 3 seconds and repeat
delay(randOff);
servo1.write(randNumber = random(60, 120));
delay(400);
digitalWrite(transistorPin, HIGH);
delay(500);
digitalWrite(transistorPin, LOW);
servo1.write(90);
randOff = random (500, 3000); // generate OFF time between 1/2 and 3 seconds and repeat
delay(randOff);
servo1.write(randNumber = random(60, 120));
delay(400);
digitalWrite(transistorPin, HIGH);
delay(500);
digitalWrite(transistorPin, LOW);
servo1.write(90);
delay(3000); // gives the PIR time to "settle" before reading again
}
pinin = digitalRead(pirPin); // reads the PIR sensor
while (pinin == 0){
pinin = digitalRead(pirPin);
}
The digitalRead() function returns HIGH or LOW, not 0 or 1.
while(digitalRead(pirPin) == LOW);
Does the same thing as your 4 lines of code, in 1/4 of the space.
while(svrPos < 181){
digitalWrite(trnPin, HIGH); // turns pump on
If you are worried that the pin won't stay on, you are using the wrong hardware. Turn the pin on ONCE.
The while loop is the wrong construct. Use a more logical for loop.
//end of code
No, it isn't.
Why are you using ServoTimer1 instead of Servo?
for (int i = 1; i > -1; i = i + x){
if (i > 0){
servo1.write(svrPos, i);
digitalWrite(trnPin, HIGH); // turns pump on
delay(15);
if (i == 180) x = -1; // switch direction at peak
Rubbish. Overly complicated. Use two for loops with logical limits.
Code 3 is the easiest to understand, but still has (some of) the issues mentioned above.
You choose whichever code you think works best for you. If it does what you want - great.
If it doesn't then tell us what the problem is - what you want to happen, and what actually happens.
Life is too short (and I'm too lazy) to read through 3 different versions.
Read lots of Threads on the Forum and lots of coding tutorials and examples and compare them to your own coding technique.
Carefully work through your code line by line in your head, and with a pencil and paper, and think carefully about what each line does and whether it is most appropriate where it is.
Ask yourself whether you will understand the code in a single read-through after a 6-month absence.
Has a bit of a tinker, made changes suggested. I can't test the code because my leave isn't for another 8 days, and the more I can do here, the more time I can enjoy on my leave.
If you are worried that the pin won't stay on, you are using the wrong hardware. Turn the pin on ONCE.
I'm turning the pin on and off because I want the water pump to pulse with every change of servo position. Admittedly, the delay might not be enough, and I'll adjust it when I get it up loaded.
Here is the adjusted code, is it looking any better?
#include <Servo.h>
/*
Code three
*/
int trnPin = 8; // transistor base connected to pin 8
int pirPin = 5; // pir connected to pin 5
Servo myservo ; // defines the servo
long svrPos = 0; // Initialise a variable for servo position
void setup()
{
myservo .attach(10); //servo on pin 10
pinMode(trnPin, OUTPUT); // set the transistor pin 8 as output to pump
pinMode(pirPin, INPUT); // set the PIR pin 5 as input
digitalWrite(trnPin, LOW); // defines LOW as movement
}
void loop()
{
delay(3000);
while(digitalRead(pirPin) == LOW);
}
for(svrPos = 0; svrPos < 180; svrPos += 1){ // goes from 0 degrees to 180 degrees in steps of 1 degree
digitalWrite(trnPin, HIGH); // turns pump on
myservo .write(svrPos); // tell servo to go to position in variable 'svrPos'
delay(15); // waits 15ms for the servo to reach the position
digitalWrite(trnPin, LOW); // turns pump off
delay(15);
}
for(svrPos = 180; svrPos>=1; svrPos-=1){ // goes from 180 degrees to 0 degrees
digitalWrite(trnPin, HIGH); // turns pump on
myservo .write(svrPos); // tell servo to go to position in variable 'svrPos'
delay(15); // waits 15ms for the servo to reach the position
digitalWrite(trnPin, LOW); // turns pump off
delay(15);
}
//end of code?
I'm guessing that's a no then.
I was going to try and compile it tonight when i finish my shift.
I'm not going for pretty, I'm going for code that works, but like I said, I'm no coder, I'm trying to get in to it though because I prefer to make things instead of buying. A few little pointers is ask I'm after.
Ah right, I thought the code would wait at the while statement until it was true, then run through the rest of the script. I see what you mean. So instead of...
while(digitalRead(pirPin) == LOW);
}
Use...
while(digitalRead(pirPin) == LOW);
{
Then close the function at the end...
delay(15);
}
}
The three second delay is basically so it only runs through once in say five seconds or so. Cat passes, trigger function, wait 3 seconds. I suppose that can be deleted, I was just trying to stop the code running through straight after its just ran through.
So, just out of interest, if the delay is before the while statement, will it not have a pause, wait at the while statement until the sensor is triggered then react straight to the servo function anyway?
I was just trying to stop the code running through straight after its just ran through.
Admirable goal. And a perfect opportunity to read, understand, and embrace the blink without delay example's philosophy, and banish delay() to dustbin.
So, just out of interest, if the delay is before the while statement, will it not have a pause, wait at the while statement until the sensor is triggered then react straight to the servo function anyway?
Yes. But, if the purpose of the device is to keep the cat away from something, giving it three seconds to start pissing/spraying doesn't seem like what you want. Is it?
This blink without delay is a ball ache, I think I've totally complicated what I had haha
#include <Servo.h>
/*
Code
*/
// Which pins are connected to which
int pumpPin = 8; // transistor base pump connected to pin 8
int pirPin = 5; // pir connected to pin 5
// Defines and vars
Servo myservo ; // defines the servo
long svrPos = 0; // Initialise a variable for servo position
long previousMillis = 0;
long pumpinterval = 100;
long servointerval = 10;
long interval = 1000;
// Variable holding the timer value so far. One for each "Timer"
unsigned long pumptimer;
unsigned long servotimer;
void setup()
{
myservo.attach(10); //servo on pin 10
pinMode(trnPin, OUTPUT); // set the pin 8 as output to pump
pinMode(pirPin, INPUT); // set the PIR pin 5 as input
digitalWrite(trnPin, LOW); // defines LOW as movement
}
void togglePump ()
{
if (digitalRead (pumpPin) == LOW){
digitalWrite (pumpPin, HIGH);
}else{
digitalWrite (pumpPin, LOW);
pumptimer = millis ();
}
}
void toggleServo ()
{
for(svrPos = 0; svrPos < 180; svrPos += 1){ // 0 to 180
myservo.write(svrPos); // servo to variable 'svrPos'
servotimer = millis ();
}
for( svrPos = 180; svrPos>=1; svrPos-=1 ){ // 180 to 0
myservo.write(svrPos); // servo to variable 'svrPos'
servotimer = millis ();
}
}
void loop()
{
while(digitalRead(pirPin) == LOW){
if ( (millis () - pumptimer) >= pumpinterval){
togglePump ();
}
if ( (millis () - servotimer) >= servointerval){
toggleServo ();
}
}
Step 1 is to run your code through the Auto Format tool provided by the Arduino IDE (Ctrl + T is the shortcut) before your post. This gets rid of funky indentation that causes headaches for those trying to read it and help.
99% of the time, when someone with a for loop and a delay tries to switch to a non-blocking approach, the for loop has to go. You aren't lucky enough to be in the 1%.
I realised that and edited it seconds before you posted.
Apart from formatting, am I on the right track with what I'm trying to achieve with this code?
tripstar76:
Apart from formatting, am I on the right track with what I'm trying to achieve with this code?
No. Updating servoTime 360 times before it's value is pointless. If you want to replace a for loop + delay() combo with non blocking code, you have to get rid of the for loop and handle entry and exit points yourself, as well as taking care of the typical stuff for loops do (initialize a value, check a condition, change a value).
This is bound to create errors. There is no opening bracket here. That would mean it closes of the enclosing section or function. First off, never use the empty statement at the end of a while() or if(). You will waste loads of time trying to fix a bug because you will overlook this error. It is the same category as using '=' where '==' was intended.
To indicate you are doing nothing, use empty braces for readability like so:
I've modified multiple things code and ended up with this...
// SeveralThingsAtTheSameTime.ino
// An expansion of the BlinkWithoutDelay concept to illustrate how a script
// can appear to do several things at the same time
// this sketch does the following
// it blinks the onboard LED (as in the blinkWithoutDelay sketch)
// it blinks two external LEDs (LedA and LedB) that are connected to pins 12 and 11.
// it turns another Led (buttonLed connected to pin 10) on or off whenever a button
// connected to pin 7 is pressed
// it sweeps a servo (connected to pin 5) back and forth at different speeds
// One leg of each LED should be connected to the relevant pin and the other leg should be connected to a
// resistor of 470 ohms or more and the other end of the resistor to the Arduino GND.
// If the LED doesn't light its probably connected the wrong way round.
// On my Uno and Mega the "button" is just a piece of wire inserted into pin 7.
// Touching the end of the wire with a moist finger is sufficient to cause the switching action
// Of course a proper press-on-release-off button switch could also be used!
// The Arduino is not capable of supplying enough 5v power to operate a servo
// The servo should have it's own power supply and the power supply Ground should
// be connected to the Arduino Ground.
// The sketch is written to illustrate a few different programming features.
// The use of many functions with short pieces of code.
// Short pieces of code are much easier to follow and debug
// The use of variables to record the state of something (e.g. pumpState) as a means to
// enable the different functions to determine what to do.
// The use of millis() to manage the timing of activities
// The definition of all numbers used by the program at the top of the sketch where
// they can easily be found if they need to be changed
//========================================
// ----------LIBRARIES--------------
#include <Servo.h>
// --------CONSTANTS (won't change)---------------
const int pumpPin = 8; // the pin numbers for the PUMP
const int pirPin = 5; // the pin number for the PIR
const int servoPin = 10; // the pin number for the SERVO
const int pumpInterval = 500; // number of millisecs between squirts
const int pumpDuration = 500; // number of millisecs that it's squirting
const int servoMinDegrees = 20; // the limits to servo movement
const int servoMaxDegrees = 150;
//------------ VARIABLES (will change)---------------------
byte pumpState = LOW; // used to record whether the pump is on or off LOW = off
Servo myservo; // create servo object to control a servo
int servoPosition = 90; // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2; // amount servo moves at each step
// will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previouspumpMillis = 0; // will store last time the LED was updated
unsigned long previousServoMillis = 0; // the time when the servo was last moved
//=================== FUNCTIONS =====================
void switchPump() {
// this is the code that actually switches the pump on and off
digitalWrite(pumpPin, pumpState);
}
//========================================
void updatepumpState() {
if (pumpState == LOW) {
// if the is pump is off, we must wait for the interval to expire before turning it on
if (currentMillis - previouspumpMillis >= previouspumpMillis) {
// time is up, so change the state to HIGH
pumpState = HIGH;
// and save the time when we made the change
previouspumpMillis += previouspumpMillis;
// NOTE: The previous line could alternatively be
// previouspumpMillis = currentMillis
// which is the style used in the BlinkWithoutDelay example sketch
// Adding on the interval is a better way to ensure that succesive periods are identical
}
}
else { // i.e. if pumpState is HIGH
// if the Led is on, we must wait for the duration to expire before turning it off
if (currentMillis - previouspumpMillis >= pumpDuration) {
// time is up, so change the state to LOW
pumpState = LOW;
// and save the time when we made the change
previouspumpMillis += pumpDuration;
}
}
}
//========================================
void servoSweep() {
// this is similar to the servo sweep example except that it uses millis() rather than delay()
// nothing happens unless the interval has expired
// the value of currentMillis was set in loop()
if (currentMillis - previousServoMillis >= servoInterval) {
// its time for another move
previousServoMillis += servoInterval;
servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative
if (servoPosition <= servoMinDegrees) {
// when the servo gets to its minimum position change the interval to change the speed
if (servoInterval == servoSlowInterval) {
servoInterval = servoFastInterval;
}
else {
servoInterval = servoSlowInterval;
}
}
if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees)) {
// if the servo is at either extreme change the sign of the degrees to make it move the other way
servoDegrees = - servoDegrees; // reverse direction
// and update the position to ensure it is within range
servoPosition = servoPosition + servoDegrees;
}
// make the servo move to the next position
myservo.write(servoPosition);
// and record the time when the move happened
}
}
//========================================
void setup() {
Serial.begin(9600);
Serial.println("Starting SeveralThingsAtTheSameTimeRev1.ino"); // so we know what sketch is running
// set the Led pins as output:
pinMode(pumpPin, OUTPUT);
pinMode(pirPin, INPUT);
myservo.write(servoPosition); // sets the initial position
myservo.attach(servoPin);
}
//========================================
void loop() {
while(digitalRead(pirPin) == LOW) {
// Notice that none of the action happens in loop() apart from reading millis()
// it just calls the functions that have the action code
currentMillis = millis(); // capture the latest value of millis()
// this is equivalent to noting the time from a clock
// use the same time for all LED flashes to keep them synchronized
updatepumpState(); // call the functions that do the work
switchPump();
servoSweep();
}
}
//========================================END
And it compiled. Just hope it does what it's supposed to do
I've got it all doing what I want it to do, apart from the servo doesn't do a 180 sweep, more like a 90 degree sweep. I've tried a few different servos, I've changed some to use write.Microseconds, but it's just not playing fair. any help would be appreciated.
// squirt cat shoo'er
// Borrowed a quite a few bits from SeveralThingsAtTheSameTime.ino and zoomkat 10-4-10 serial servo test
// ----------LIBRARIES--------------
#include <Servo.h>
// --------CONSTANTS (won't change)-
const int pumpPin = 8; // the pin numbers for the PUMP
const int pirPin = 10; // the pin number for the PIR
const int servoPin = 9; // the pin number for the SERVO
const int pumpInterval = 50; // number of millisecs between squirts
const int pumpDuration = 1000; // number of millisecs that it's squirting
const int servoMinDegrees = 800; // the limits to servo movement
const int servoMaxDegrees = 2160;
//------------ VARIABLES (will change)
int calibrationTime = 10; //the time we give the sensor to calibrate (10-60 secs according to the datasheet)
byte pumpState = LOW; // used to record whether the pump is on or off LOW = off
Servo myservo; // create servo object to control a servo
int servoPosition = 1500; // the current angle of the servo - starting at 90.
int servoInterval = 7; // millisecs between servo moves
int servoDegrees = 15; // amount servo moves at each step will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previouspumpMillis = 0; // will store last time the LED was updated
unsigned long previousServoMillis = 0; // the time when the servo was last moved
//=================== FUNCTIONS ===========
//--------------- Pump --------
void updatepumpState() {
if (pumpState == LOW) { // if the is pump is off, we must wait for the interval to expire before turning it on
while (currentMillis - previouspumpMillis >= pumpInterval) {
// time is up, so change the state to HIGH
pumpState = HIGH;
// and save the time when we made the change
previouspumpMillis += pumpInterval;
}
}
else { // i.e. i// if the pump is on, we must wait for the duration to expire before turning it off
while (currentMillis - previouspumpMillis >= pumpDuration) {
// time is up, so change the state to LOW
pumpState = LOW;
// digitalWrite(pumpPin, pumpState);
// and save the time when we made the change
previouspumpMillis += pumpDuration;
}
}
}
void switchPump() {
// this is the code that actually switches the pump on and of
digitalWrite(pumpPin, pumpState);
}
//----------------- Servo --------
void servoSweep() {
// this is similar to the servo sweep example except that it uses millis() rather than delay()
// nothing happens unless the interval has expired
// the value of currentMillis was set in loop()
if (currentMillis - previousServoMillis >= servoInterval) {
// its time for another move
previousServoMillis += servoInterval;
servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative
if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees)) {
// if the servo is at either extreme change the sign of the degrees to make it move the other way
servoDegrees = - servoDegrees; // reverse direction
// and update the position to ensure it is within range
servoPosition = servoPosition + servoDegrees;
}
// make the servo move to the next position
myservo.writeMicroseconds(servoPosition);
// updatepumpState();
// switchPump();
}
}
//------------------ Setup -----
void setup() {
Serial.begin(9600);
Serial.println("test.ino"); // so we know what sketch is running
// set the pins as output:
pinMode(pumpPin, OUTPUT);
pinMode(pirPin, INPUT);
//give the sensor some time to calibrate
Serial.print("calibrating sensor ");
for(int i = 0; i < calibrationTime; i++){
Serial.print(".");
delay(1000);
}
Serial.println(" done");
Serial.println("SENSOR ACTIVE");
delay(50);
myservo.writeMicroseconds(servoPosition); // sets the initial position
myservo.attach(servoPin, 800, 2160);
}
//---------------- Loop --------
void loop() {
Serial.println("LOOP");
pumpState = LOW;
digitalWrite(pumpPin, pumpState);
while(digitalRead(pirPin) == HIGH) {
Serial.println("PIR");
// Notice that none of the action happens in loop() apart from reading millis()
// it just calls the functions that have the action code
currentMillis = millis(); // capture the latest value of millis()
// this is equivalent to noting the time from a clock
// use the same time for all LED flashes to keep them synchronized
updatepumpState(); // call the functions that do the work
switchPump();
servoSweep();
}
}
//========================================END