Particle Photon OTA Updater

Hello Particle Community!

So I’ve had my Particle Photon for several weeks now and I’ve got to say that this thing is awesome! So many project ideas running through my head! For those individuals, like myself, who are creating projects that use the Photon’s sleep functionality, the OTA updating has been a bit of a sore subject.

One solution for “remote” projects that can’t be easily retrieved for update has been to create an update window where the Photon doesn’t sleep and waits to see if a remote update will be trigger, but that is wasting valuable power! I’ve seen posts that suggest Particle is working on OTA queuing in their Cloud platform but I have not seen anything that suggests that has been implemented. So until they get to that I’d like to show you my app that might help some of you developers out there get by.

I’ve uploaded my code ( https://gitlab.com/danrussell/photon-ota-updater ) and a short Wiki ( https://gitlab.com/danrussell/photon-ota-updater/wikis/home ) to GitLab so that everyone should be able to download the code. There’s still some functionality I’d like to add but I really wanted to get the app together quickly and get it out here so I could get some feedback from the community. Feel free to post any questions and suggestions!

1 Like

Thanks for this updater code! I have successfully used it to re-program a photon that I have as a battery powered sensor that wakes every 5 minutes and sends a pool temp reading. Being this is a pool sensor, it’s outside and was always a pain to go fetch it and put it into safe mode to reprogram it. Now it’s a breeze!

I have a couple of requests if you are interested. It would be good if after I click the “add” button after specifying the firmware code that the app return me to the devices page. Additionally, it would be good if the devices page periodically refreshes to show the current status of pending flash devices or not.

Perhaps this 3rd item is just a UI issue, but once I click “edit” from the main device page, the new page states an update required = true. Hopefully this is not actually true because I have not specified the firmware image to upload yet.

I did get a Exception trace when using the code. My guess is that my device has not actually gotten the deviceName from the cloud yet and sends an request with “not_set”, which triggers this:

> 2016-12-10 10:44:02.164 ERROR 13397 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] w
> ith root cause

> java.lang.NullPointerException: null
>         at com.particle.controllers.OtaUpdateController.update(OtaUpdateController.java:63) ~[classes!/:na]
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
>         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
>         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]
>         at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]
>         at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:817) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:731) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:968) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:859) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:844) ~[spring-webmvc-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) ~[spring-web-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.5.RELEASE.jar!/:4.2.5.RELEASE]
>         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) ~[tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]
>         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]
>         at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.32.jar!/:8.0.32]
>         at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]

One thing I ran into when implementing your example INO code is a “race” condition with the cloud publishing the device name to the photon. When the device wakes up and immediately requests from the Java server if an update is ready, the deviceName can be empty and it effectivey does not update. Going to sleep after this would actually never allow for the Java server to update the photon. If one waits to sleep until the deviceName != “not_set”, you could be actually waiting several seconds before the particle cloud publishes the device name which might be a feature killer depending on your battery size and charge pattern.

I resolved this by hard coding the deviceName in my code. This has the drawback of never allowing me to change the device name without also changing that string in my code, but that’s fine with me.

I’m glad you are getting some use out of this project! Also thanks for the recommendations and bugs you pointed out! I haven’t had time lately to get back into this project but I recently purchased 3 more Photons to begin work on my sensor network so I will definitely be fixing the bugs you pointed out and implementing the features you suggested. I’ll keep you updated! Thanks again!