I have a particle programmed to activate a servo that opens and closes an HVAC vent. The photon is running the servo and opening the vent during the night so when I look at the vent (physically), it is open, even though the last command I sent through Smartthings was to close it. Smartthings still reports it is closed status. Why would the program run on its own during the night? Is there a way for me to see when the photon is activating the servo on its own?
Welcome to the community
However, for us it’s not possible to answer this in any meaningful way without knowing your code or at least considerably more background info.
Thanks ScruffR. Wasn’t sure what kind of background would be helpful. Here is the code:
//
//modified by PatL for Garage HVAC Vent so SmartThings could control the garage vent remotely with an "on" and "off" control
// Copyright (c) <year>, <copyright holder>
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
// * Redistribution of this software in source or binary forms shall be free
// of all charges or fees to the recipient of this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// a maximum of eight servo objects can be created
Servo myservo;
int val;
int ledControl(String command);
int speakerPin = D0; // variable to store speaker pin
int ServoPin = A4;
// create an array for the notes in the melody:
// C4,G3,G3,A3,G3,0,B3,C4
//int melody[] = {1908,2551,2551,2273,2551,0,2024,1908};
int melody[] = {956,1014,1136,1275,1432,1519,1700,1915};// create an array for the duration of notes
// note duration: 1/32 note:
int noteDuration=1000/32;
void setup() {
myservo.attach(ServoPin); // attaches the servo on analog pin A4 to the servo object
Particle.function("ledstate", ledControl);
Particle.variable("checkStatus1", "off");
pinMode(speakerPin, OUTPUT); // attach speaker pin
}
void loop() { }
int ledControl(String command) {
//if and else will set on off functionality
if (command == "1") {
myservo.attach(ServoPin);
delay(500);
myservo.write(0);
Particle.variable("checkStatus1", "on");
delay(1200);
myservo.detach();
playUpNotes();
}
else if(command == "0") {
myservo.attach(ServoPin);
delay(500);
myservo.write(135);
Particle.variable("checkStatus1", "off");
delay(1200);
myservo.detach();
playDownNotes();
}
else {
myservo.attach(ServoPin);
delay(500);
val = command.toInt(); //converts command to integer to be used for positional arrangement
val = map (val, 0, 99, 2, 170);
myservo.write(val);
Particle.variable("checkStatus1", "on");
delay(1200);
myservo.detach();
}
//Return added by PatL to avoid compile error
return -1;
}
// play melody function
void playUpNotes() {
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < 8;thisNote++) { // to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
tone(speakerPin, melody[thisNote],noteDuration); // the note's duration + 30%
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
// stop the tone playing
noTone(speakerPin);
}
}
void playDownNotes() {
// iterate over the notes of the melody:
for (int thisNote = 7; thisNote >-1;thisNote--) { // to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
tone(speakerPin, melody[thisNote],noteDuration); // the note's duration + 30%
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
// stop the tone playing
noTone(speakerPin);
}
}
I’ve just skimmed over your code, but one thing immediately struck my attention.
Particle.variable()
should only be called in setup()
to register a variable with the cloud. It’s not there to update its value with the cloud - as you do in your ledControl()
.
You’d pass a global variable (of function) as second parameter and whenever some remote client requests the current value the registered Particle.variable()
will be handed back to the requester from there.
The next thing is that whenever your device might restart for any reason your code will fall back to its default state. To prevent that you should keep the most recent value in a retained variable and set the state of the pin in setup()
.
Finally not use delay()
inside a Particle.function()
but rather use a software timer to do execute the deferred action(s).
And to make meaningful use of the return value of a Particle.function()
I’d choose distinct values for each branch that was actually executed.
Thank you for the feedback. I copied this code (mostly) from another user who was controlling a servo with the particle and using Smartthings as the interface. Unfortunately I am not very familiar with this programming language so while some of your feedback made sense to me in concept, I don’t have the knowledge to implement it (yet). I was just happy to get it to work through smartthings and have it do what I wanted (sans the auto activation problem) If you are so inclined to make some modifications to my code it would be appreciated. If not I completely understand.
Don’t worry @ScruffR will help you 100%
also tomorrow I’m gonna have some time so I’ll try my best
to show you a ScruffR idea
Best,
Arek
Thank you for the assist, I am sure I’ll learn something.
Pat
You can give this a try
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
typedef struct song_t {
uint16_t note; // frequency
uint16_t duration; // 1 equals a 1/64 note at given interval
} song_t;
const int pinSpeaker = D0;
const int pinServo = A4;
const int INTERVAL = 1600/64; // for a 1/64 note as minimum length
const song_t melody[] = // create an array for the notes in the melody:
{ { 0, 0 } // indicate song boundary
, { 956, 2 } // 2/64 tone (1/32)
, { 0, 1 } // 1/64 pause
, { 1014, 2 }
, { 0, 1 }
, { 1136, 2 }
, { 0, 1 }
, { 1275, 2 }
, { 0, 1 }
, { 1432, 2 }
, { 0, 1 }
, { 1519, 2 }
, { 0, 1 }
, { 1700, 2 }
, { 0, 1 }
, { 1915, 2 }
, { 0, 0 } // indicate song boundary
};
const int songLength = sizeof(melody) / sizeof(melody[0]);
retained int state = -1; // the value of these variables will be stored in Backup RAM
retained int angle = 0;
int controlServo(const char* command);
void detachServo();
void playSong();
Timer timDetachServo(1200, detachServo, true); // one-shot timer to detach servo after a while (1.2 seconds)
Timer timPlaySong(INTERVAL, playSong); // set cadence to minimum interval for the shortes note to play (duration 1 in melody[])
Servo servo;
void setup() {
Particle.variable("checkState", state); // hook-up state variable with the cloud
Particle.function("controlServo", controlServo);
pinMode(pinSpeaker, OUTPUT); // attach speaker pin
servo.attach(pinServo); // attach servo pin to servo
servo.write(angle); // re-initialize previous angle setting
timDetachServo.start(); // initiate deferred servo detachment
}
void loop() { }
int controlServo(const char* command) {
servo.attach(pinServo);
switch (state = atoi(command)) {
case 0:
angle = 135;
break;
case 1:
angle = 0;
break;
default:
angle = state;
state *= -1;
break;
}
servo.write(angle);
timDetachServo.start(); // initiate deferred servo detachment
if (state >= 0)
timPlaySong.start();
return state;
}
void detachServo() {
servo.detach();
}
void playSong() {
static int curNote = -1; // static local variables will persist across function calls
static int curTime = -1;
switch (curNote) {
case 0: // beginning or
case songLength-1: // end of song
timPlaySong.stop();
noTone(pinSpeaker);
curNote =
curTime = -1;
return;
break;
case -1: // initialize song
curNote = (state ? 0 : songLength-1); // first note to play (from the end when OFF)
curTime = 0;
__attribute__((fallthrough)); // fall through // as intended
default:
if (melody[curNote].duration <= curTime++) {
curNote += (state ? +1 : -1); // move to next note (backwards when OFF)
if (melody[curNote].note)
tone(pinSpeaker, melody[curNote].note, INTERVAL * melody[curNote].duration);
else
noTone(pinSpeaker);
curTime = 1;
}
break;
}
}
ScruffR (I responded to the PM you sent)
Thank you very much ScruffR. I flashed the code and the servo control is working as expected through Smartthings. I will check the vent tomorrow morning to see if it opens on its own and let you know the results. The code you wrote is obviously much cleaner than the previous version and I will study it so I can learn from it.
Also thought you might be interested in the hardware that the code controls. I have a hard to get to HVAC vent in my workshop/garage and was tired of climbing over things to get to it to open it when I am working in the garage. I attached a high torque RC servo I had laying around, made a custom PCB, 3D printed a case, added a speaker, and plugged it into 5V USB output wall wart. The mechanism works very well and I don’t have to risk my life to get heat/AC in the workshop anymore.
Your support was awesome and much appreciated
Pat
I can't recall sending a PM nor do I find it in my outbox
Glad you like my code and hope it'll keep the vent in a steady state.
If it doesn't you might need to add some logging to controlServo()
in order to investigate whether the function may get called inadvertently.
Another possibility could be the servo losing position over time which could be mitigated by reinforcing the current state from time to time.
I had assumed it was a PM when I saw the code in my email inbox and see now that it was simply your reply. Sorry for the confusion.Can you point me to an article on how to incorporate logging?
The vent is still opening on its own randomly. Disconnected the mechanical linkage to the vent from the servo arm to remove that variable, and the servo is definitely acting on its own with some kind of signal from the Particle. I say “some kind” because I noticed that it is not always moving to the full on or full off position. The stored/retained values in the program are set to close the vent when power is lost an regained. At this point I suspect power line electrical interference, maybe I will try a different power supply for the 5V usb, currently using an old phone charger.
It’s not uncommon for a servo to drift over time, as @ScruffR mentioned.
You might add a timer that calls controlServo
once an hour or so to account for any drift.
I think you should investigate the power supply you are using. I have definitely seen servos that act strangely when you don't have enough current available in the power supply. I would recommend at least a 1A supply.
Thanks. I will implement a timer in the code. The power supply is 1.5A but I am wondering if that has noise in it. Now my problem is a solid blue LED next to the pin 7 (another post)
If you have a dim blue D7 LED now, it was almost certainly a power issue. That’s caused by certain brownout or sudden blackout conditions causing the STM32 MCU to erase certain sectors, in this case the bootloader. The only cure for that is a SWD/JTAG programmer because if there is no bootloader there isn’t a way to program the flash by USB.
Thank you. I checked the price of the debugger ($30) and a new photon ($19) so I’ll just get a new Photon and hope it doesn’t happen again. If it does, maybe I’ll spring for the debugger
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.