Particle is self Activating

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 :+1:

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.

1 Like

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.

2 Likes

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.

1 Like

Don’t worry :grin: @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 :grin:
Best,
Arek

1 Like

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;
  }
}
1 Like

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

1 Like

I can't recall sending a PM nor do I find it in my outbox :man_shrugging:

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.

1 Like

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?

1 Like

Here is the hardware to control the vent


3 Likes

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.

1 Like

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.