Working TEMPERATURE Datafield - EDGE (Almost)

The following code works great as a data field on my Edge 820 to fire off a background process to retrieve the internal temperature. It runs every 5 minutes, which is a limitation of the frequency of background processes. That's ok. Thanks JIM for the pointers!

The only problem is that it often waits several minutes before it gets the first reading... I'm sure there is a simple fix to get it to retrieve the sensor data immediately the first time before waiting 5 minutes before the next retrieval.

The "paintScreen" code simply writes the current time of data as well as the temperature and the time the sensor was retrieved.

The rest of the code is the mechanisms to manage the background process and associated data.

If anyone can suggest the change I need to prevent the screen from saying: "Waiting...." for several minutes before the first TEMP retrieval, that would be awesome.

=======================================================================================



using Toybox.Application as App;
using Toybox.Graphics as Gfx;
using Toybox.WatchUi as Ui;
using Toybox.Background;
using Toybox.System;
using Toybox.Sensor;
using Toybox.Graphics as Gfx;
using Toybox.Lang;
using Toybox.Time;

var ToD, ToT; // time of day and time that temp was retrieved

// Grab Time of Day and Internal Temp Sensor via Background Process and Paint Screen
class paintScreen extends Toybox.WatchUi.DataField {
function initialize() { DataField.initialize(); }
function onUpdate(dc) {
var w = dc.getWidth() / 2, h = dc.getHeight() / 2;

ToD = Lang.format("$1$:$2$", [System.getClockTime().hour, System.getClockTime().min.format("%02d")]);
var message = App.getApp().backgroundMessage;
dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); dc.clear();
dc.drawText(w, h, Gfx.FONT_MEDIUM, "ToD: " + ToD, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
dc.drawText(w, h + 40, Gfx.FONT_MEDIUM, message, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
}
}

(:background)
class BackgroundApp extends App.AppBase {
var backgroundMessage = "Waiting...";
function initialize() { AppBase.initialize(); }
function getInitialView() { Background.registerForTemporalEvent(new Time.Duration(5 * 60)); return [ new paintScreen() ]; }
function onBackgroundData(data) { backgroundMessage = data; Ui.requestUpdate(); }
function getServiceDelegate() { return [new BackgroundServiceDelegate()]; }
}

// The Service Delegate is the main entry point for background processes
(:background)
class BackgroundServiceDelegate extends System.ServiceDelegate {
var intTEMP = new[2];

function initialize() { System.ServiceDelegate.initialize(); }
function onTemporalEvent() {
Sensor.setEnabledSensors( [Sensor.SENSOR_TEMPERATURE] );
Sensor.enableSensorEvents( method(:onSnsr) );
}

function onSnsr(sensor_info)
{
if( sensor_info.temperature != null ) { intTEMP[1] = (sensor_info.temperature * 9.0/5.0 + 32.0).format("%.1f") + " F"; }
else { intTEMP[1] = "--- F"; }
ToT = Lang.format("$1$:$2$", [System.getClockTime().hour, System.getClockTime().min.format("%02d")]);
intTEMP[0] = ToT;
Background.exit(intTEMP);
}
}
  • The reason it waits at times gets back to what I said about deleting the temporal event when you exit. Once you register the temporal event, it fires about every 5 minutes, all the time, and can fire at most every 5 minutes. So if it just fired,and then you start using your DF, it might be another 5 minutes before it fires again.

    You need to delete the temporal event when the DF exits.

    The trick here is that onStop() in AppBase is called when the background exits and also when the main app exits, and you need to know who's calling it. A way you do this by way of setting a variable in the background service delegate and checking it in onStop(). So something like

    var inBackground=false;

    class MyApp extends App.AppBase {

    (other stuff)

    function onStop(state) {
    if(!inBackground) {Background.deleteTemporalEvent();}
    }
    }

    class BackgroundServiceDelegate extends System.ServiceDelegate {
    function initialize() {
    System.ServiceDelegate.initialize();
    inBackground=true;
    }

    (other stuff)
    }



    inBackground is a global here, but it's a seperate global in the main app and the background. So when you set it in the ServiceDelegate, it's in the context of the background process, and not in the main app.

    So, when the DF exits, the temporal event is stopped, so next time you register, it will run right away. (if it's been 5 minutes or more)

    There's other ways to do a gate like this for onStop(), but a thing to understand is that things in the AppBase can be called only by the background, only by the main app, or by both. initialize(), onStart() and onStop() are called by both and getInitialView() by the main app, for example.