Battery saving help

I have started to get into the hobby of building some personal IOT devices that started when I got bit by the bug after building a brewpi homebrew fermentation controller from a raspberry pi and an arduino. Realizing the potential of these devices has thrown me further down the rabbit hole. I however like to create a concept and wire it up but the programming is all greek to me. Sometimes if I struggle my way through the comments I can understand what is happening with someone elses code but for all practical purposes I am a complete stranger to code. Guess I should pick a new hobby haha. Now on to where I need help… Essentially what I designed was some speakers that I mounted to my hottub pergola in my backyard detached from my house. I didn’t want to have to run speaker wire out to the location so I used a setup that I have had a ton of success with in other areas of my home for a whole home audio setup which is basically a raspberry pi with a hifiberry amp+ on top. For anyone looking into audio I love this setup the software I use is called max2play and its awesome. I didn’t want to mess with any mains wiring by the hottub due to the dangers of water and electricity so I currently have solar powered cafe lights on the pergola that run off a double A battery I beefed up the solar panels from the stock and now they work great and keeping up with the spirit of the solar hottub area I wanted the speaker system to be solar powered as well. The raspberry pi and amp I knew used alot of juice when sitting at idle and I only need the speakers to play a couple of hours a night. I wanted the solar panel and battery to be small so this is where the particle photon comes in. I basically uses this battery https://www.amazon.com/gp/product/B00ME3ZH7C/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1 it has 12 volt output and a 5 volt output and supports through charging via the 12 volt port. My pi and amp combination runs off the 12 volt port and my particle photon from the 5 volt usb. I modified some code that I found where someone was controlling an 8 channel relay and integrating this with a smartthings home automation controller to just control a one channel relay which I use to switch on and off the pi. Success my code works I can turn on and off the speakers from my smartthings controller. They sound great and when synced with my patio speakers they deliver a way more even sound to my pool area so multiple success! My battery and solar panel is seeming to keep up with these super sunny phoenix summer days but I want to make dead sure it keeps up in the winter or days with weather. I don’t want to have interruptions or Id deem the project a failure… currently im using my backpacking solar panel (goal zero namad 7) to power my project for proof of concept and it has been stellar the last few weeks. Im currently waiting for the solar panel I plan to permanently install to arrive from a slow boat coming from China. The panel I plan on installing is a little smaller than my hiking one. What I want is to create some even more power saving code into my photon to make sure this thing runs smoothly. Currently the photon is running full speed 24 hours a day. I tried reading about the different modes and just ended up ultimately confused. For instance I definitely do not need for any reason to be able to play music after 2 am for instance that would be horrible for the neighbors so I would be fine if my particle went into a deep sleep mode from say 2 am to 9 am to save even more juice. Or I even had some confusion is there a mode that it could be put into that would keep looking for my on command but sleep all other functions that were unnecessary that I could use all the time for my daytime function? Currently the only battery saving option I have in my code is I saw how someone controlled the color of the led and I just changed the values to zero turning off the photons led. The battery pack has some leds that I cant shutoff but Im sure it doesn’t use that much. I will attach my code here and some pictures of my project. Id appreciate input or suggestions on what should be in there to achieve what I want to achieve. Thanks in advance for your patients and help just rememeber I’m a total noob to code.

Photon Code

// A Spark function to parse the commands
int relay4(String command);

// We name pins 
int r4 = D3;

// This routine runs only once upon reset
void setup() 
{
  //Register Spark functions
  Spark.function("strelay4", relay4);
  
  // Initialize output pins
  
  pinMode(r4, OUTPUT);
  
  // take control of the LED
  RGB.control(true);
  
  // the following sets the RGB LED to light green:
  RGB.color(0, 0, 0);
}



// This routine loops forever 
void loop()
{
  // nothing here
}



//acts based on messages from spark cloud
int relay1(String command)
{

int relay4(String command)
{
  if (command == "1") 
    {   
    digitalWrite(r4, HIGH);   // Turn ON the relay
    return 1;
    } 
  else 
    {               
    digitalWrite(r4, LOW);    // Turn OFF the relay
    return 0;
    }
}

Smarththings Groovy Code

/*
 *  Particle.io Device Example
 *
 *  
*/
definition(
    name: "Particle.io Device",
    namespace: "rhworkshop",
    author: "Andy Rawson",
    description: "Allows a Particle.io device to be used with SmartThings",
    category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")

// Preferences and setup pages
preferences {
    page(name: "page1", title: "Particle Device", nextPage: "page2", uninstall: true) {
        section("Username and Password") {
        	input "particleUsername", "text", title: "Your Particle.io Username", required: true
            input "particlePassword", "password", title: "Your Particle.io Password", required: true
        }
    }
}

page(name: "page2", title: "Check Auth", nextPage: "page3", install: false, uninstall: false)
page(name: "page3", title: "Pick Device", install: true, uninstall: false)

def page2() {
   	dynamicPage(name: "page2") {
   		section("Generate Token") {
			if (loginToParticle()) {
            	paragraph title: "Success logging in to Particle.io", "Auth Token generated"            
            }
            else {
            	paragraph title: "Error logging in to Particle.io", "Check your username and password or check the log for other errors"
            }
		}
	}
}

def page3() {
	dynamicPage(name: "page3") {
       	section("Particle Device to use"){
   			def particleDevices = getDevices()
   	    	input(name: "particleDevice", type: "enum", title: "Select the particle Device", required: true, multiple: false, options: particleDevices)
       	}
        section {
            label title: "Assign a name", required: false
        }
	}
}

// -----------------------------------------------------------
// Setup functions
// -----------------------------------------------------------
def installed() {
	state.webhookName = "dev${particleDevice[-6..-1]}"
    state.appURL = "https://graph.api.smartthings.com/api/smartapps/installations/${app.id}/stdata/${state.webhookName}/{{pin}}/{{data}}?access_token=${state.accessToken}"
	//log.debug "Installed with settings: ${settings}"
    checkWebhook() 
    //setupSensors()
}

def updated() {
	//log.debug "Updated with settings: ${settings}"
	unsubscribe()
    checkWebhook()
    //setupSensors()
}

// Log in to Particle.io
def loginToParticle() {
	   	if (!state.particleToken){
	   		def clientAuth = "particle:particle"
	   		clientAuth = "Basic " + clientAuth.encodeAsBase64().toString()
	   		def params = [
	        	headers: [Authorization: clientAuth],
				uri: "https://api.particle.io/oauth/token",
				body: [grant_type: "password", 
        			username: particleUsername,
        			password: particlePassword
                	] 
	        ]
	   		try {
	        	httpPost(params) {response -> 
    	    		state.particleToken = response.data.access_token
        	       	}
                log.debug "Created new Particle.io token"        
	        	//log.debug "Particle Token ${state.particleToken}"
	        	checkToken()
				return true
			} catch (e) {
    	    	log.error "error: $e"
                return false
        	}
 
		}
        else {
        	return true
        }
}

// Check to see if we need to make the webhook or if it is there already
void checkWebhook() {
	int foundHook = 0
    try {
		httpGet(uri:"https://api.particle.io/v1/webhooks?access_token=${state.particleToken}",
	    	) {response -> response.data.each { 
	    		hook ->
					//log.debug hook.event
	                if (hook.event == state.webhookName) {
	                	foundHook = 1
	                	log.debug "Found existing webhook id: ${hook.id}"                    
 	               	}
	           		}
	 	   			if (!foundHook) {
	    				createWebhook()
	    			}
	    			else {
	    			}
			}
 	}
 	catch (e) {
   		log.error "error: $e"
    }
}

// Create a new particle webhook for this app to use
void createWebhook() {
	log.debug "Creating a Particle webhook "
    try {         
		httpPost(uri: "https://api.particle.io/v1/webhooks",
      			body: [access_token: state.particleToken, 
        		event: state.webhookName,
                url: state.appURL, 
                requestType: "GET", 
                mydevices: true]
      			) {response -> log.debug (response.data)}
    }
 	catch (e) {
   		log.error "error: $e"
    }
}

// get a list of the particle devices from the account
def getDevices() {
	def particleDevices = [:]
	//Particle API Call
    try {
	    def readingClosure = { response -> response.data.each { core ->
    		particleDevices.put(core.id, core.name)  
        	}
		}
	    httpGet("https://api.particle.io/v1/devices?access_token=${state.particleToken}", readingClosure)
    }
 	catch (e) {
   		log.error "error: $e"
    }
 	return particleDevices
}

// Is there an accessToken yet?
def checkToken() {
	if (!state.accessToken) {
    	createAccessToken() 
    }
}

// api mappings for the Particle.io API to talk to the SmartThings API
mappings {
    path("/stdata/:webhookName/:pin/:data") {
        action: [
            GET: "setDeviceState"
        ]
    }
}

// -----------------------------------------------------------
// Send and receive commands with the Particle device
// -----------------------------------------------------------

// send an on command to the particle device
def switchOnHandler(evt) {
	evt.getProperties().each {e -> log.debug "${e}" }
    log.debug "switch turned on!"
    def pinToSet = "D0"
    try {
    	httpPost("https://api.particle.io/v1/devices/${particleDevice}/setOn?access_token=${state.particleToken}","command=${pinToSet}",) {response -> log.debug (response.data)}
	}
 	catch (e) {
   		log.error "error: $e"
    }
}

// send an off command to the particle device
def switchOffHandler(evt) {
    log.debug "switch turned off!"
    def pinToSet = "D0"
    try {
    	httpPost("https://api.particle.io/v1/devices/${particleDevice}/setOff?access_token=${state.particleToken}","command=${pinToSet}",) {response -> log.debug (response.data)}
	}
 	catch (e) {
   		log.error "error: $e"
    }
}

// send a value to particle device
def switchValueHandler(evt) {
    log.debug "switch dimmed to ${evt.value}!"
    def pinToSet = "D0"
    def pinValue = "101"
    try {
    	httpPost("https://api.particle.io/v1/devices/${particleDevice}/setValue?access_token=${state.particleToken}","command=${pinToSet}:${pinValue}",) {response -> log.debug (response.data)}
	}
 	catch (e) {
   		log.error "error: $e"
    }
}

// got the webhook from the particle device now do something with the information
def setDeviceState() {
	log.debug "Got webhook - pin: ${params.pin} data: ${params.data}"    
  //return [Respond: "OK"]
}

// -----------------------------------------------------------
// Uninstall Section
// -----------------------------------------------------------

def uninstalled() {
  log.debug "Uninstalling ParticleThings"
  deleteWebhook()
  deleteAccessToken()
  log.debug "ParticleThings Uninstalled"
}

// Cleanup on uninstall - remove the particle webhook for this app
void deleteWebhook() {
try{
httpGet(uri:"https://api.particle.io/v1/webhooks?access_token=${state.particleToken}",
    ) {response -> response.data.each { 
    		hook ->
				//log.debug hook.event
                if (hook.event == state.webhookName) {
                	httpDelete(uri:"https://api.particle.io/v1/webhooks/${hook.id}?access_token=${state.particleToken}")
                    log.debug "Deleted the existing webhook with the id: ${hook.id} and the event name: ${state.webhookName}"
                }
           }
 }
 } catch (all) {log.debug "Couldn't delete webhook, moving on"}
}

// Cleanup on uninstall - remove the particle access token
void deleteAccessToken() {
	try{
    	def clientAuth = "${particleUsername}:${particlePassword}"
	   	clientAuth = "Basic " + clientAuth.encodeAsBase64().toString()
	   	def params = [
	        headers: [Authorization: clientAuth],
			uri: "https://api.particle.io/v1/access_tokens/${state.particleToken}"
	        ]
	httpDelete(params) //uri:"https://${particleUsername}:${particlePassword}@api.particle.io/v1/access_tokens/${state.particleToken}")
	log.debug "Deleted the existing Particle Access Token"
 } catch (e) {log.debug "Couldn't delete Particle Token, moving on: $e"}
}

Project Pictures
originally I had a pi3 powering it but i switched it for the zero w thinking this would save even more power while its on and accomplish the same thing.

2 Likes

@KyleG are you able to be of any help here? I chose particle over the other development boards because of the user base and forums. I’m surprised that nobody has responded to my thread :frowning:

Hi, first thing to get an answer: ask a clear question.
What I understand from this pist is you are looking to reduce the power consumption of your photon. You are also mentionning a Raspberry Pi, but I didn’t really understand how you are using it and why.
And there’s also a relay, which can use some power. Is it ON 24h?

1 Like

One other reason for the lack of response might be the looooooooong spaghetti text introduction.
At least for me that was the reason to stop reading after the first few lines.

Adding a short TL;DR intro and having some paragraphs structuring the text and stating a specific question or clear indication of the problem early on may help keeping readers interested.

And after a third attempt to read over the - excuse the bluntness :wink: - “problem obscuring waffle” some hints:

  • try using a bi-stable relay which only draws power when changed and not all the time while held in one position
  • System.sleep(SLEEP_MODE_DEEP, secsTillWake) isn’t that hard to use, just calculate the seconds between (desired wake time) - (current time) and use as secsTillWake
  • if you have a wake button pulling WKP pin to 3.3V you can wake the device any time you like
  • if you don’t want to have the device on permanently during the day but want to be able to activate it wirelessly, you may need to accept some latency and need a way to place “defered commands” on a server which the Photon would read every time it wakes (e.g. once every minute)
  • …
11 Likes

@bscuderi was ScruffR’s suggestion able to help you out?

Hey sorry I realize after reading all the other posts that I rambled in a tired stooper and needed to be more clear… Therefore I started over and posted a whole new clear and concise thread located here…

To answer your question the relay is always powered via the 5v pin on the particle. As far as I know there is no way to not power this? However, I cant imagine that is alot of power while the relay is off.

Secondly the raspberry pi is hosting a media server in the form of squeezebox as well as airplay. I have a hifiberry amp+ dac board on the raspberry pi that powers some speakers. The amp and pi use alot of power even at idle compared to a particle so that is why the particle is incorporated to switch a relay that turns on and off the raspberry pi and amp combo.

okay maybe I don’t want the latency so I could live with it just sleeping at night… This is the first I’ve heard of a bistable relay I’ll look into that. As far as the system sleep how do I make it occur at a specific time. What section does the code need to go in the loop or the setup?

Check if the time is equal to the time you want your device to go to sleep in an if statement. You get the time with the Time functions, like Time.hour(), Time.minute(), etc.

I’m sure that the syntax is probably way out in left field as I am NOT a programmer… But am I on the right track here

void loop()
{
      if (Time.hour()=2)
           {
      System.sleep(SLEEP_MODE_DEEP, 25200)
           }
}

It’s fine except that equality is tested with == not = (which is the assignment operator).


void loop()
{
      if (Time.hour()==2)
           {
      System.sleep(SLEEP_MODE_DEEP, 25200)
           }
}

So let me see if I understand this right.if I put the above code in my particle is just constantly checking in a loop over and over again to see if the hour == 2 or basically is 2 in the morning. Is that correct? Then if it is true it runs the stuff in the {} which is the deep sleep so after 25,200 seconds it wakes up which would be 9 am… is this based upon UTC time? and when my particle wakes back up does it automatically reconnect with the cloud resuming the normal function of the rest of my code?

Actually I would not check for a specific hour alone.
Imagine what is if your device wakes up between 3am and 9am for some reason? Then the device would not go back to sleep or if this happens at 2:59am, the device would wake again 59 minutes too late.

Hence I’d do things slighty different

Knowing that Time.local() returns the local date/time in seconds allows for easy range checks by use of the fact, that a day has 86400 seconds

const int secGo2Sleep = 02*3600 + 00*60 + 00;  // 00 minutes and 00 seconds for illustration purpose only 
const int secWakeUp   = 09*3600 + 00*60 + 00;

void loop() {
  int secNow = Time.local() % 86400;  // whats the current second of the day

  if (secGo2Sleep <= secNow && secNow < secWakeUp) {
    System.sleep(SLEEP_MODE_DEEP, secWakeUp - secNow);
  }
}

If your secGo2Sleep would happen to be set before midnight, you may need to adapt this slightly.

1 Like

Yes that is correct.

Yes, that’s right. It will be UTC time if you have not set your time zone with Time.zone().

From deep sleep, the device will reset when it wakes up; that is, it will start the code over, running setup() again. If you are in SYSTEM_MODE(AUTOMATIC), which is the default, then yes, the device will automatically connect to the cloud.

1 Like

Okay thank you all so much for the help! Your suggestion makes sense to me and is a definite improvement in the logic in case of strange resets at a weird time or whatever. Here is my new code I want to make sure this makes sense before I flash it to my particle becuase my particle is installed in my hot tub cabinet it is a royal pain in the butt to access if I had to reset it :wink:

// A Spark function to parse the commands
int relay4(String command);

// We name pins 
int r4 = D3;

// Sets the sleep and wakeup time in UTC 09 is 2 phx time and 16 is 9
const int secGo2Sleep = 09*3600 + 00*60 + 00;
const int secWakeUp   = 16*3600 + 00*60 + 00;


// This routine runs only once upon reset
void setup() 
{
  //Register Spark functions
  Spark.function("strelay4", relay4);
  
  // Initialize output pins
  
  pinMode(r4, OUTPUT);
  
  // take control of the LED
  RGB.control(true);
  
  // the following sets the RGB LED off:
  RGB.color(0, 0, 0);
}



// This routine loops forever to check sleep
void loop() {
  int secNow = Time.local() % 86400;  // whats the current second of the day

  if (secGo2Sleep <= secNow && secNow < secWakeUp) {
    System.sleep(SLEEP_MODE_DEEP, secWakeUp - secNow);
  }
}



//acts based on messages from spark cloud


int relay4(String command)
{
  if (command == "1") 
    {   
    digitalWrite(r4, HIGH);   // Turn ON the relay
    return 1;
    } 
  else 
    {               
    digitalWrite(r4, LOW);    // Turn OFF the relay
    return 0;
    }
}


In order to have Time.local() return your actual time, you should add Time.zone(-7) (or in case of DST -6) in setup()

oh okay in the parentheses just put my offset from utc?
like
Time.zone(7)

It needs to be -7 or -6 (for DST) as the west is 7/6 hours behind UTC

You should also use the “modern” Particle.function() instead of Spark.xxxx()

1 Like

okay updated code… again thanks for your patients I am learning as I go. Particle functions instead of spark and a timezone in setup and I put the local time calculator back in the loop.

// A Spark function to parse the commands
int relay4(String command);

// We name pins 
int r4 = D3;

// Sets the sleep and wakeup time
const int secGo2Sleep = 2*3600 + 00*60 + 00;
const int secWakeUp   = 9*3600 + 00*60 + 00;


// This routine runs only once upon reset
void setup() 
{
  //Register Particle functions
  Particle.function("strelay4", relay4);
  
  // Initialize output pins
  
  pinMode(r4, OUTPUT);
  
  // take control of the LED
  RGB.control(true);
  
  // the following sets the RGB LED off:
  RGB.color(0, 0, 0);
  
  //set local time
  Time.zone(-7);
}



// This routine loops forever to check sleep
void loop() {
  int secNow = Time.local() % 86400;  // whats the current second of the day

  if (secGo2Sleep <= secNow && secNow < secWakeUp) {
    System.sleep(SLEEP_MODE_DEEP, secWakeUp - secNow);
  }
}



//acts based on messages from Particle cloud


int relay4(String command)
{
  if (command == "1") 
    {   
    digitalWrite(r4, HIGH);   // Turn ON the relay
    return 1;
    } 
  else 
    {               
    digitalWrite(r4, LOW);    // Turn OFF the relay
    return 0;
    }
}

I was able to successfully compile and flash the code above to the device… It didn’t blow up and my on and off still is functioning flawlessly. I guess we shall see in about 15 minutes if it will take a nap like it is supposed to. Fingers crossed.

1 Like

If you want to check the code, you could just change the times to sleep/wake, do won’t need to stay up till 2am :wink:

1 Like