Using the simulator to test Edge 1030 internal barometer readings?

I am attempting to use the Edge 1030 internal barometer pressure information within my Connect IQ app.

Connect IQ Device Simulator 7.3.0

I set up the test using:

---------------

import Toybox.Sensor;

private var _info = null;

private var _barometer = Lang.Float;

_info = Sensor.Info;

_barometer = _info.pressure;

System.println(_barometer) // this prints 'null'

------------

My problem starts when I want to test the _barometer object with an actual simulated value.  I don't know how to do this.

Running:  Simulation -> Activity Data

doesn't seem to supply the _barometer with an actual value.

If I change the above code to:

System.println(_barometer.toString()) // throws errors

Any direction on this would be appreciated.  Relatively new to Monkey C and Connect IQ

Top Replies

All Replies

  • The posted code isn't quite right.

    private var _info = null;
    private var _barometer = Lang.Float; // [1]
    _info = Sensor.Info; // [2]
    _barometer = _info.pressure;

    [1] it doesn't make sense to initialize _barometer to a class (Lang.Float). Perhaps you meant "private var _barometer as Lang.Float;". It would be more correct to type "private var _barometer as Lang.Float or Null; " tho. (I would probably name the variable something like pressure as opposed to barometer, though. Variable names should be as descriptive as possible.)

    [2] you can't directly access Sensor.Info (a class) and expect useful data to be populated in it. You need an instance of the Sensor.Info class.

    _info = Activity.Info;; // [3]
    _barometer = _info.ambientPressure;

    [3] same comment as [2]

    Here is a rough explanation of the difference between a class and an instance of a class, using a hypothetical House class:

    House class: general blueprints for making a house
    Instance of House class: a specific house

    Imagine that the house class has a member variable called numOccupants, which is a Number representing the amount of people living in the house. Asking how many people live in the blueprints for a house doesn't really make sense, so referring to House.numOccupants doesn't make sense. It would make sense to ask how many people live in a specific house, though.

    var sherlocksHouse = getHouseAtAddress("221B Baker Street");
    var numOccupants = sherlocksHouse.numOccupants; // number of occupants at 221B Baker Street
    var numOccupants2 = House.numOccupants; // number of occupants at ??? [4]

    [4] "House.numOccupants" isn't meaningful, and as a matter of fact, it is a compile error if the type checker is enabled: "Cannot find symbol ':numOccupants' on class definition '$.House'."

    In the case of Sensor.Info:

    Sensor.Info: description of the structure of sensor information
    Instance of Sensor.Info: actual sensor information

    In general, you almost never access member variables on the class itself. Typically you call a function to obtain an instance of a class, and access member variables on the instance.

    This code should work better:

    import Toybox.WatchUi;
    import Toybox.Sensor;
    import Toybox.Activity;
    import Toybox.Timer;
    
    function YourAppView extends WatchUi.View {
        var timer;
        function initialize() {
            View.initialize();
            timer = new Timer.Timer();
            // schedule update for every second (1000 ms)
            timer.start(method(:timerCallback), 1000, true);
            //...
        }
    
        function timerCallback() as Void {
            WatchUi.requestUpdate(); // request update (onUpdate() will be called)
        }
    
        function onUpdate() {
            var sensorInfo = Sensor.getInfo();
            // type declarations aren't necessary (or allowed) for local variables, as the type is inferred
            var sensorInfoPressure = sensorInfo.pressure;
            // no need for null check here, as toString()/format() is not called
            System.println("sensorInfo.pressure = " + sensorInfoPressure);
    
            var activityInfo = Activity.getActivityInfo();
            var activityInfoPressure = activityInfo.ambientPressure;
            // no need for null check here, as toString()/format() is not called
            System.println("activityInfo.ambientPressure = " + activityInfoPressure);
    
            // ...
        }
    }

  • I also noticed that while the following code correctly produces a type error...

    var pressure = Sensor.Info.pressure; // Cannot find symbol ':pressure' on class definition '$.Toybox.Sensor.Info'

    ...the following code [which is very similar to your original code] does not produce a type error:

    var info = Sensor.Info;
    var pressure2 = info.pressure;

    I filed a bug report for this: forums.garmin.com/.../type-checker-does-not-detect-access-of-non-existent-symbol-on-class-definition-when-class-is-assigned-to-a-variable

  • The code within the <function onUpdate()> works within the app and generates dummy values on my Debug Console.  Now that you have shown these existing methods, I have been able to find where they are located in the documentation.  Appreciate this.  

    I had tried creating an instance of the class and then accessing the pressure through the instance, within my <View.mc> file.  This was a dead end, and as you have stated, no error was thrown.

    Also tried creating a <WatchUi.DataField> to overlay onto the app layouts.  This would have been ideal but have currently been unable to get this working.  Having data that updates once per second with the compute() function would be helpful.  Tracing my code using System.println() statements, it is noted that when calling the DataField from within a view, the compute() function is not executed.

    Again, thanks for all the direction.

  • No problem!

    A CIQ data field and a CIQ device app are separate app types, they can't be used with each other. Specifically, you can't implement a device app which instantiates a DataField - as you found, that simply won't work.

    If you want to implement an app which completely replaces a built-in activity, with full control over the UI, create a CIQ device app. However, you will have to reinvent the wheel for many features that are specific to whatever activity you are replacing.

    If you want to create a data field which can be added to an existing built-in activity, create a CIQ data field. Furthermore, if you want to be spared the burden of rendering the data field's display, and your field only returns a simple value (number or string), then you should create a simple data field (as opposed to a complex data field). Only data fields have a compute() function will be automatically called once per second, not other CIQ app types.

  • I had tried creating an instance of the class

    In general, the correct way to create an instance of a class would be to use the new keyword.

    e.g. var sensorInfo = new Sensor.Info();

    The fact that Monkey C allowed you type something like var info = Sensor.Info is a bug, imo. (I mentioned that in the bug report).

    However, using the new keyword is still incorrect (in the specific cases of the Sensor.Info class and the Activity.Info class), as the API provides methods for you to retrieve instances of those classes which have all the relevant data populated. If you create the instances yourself, you still have no way to populate their fields with the correct values.

    Typically if an API is returning data to you in the form of a class instance, you don't create the class instance yourself, you call a function which creates the class instance, populates all the data, and returns the instance to you.

    The process of creating class instances is usually meant for data that you control (as opposed to data that you want to receive).

  • Good to know that the CIQ DataField cannot be used within a CIQ device App, and that I wasn't just overlooking something.

    Did test,

    var sensorInfo = new Sensor.Info();

    As stated, the variable contains null values, which isn't helpful.

    Going to try to implement a `Timer.Timer()`, similar to the snippet supplied above, to replicate updating functionality like the DataField `compute()` function.  

  • Did test,

    var sensorInfo = new Sensor.Info();

    Sorry, I shouldn't have used that as an example. My point was that:

    - if you actually did want to create a new instance of the Sensor.Info class, the correct code would be "var sensorInfo = new Sensor.Info();" and not "var sensorInfo = Sensor.Info;"

    - but you don't want to create a new instance of Sensor.Info class anyway

    It was intended to be more of a general statement about how to create new instances of classes, and not a suggestion that it's the right thing to do in this case.