Best way to programming this app?

Hi!

First of all, i am new in Eclipse and Monkey C programming language. But i have good background programming in C#.

I am currently working with a vivoactive 3 watch given up by Garmin to my PhD research. My aim is to progrmming an app which be able to get the watch sensor signals to externally processing it. Concretelly i need the accelerometre and the hearth rate signals. I have read the Garmin´s official website programmer guide to get some background on Monkey C. But i would like to ask about what will be the best way to get these signals and send it from the watch to an smartphone. I would like to run the app in background, so i would not need to programming a GUI.

Someone can help me on "where to begin":

I would like to know what is the best App Type for my project, if there exist some open source/example apps to get the signal and communicate the watch and finally how i can store data on the watch.

For example, i am having problems creating a .xml file on resources folder, to use JSON as in Garmins example. When i compile the code, i get errors.:

Garmin example:

<resources>
<jsonData id="jsonDictionary">{"key":"value", "3":"three", "three":3}</jsonData>
<jsonData id="jsonArray">[1,2,3,4,5,6]</jsonData>
<jsonData id="jsonMix">[1,{"1":"one"},["a","b","c"]]</jsonData>
<jsonData id="jsonPrimitive">5</jsonData>
<jsonData id="jsonFile" filename="data.json"/>
</resources>


Errors:

BUILD: ERROR: Problem validating a resource file: Premature end of file.
BUILD: com.garmin.monkeybrains.resourcecompiler.ResourceException: Problem validating a resource file: Premature end of file.
BUILD: at com.garmin.monkeybrains.resourcecompiler.DocumentParser.validateResourceFile(DocumentParser.java:285)
BUILD: at com.garmin.monkeybrains.resourcecompiler.DocumentParser.addJungleResources(DocumentParser.java:399)
BUILD: at com.garmin.monkeybrains.Project.addProjectResources(Project.java:163)
BUILD: at com.garmin.monkeybrains.ProjectBuilder.addCIQProjectFiles(ProjectBuilder.java:210)
BUILD: at com.garmin.monkeybrains.Monkeybrains.runPRGCompiler(Monkeybrains.java:1229)
BUILD: at com.garmin.monkeybrains.Monkeybrains.compileApplication(Monkeybrains.java:1024)
BUILD: at com.garmin.monkeybrains.Monkeybrains.run(Monkeybrains.java:2315)
BUILD: at com.garmin.monkeybrains.Monkeybrains.simpleMain(Monkeybrains.java:254)
BUILD: at com.garmin.monkeybrains.Monkeybrains.simpleMain(Monkeybrains.java:236)
BUILD: at com.garmin.monkeybrains.Monkeybrains.main(Monkeybrains.java:283)
BUILD: Caused by: org.xml.sax.SAXParseException; systemId: file:/D:/1.-GIT/4.%20Tesis/Desarrollo-Tesis/Desarrollos/03-Pruebas%20Recursos/resources/jsonData.xml; lineNumber: 1; columnNumber: 1; Premature end of file.
BUILD: at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1471)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:1013)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:534)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.StreamValidatorHelper.validate(StreamValidatorHelper.java:176)
BUILD: at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:115)
BUILD: at java.xml/javax.xml.validation.Validator.validate(Validator.java:124)
BUILD: at com.garmin.monkeybrains.resourcecompiler.DocumentParser.validateResourceFile(DocumentParser.java:281)
BUILD: ... 9 more
BUILD: usage: monkeyc [-a <arg>] [-d <arg>] [-e] [--Eno-invalid-symbol] [-f <arg>] [-g]
BUILD: [-h] [-i <arg>] [-m <arg>] [-o <arg>] [-p <arg>] [-r] [-s <arg>] [-t] [-u
BUILD: <arg>] [-v] [-w] [-x <arg>] [-y <arg>] [-z <arg>]
BUILD: -a,--apidb <arg> API import file
BUILD: -d,--device <arg> Target device
BUILD: -e,--package-app Create an application package.
BUILD: --Eno-invalid-symbolDo not error when a symbol is found to be invalid
BUILD: -f,--jungles <arg> Jungle files
BUILD: -g,--debug Print debug output
BUILD: -h,--help Prints help information
BUILD: -i,--import-dbg <arg> Import api.debug.xml
BUILD: -m,--manifest <arg> Manifest file
BUILD: -o,--output <arg> Output file to create
BUILD: -p,--project-info <arg>projectInfo.xml file to use when compiling
BUILD: -r,--release Strip debug information
BUILD: -s,--sdk-version <arg> SDK version to target
BUILD: -t,--unit-test Enables compilation of unit tests
BUILD: -u,--devices <arg> devices.xml file to use when compiling
BUILD: -v,--version Prints the compiler version
BUILD: -w,--warn Show compiler warnings
BUILD: -x,--excludes <arg> Add annotations to the exclude list
BUILD: -y,--private-key <arg> Private key to sign builds with
BUILD: -z,--rez <arg> Resource file

Hope you can help me.

Regards!

  • My json resource file looks like this, so it is different format from yours:

    <jsonDataResources>
       	<jsonData id="singleValue">
            3.14
       	</jsonData>
    </jsonDataResources>

    On the communication side of CIQ, I've never tried it, so can't help Slight smile

  • Thanks! I solved it yesterday, with a sample from the garmin guide. It looks like yours.

    The problem i have now is how to get the accelerometre and hearth rate data, and how to communicate it.

  • The problem i have now is how to get the accelerometre and hearth rate data, and how to communicate it.

    If you want to receive the data in your app and then do stuff with it, you can use the Sensor module and access the data directly:

    using Toybox.WatchUi;
    using Toybox.Sensor;
    
    class MyBehaviorDelegate extends WatchUi.BehaviorDelegate
    {
        hidden var _enabled;
        
        function initialize() {
            _enabled = false;
        }
    
        function onSelect() {
    
            _enabled = !_enabled;
            if (_enabled) {
            
                // enable support for external heart rate sensors
                Sensor.setEnabledSensors([ Sensor.SENSOR_HEARTRATE ]);
                
                // start receiving low frequency data (heart rate)
                Sensor.enableSensorEvents(self.method(:onLowFrequencyData));
                
                // var options = {
                //    :period => 1,           // invoke the callback once a second
                //    :accelerometer => {
                //        :enabled => true,
                //        :sampleRate => 25,  // 25 samples/second
                ////        :includePower => true,
                ////        :includePitch => true,
                ////        :includeRoll => true,
                //    },
                //    :heartBeatIntervals => {
                //        :enabled => true
                //    }
                //};
                
                //// start receiving high frequency accelerometer and beat-to-beat interval data
                //Sensor.registerSensorDataListener(self.method(:onHighFrequencyData), options);
            }
            else {
                //// stop receiving high frequency sensor updates
                //Sensor.unregisterSensorDataListener();
                
                // stop receiving low frequency sensor updates
                Sensor.enableSensorEvents(null);
                
                // disable the heart rate sensor
                Sensor.setEnabledSensors([]);
            }
            
            return true;
        }
        
        function onLowFrequencyData(sensorInfo) {
            System.println("Heart Rate   : " + sensorInfo.heartRate);
            System.println("Accelerometer: " + sensorInfo.accel); // array of [ X, Y, Z ]
            System.println("Magnetometer : " + sensorInfo.mag);   // array of [ X, Y, Z ]
        }
        
        function onHighFrequencyData(sensorData) {
            var accelerometerData = sensorData.accelerometerData;
            if (accelerometerData) {
                // you'll want to process the data here
                
                // these should have 25 samples of accel data each
                // accelerometerData.x;
                // accelerometerData.y;
                // accelerometerData.z;
    
                // if you requested these when registering for high frequency data
                // accelerometerData.pitch;
                // accelerometerData.roll;
                // accelerometerData.power;
            }
    
            var heartRateData = sensorData.heartRateData;
            if (heartRateData != null) {
                // you'll want to process the data here
    
                // expect 0-5 beat-to-beat intervals
                // heartRateData.heartBeatIntervals; // array of beat intervals in milliseconds
            }
        }
    }

    If you just want to record the data to FIT file and analyze it offline, you'd also have to create a recording session and enable logging of the accelerometer data. You don't want to record for an extended period of time since it is throwing data into the file at ~300 bytes/second.

            if (_session == null) {
                // setup sensor logger to enable recording of accelerometer
                // data. don't leave this recording for long, it is recording
                // lots of data
                var sensorLogger = new SensorLogging.SensorLogger({
                                        :accelerometer => {
                                            :enabled => true
                                        }
                                    });
    
                _session = ActivityRecording.createSession({
                     :name => "Generic",
                     :sport => ActivityRecording.SPORT_GENERIC,
                     :subSport => ActivityRecording.SUB_SPORT_GENERIC,
                     :sensorLogger => sensorLogger
                });
                _session.start();
            }
            else {
                _session.stop();
                _session.save();
                _session = null;
            }
    

  • Thanks Travis!

    I am a novice with Eclipse and Connect IQ and i am currently trying and testing simple apps reading the Garmins guide to get fluent with the enviroment.

    What i want to do with my App is just monitor hearth rate and accelerometre data and when some event is triggered i would like to store this data during this events, with the aim off process it later in a remote server. 

    In your replay you said me "You don't want to record for an extended period of time since it is throwing data into the file at ~300 bytes/second.". So, on this basis, i think that i have 2 options:

    • Store the data in the watch during the event and once it has finished send to the mobile phone and pull to the server
    • Send the data in real time to the mobile phone, store it there and then pull.

    I am not sure about how long an event could be (i have asked to my supervisor about it). But i think that the first option will be the best to start.

    I will try to test your code to get sensor data this week. 

    But my questions now are:

    • The best way to save the data in a file (in the watch)
    • How to send this file to a mobile phone 

    By the way, the "recording sesion"  you said above, is like use a thread to get data? i think this will be the best way to save the data for this particular purpose. What is your recomendation?

    Hope you can help me.

    Thanks and regards!

  • The ConnectIQ platform doesn't provide a generic File API. If you want to store data, there are really only a few ways to do it.

    1. Store the data using ActivityRecording.Session (in a FIT file)
    2. Cache the data using Application.Storage
      1. Push the data off device using Communications.makeWebRequest
      2. Push the data off device using Communications.transmit

    There are actually two different ways to handle the first. Both involve creating a Session via a call to ActivityRecording.createSession(). In one case, you can provide a :sensorLogger attribute to the createSession call (see the PitchCounter sample app). In this case a lot of data will be written to the FIT file.. The X/Y/Z accelerometer data is 3 floats (12 bytes) and there could be as many as 25 samples per second. The second way would be to not use the sensorLogger, but to use the functionality in the FitContributor module. In this case, you'd probably not write all of the accelerometer data to the fit file, you'd do the calculation on device and set the field value to indicate the detected event or state change.

    Using the FIT file has the advantage that you don't really need to do anything to get the file off the device as it would be automatically uploaded to Garmin Connect. You'd need to provide a way to get the original FIT file out of Garmin Connect, which is a separate issue entirely.

    The Application.Storage module is more convenient, but more limiting since there are limitations on the amount of data you can store in the file. You'd probably need to cache data on the device to handle the case that your phone/internet connection is not always available. If you can keep the amount of data you need to cache relatively small (say by only caching a fixed number of records), this could be made to work.

  • Thanks again Travis!

    First, i have been working with the sensor events example you send me in your last response and it works well. I will try to work with the session example on next week. Hope i won´t have problems.

    About the Sensors Data Management. I have read all the posibilities you told me. My main goal is to not lose any data, so if there is any way to get the FIT file from Garmin Connect, i think this is the best option. Besides, the communication problem is solved.

    About the Application.Storage module, you said there exist limitations on the amount of data to store in a file and i have to cache it in the device. I have a doubt here, when you talk about cache data, is this a way to been able to write all the data in the file or is just a way to save the files until you have connection with the phone?. 

  • This is just a way for you to save data that is associated with your application. My thought was that you might be able to cache your data into app storage as it is recorded, and then upload it and clear the cache when a connection became available. As you suggested, the FIT file route essentially does this all for you and it isn't as limited with the amount of data it can store.

  • Ok. 

    I will try to do it on next week and I will give you some feedback about how it was. 

    Because of you said, the problem using this method would be to find the FIT file in the phone, once you have sent it. Do you know how I can do it? I suppose that does not exist any Android API to do this task... 

  • the problem using this method would be to find the FIT file in the phone

    You don't find the FIT file on the phone. It may be cached on the phone temporarily, but it will not be stored there long term. You'd have to download the FIT file from Garmin Connect. For example, this is a previously uploaded FIT file from my Edge cycling computer:

      https://connect.garmin.com/modern/activity/3728700768

    If you use the Garmin Connect API, you should be able to programmatically find recent uploads for a given ConnectIQ account and then download the FIT file.

  • Ok. 

    If I understood correctly, your purposed approach is the next: once the FIT file is in the phone/Connect App, it will be temporary stored. So, using the mobile app programming SDK I might be able to make a program and get this file before it is deleted. Is this correct?