Fit Contributor

Hi,

just a question to the FitContributor: If I calculate the average values of an high-intensity period of an interval and write those values to the Fit file, does Garmin connect web make those values somehow visible or is it just a value in the Fit file?
  • Paviamontenegro

    I'm not sure why you set the nativeNum option to 1. It's probably not preventing the data from getting written to the fit file, but a session message with a nativeNum = 1 indicates that your data is an "event_type" according to the profile.xlsx spreadsheet in the fit SDK.

    It looks like maybe you used the FITtoCSV.bat file to get the two rows of data that you showed from the FIT file. If your session data is showing up in the FIT file, it would be in a session message. On my files, it shows up near the bottom of the CSV file and way on the right end of the row. In the following line, the session message added by the FIT Contributor is the CIQ_device_info array way at the right end of the line.

    Data,6,session,timestamp,846874116,s,start_time,846874013,,start_position_lat,535367896,semicircles,start_position_long,-1126437926,semicircles,total_elapsed_time,97.278,s,total_timer_time,97.278,s,total_distance,465.22,m,total_cycles,106,cycles,nec_lat,535367953,semicircles,nec_long,-1126437926,semicircles,swc_lat,535349190,semicircles,swc_long,-1126457075,semicircles,unknown,Bike,,message_index,0,,total_calories,3,kcal,avg_speed,4.782,m/s,max_speed,6.382,m/s,total_ascent,0,m,total_descent,3,m,first_lap_index,0,,num_laps,2,,unknown,0,,event,9,,event_type,1,,sport,2,,sub_sport,0,,avg_heart_rate,78,bpm,max_heart_rate,89,bpm,avg_cadence,73,rpm,max_cadence,87,rpm,total_training_effect,1,,trigger,0,,unknown,0,,avg_fractional_cadence,0.1875,rpm,max_fractional_cadence,0,rpm,enhanced_avg_speed,4.782,m/s,enhanced_max_speed,6.382,m/s,CIQ_device_info,31|10|161|1|0|0|76|

    Also, check out posts 6-10 in this thread. There was a bug where the VA would not write session messages in Apps. I don't know if it's been fixed and I don't have a VA handy to test right this minute, but there is a work around described.
    https://forums.garmin.com/showthread.php?363975-Fenix-3-does-not-record-Session-message-in-FIT-file
  • Thank you very much for your reply, MoxyRoger!

    I did really everything from the mentioned posts/thread...
    • Removed nativeNum
    • Changed all data to Session-based (got a LAP-field before, as you found out)
    • Use a custom field (according to the Vivoactive-Bug) -> That gives me a "You dont use the super-calass" warning

    But I´ve never got MonkeyGraph showing anything.
    The fit-file from the simulator includes my custom field-definition. But as you showed, there is nothing in the session-section in the csv.
    Neither in vivoactive, not in another device. Simulator or real device.

    What I can say now:
    It seems like the data is currently repeated. I initialized it with "0", then it is several times shown in the "data"-section and nothing in the "session"-section.
    At the end of the recording, I tried to store the correct value before and after session.stop(). Offcourse before session.save().
    Sys.println gives me the right value in the right format (single digit integer).
    From my point of view somenthing is wrong with MESG_TYPE_SESSION in my development :(

    resources.xml (It is seperated from strings.xml if that matters)
    <resources>
    <fitContributions>
    <fitField id="0"
    displayInActivitySummary="true"
    dataLabel="@Strings.menu_label_Rep"
    unitLabel="@Strings.unit_Rep"
    sortOrder = "0"
    precision="0"/>

    <fitField id="1"
    displayInActivitySummary="true"
    dataLabel="@Strings.menu_label_Timer"
    unitLabel="@Strings.unit_Duration"
    sortOrder = "1"
    precision="0"/>

    <fitField id="2"
    displayInActivitySummary="true"
    dataLabel="@Strings.menu_label_Rest"
    unitLabel="@Strings.unit_Duration"
    sortOrder = "2"
    precision="0"/>
    </fitContributions>
    </resources>


    class Recorder
    {
    var session = null;
    var supportsRecording = null;

    var repetitionField = null;
    var durationField = null;
    var restingField = null;

    var customField = null;
    function initialize()
    {
    if(supportsRecording == null)
    {
    supportsRecording = Toybox has :ActivityRecording;
    customField = new CustomField();
    Sys.println("Supports recording");
    }
    }
    function ToggleRecording()
    {
    if(GlobalSetup.Recording && supportsRecording)
    {
    if(session == null )
    {
    var str = Ui.loadResource(Rez.Strings.main_label_Stretch);
    session = ARec.createSession({:name=>str, :sport=>ARec.SPORT_TRAINING , :subSport=>ARec.SUB_SPORT_FLEXIBILITY_TRAINING });

    CreateCustomFields();
    }
    if(session.isRecording())
    {
    session.stop();
    Sys.println("Stop recording");
    }
    else
    {
    session.start();
    Sys.println("Start recording");
    }
    }
    }
    function CreateCustomFields()
    {
    if(Toybox has :FitContributor)
    {
    //Repetitions
    var options = {
    :msgType => Fit.MESG_TYPE_SESSION,
    :units => Ui.loadResource(Rez.Strings.unit_Rep)
    };
    //0 Field ID - Repetitions
    repetitionField = customField.createField(Ui.loadResource(Rez.Strings.menu_label_Rep), 0, Fit.DATA_TYPE_UINT8, options);
    repetitionField.setData(0);

    //Stretch Duration
    options = {
    :msgType => Fit.MESG_TYPE_SESSION,
    :units => Ui.loadResource(Rez.Strings.unit_Duration)
    };
    //1 Field ID - Stretch Interval
    durationField = customField.createField(Ui.loadResource(Rez.Strings.menu_label_Timer), 1, Fit.DATA_TYPE_UINT16, options);
    durationField.setData(0);

    //2 Field ID - Rest Interval
    restingField = customField.createField(Ui.loadResource(Rez.Strings.menu_label_Rest), 2, Fit.DATA_TYPE_UINT16,
    {
    :msgType => Fit.MESG_TYPE_SESSION,
    :units => Ui.loadResource(Rez.Strings.unit_Duration)
    });
    restingField.setData(0);
    }
    else
    {
    Sys.println("Custom Fields not supported");
    }
    }
    function SaveRecording()
    {
    if(GlobalSetup.Recording && supportsRecording && session != null)
    {
    if(session.isRecording())
    {
    session.stop();
    }
    if(repetitionField != null)
    {
    repetitionField.setData(StretchTimer.reputation -1);
    Sys.print("Repetition: ");
    Sys.println(StretchTimer.reputation -1);
    }
    if(restingField != null)
    {
    restingField.setData(StretchTimer.GlobalSetup.RestDuration.value());
    Sys.print("Rest: ");
    Sys.println(StretchTimer.GlobalSetup.RestDuration.value());
    }
    if(durationField != null)
    {
    durationField.setData(StretchTimer.GlobalSetup.StretchDuration.value());
    Sys.print("Duration: ");
    Sys.println(StretchTimer.GlobalSetup.StretchDuration.value());
    }
    session.save();
    Sys.println("Store recording");
    }
    else
    {
    //deactivated recording during an activity => Remove
    if(supportsRecording && session != null)
    {
    if(session.isRecording())
    {
    session.stop();
    }
    AbortRecording();
    }
    }
    }
    }

    class CustomField extends Ui.DataField
    {
    // Constructor
    function initialize() {

    }
    }


    The CSV includes the following. It repeats "Data 10" and "Data 11" several times:
    Data,9,field_description,field_name,"Wiederholungen",,units,"wdh",,native_mesg_num,"20",,developer_data_index,"0",,field_definition_number,"0",,fit_base_type_id,"2",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
    Data,9,field_description,field_name,"Intervall Dehnen",,units,"s",,native_mesg_num,"20",,developer_data_index,"0",,field_definition_number,"1",,fit_base_type_id,"132",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
    Data,9,field_description,field_name,"Intervall Wechsel",,units,"s",,native_mesg_num,"20",,developer_data_index,"0",,field_definition_number,"2",,fit_base_type_id,"132",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
    Definition,10,record,timestamp,1,,distance,1,,Wiederholungen,1,,Intervall Dehnen,1,,Intervall Wechsel,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
    Data,10,record,timestamp,"860504593",s,distance,"0.0",m,Wiederholungen,"0",wdh,Intervall Dehnen,"0",s,Intervall Wechsel,"0",s,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
    Data,10,record,timestamp,"860504639",s,distance,"0.0",m,Wiederholungen,"0",wdh,Intervall Dehnen,"0",s,Intervall Wechsel,"0",s,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,


    "session" does´nt include any of my fields, nor the names, neither the expected values (they are nowhere in the file)

    That´s all I got... :(

    EDIT: If somebody is interested in testing that: App & Source (latest version)
  • I think the problem is that you need to session.start() before CreateCustomFields()
  • I think the problem is that you need to session.start() before CreateCustomFields()


    Didn´t changed anything :(
    It really seems, that the data is stored per LAP and not per SESSION, as I want it.
    Or probably it is related to the sport and subsport-type?!?

    [...]

    Okay, I got it... It was a typo :( After studying the docu, it says: "MESG_TYPE_RECORDING" is the default value. It has to be related to it and les voila:
    :msgType => Fit.MESG_TYPE_SESSION,
    :mesgType => Fit.MESG_TYPE_SESSION,

    The data is finally in MonkeyGraph! Displays "#value?" but I´ll figure that out :cool:

    Thanks everyone :rolleyes:
  • If you think you have solved all problems now, wait until you tried it on the real watch. To be honest the api is so buggy that I stopped development of the fitcontributor.
  • If you think you have solved all problems now, wait until you tried it on the real watch. To be honest the api is so buggy that I stopped development of the fitcontributor.

    Sadly, you are totally right :(
    I wrote a different app in the past with the same conclusion. But thought it got better, until I used the fitContributor...

    The simulator creates my three fields, gave them the right names and added the right values in the right format. Also at the right place (session).
    Monkeygraph shows the right name, but #VALUE? as value => Changed format, added nativeNum, checked datatypes and ENUM values to ensure it´s what I expect...
    Submitted the new version into the store (to make the fields known in GC).
    And they where not even included in the *.fit-file :(

    On the positive side of life:
    I changed the App-Properties from local (app.setProperty(ENUM)) to adjustable via smartphone (properties.xml & app.setProperty("PropertyName")). That worked great!
    The simulator messes up the stored values with the default values, but this is not happening on the real device.

    If someone with a different device then a Vivoactive, can possibly test if it is related to my device. That would be great! I´ve added the VA approach to use a custom DataField, but you´ll never know :rolleyes:
  • And they where not even included in the *.fit-file :(

    Just to repeat my experience, i'm only using MESG_TYPE_SESSION and if session.createField() is before session.start() there are no entries in the FIT file.
    My fields show up correct in Monkeygraph and GCM, but not in GC Web!

    I wanted to open a case with Garmin Connect, but this inferior support said that i should contact the developer (myself :confused:).
  • Just to confirm, I played around with session.createField() and session.start() order.
    It seems (for me) it must be after start() on a real device.
    And it is important to initialize the values (probably by 0).

    But:
    I wanted to remove the compiler-warning (Class 'CustomField' does not initialize its super class, 'DataField'), by using the super-class constructor.
    (Related to the Vivoactive approach, mentioned earlier in this thread)
    Putting Code in CODE- or QUOTE-Tags gives me a persmission Error...
    Just dont use the super-class "Ui.DataField.initialize();"

    That definitively has to be uncommented!
    Now the data is included in the fit-file on Garmin Connect and *drum roll* nothing is shown!

    Recorded another activity (without touching the app on the device) and the data is missing again...
    Comparing the two Fit.Files (after converting to CSV) several things are missing:
    • Data developer_data_id
    • Data field_description (Three times off course)
    • Definition developer_data_id
    • Definition field_description
    • Definition session (containing my three values)


    This is really depressing :( I think I´ll create a thread in the bug-thread and stop spending hour for hour, to find out nothing works as desired :mad:

    Working:
    https://connect.garmin.com/modern/activity/1670995263
    Not included:
    https://connect.garmin.com/modern/activity/1671005401
  • Hi,

    I realized that some guys were able to create a colored graph. The graph which I create in my apps are all black and I don't know why. Maybe you could review my code shortly. I am trying to write a number to the graph but defined it as float as recommended here in the thread.

    Here is my code (I define the color blue here):

    In the resources file:
    <strings>
    <string id="gpsStrengthGraphLabel">GPS Strength</string>
    <string id="gpsStrengthLabel">GPS Strength</string>
    <string id="gpsStrengthUnits">strength</string>
    </strings>


    <fitContributions>
    <fitField id="0" displayInChart="true" sortOrder="0" precision="1" chartTitle="@Strings.gpsStrengthGraphLabel" dataLabel="@Strings.gpsStrengthLabel" unitLabel="@Strings.gpsStrengthUnits" fillColor="#0000FF" />
    </fitContributions>


    and in the source file:
    var gpsStrengthField;
    var gpsStrengthColor;


    function initialize() {
    DataField.initialize();

    gpsStrengthField = createField("GPS Strength", 0, Fit.DATA_TYPE_FLOAT, { :mesgType=>Fit.MESG_TYPE_RECORD, :units=>"strength" });
    gpsStrengthField.setData(0);
    } // end of initialize


    function compute(info) {

    gpsStrengthColor = 0;
    if (info.currentLocationAccuracy != null) {
    gpsStrengthColor = info.currentLocationAccuracy;
    }

    gpsStrengthField.setData(gpsStrengthColor.toNumber());
    } // end of initialize


    I have really no clue why a black graph is created. Any hints?
  • It looks like you're using it right. Do you see the blue color when using the monkeygraph tool, or is this isolated to the graphs in Garmin Connect/Connect Mobile? If I were in your shoes, I'd go back to a test app based on the sample programs (MoxyDataField or MO2Display). Once you have that working, find the differences and apply those changes to your app.

    gpsStrengthField = createField("GPS Strength", 0, Fit.DATA_TYPE_FLOAT, { :mesgType=>Fit.MESG_TYPE_RECORD, :units=>"strength" });

    // snipped...

    gpsStrengthColor = 0;
    if (info.currentLocationAccuracy != null) {
    gpsStrengthColor = info.currentLocationAccuracy;
    }

    gpsStrengthField.setData(gpsStrengthColor.toNumber());



    Is this the data that you actually want to store in the .fit file? If so, you should reconsider the data type. Right now you're using a Float to represent to represent a Number (you don't need the precision or range). For that matter, you don't even need the full range of Number, you just need something to represent an integral value 0 to 4. You probably want DATA_TYPE_UINT8 (1 byte vs 4).