Invalid Value: Cannot convert Object to Long

I have a weird error that I can't seem to debug. I have made this app which collects data, including accelerometer data, for a separate project. It runs correctly on all devices on the simulator so I uploaded it to the store. It then ran fine on multiple different watches but on the forerunner 245 & 245 music (potenitally all devices api level v3.3) it crashes ~0.5s after you press start.

Reading the ciq_log file its says:

Error: Invalid Value
Details: Cannot convert Object to Long
Time: 2025-02-22T17:55:48Z
Part-Number: 006-B3077-00
Firmware-Version: '13.70'
Language-Code: eng
ConnectIQ-Version: 5.0.2
Filename: F2MD1445
Appname: Orienteering
Stack:
- pc: 0x10000eb2
File: '..\OnalysisApp.mc'
Line: 223
Function: onSensorEvent_HD

This is the function in question:

    function onSensorEvent_HD(data as Sensor.SensorData) as Void{
        if ($.activity != null and $.activity.isRecording()){
            $._data.storeSensorData(data);

            if ($._data.sensorAccelX != null and $.fieldSensAccelX != null){
223 ->                ($.fieldSensAccelX as FitContributor.Field).setData($._data.sensorAccelX as Object);
            }
            if ($._data.sensorAccelY != null and $.fieldSensAccelY != null){
                ($.fieldSensAccelY as FitContributor.Field).setData($._data.sensorAccelY as Object);
            }
            if ($._data.sensorAccelZ != null and $.fieldSensAccelZ != null){
                ($.fieldSensAccelZ as FitContributor.Field).setData($._data.sensorAccelZ as Object);
            }
        }
    }
and this is how the fields are made:
            $.fieldSensAccelX = activitySession.createField("Acceleration_X", OnalysisApp.FITFIELD_SENSORACCELERATIONX_HD, FitContributor.DATA_TYPE_SINT8, { :count => OnalysisApp.SAMPLERATE_ACCELERATION, :mesgType => FitContributor.MESG_TYPE_RECORD as Number, :units => Ui.loadResource(Rez.Strings.unitSensorAcceleration) as String });
            $.fieldSensAccelY = activitySession.createField("Acceleration_Y", OnalysisApp.FITFIELD_SENSORACCELERATIONY_HD, FitContributor.DATA_TYPE_SINT8, { :count => OnalysisApp.SAMPLERATE_ACCELERATION, :mesgType => FitContributor.MESG_TYPE_RECORD as Number, :units => Ui.loadResource(Rez.Strings.unitSensorAcceleration) as String });
            $.fieldSensAccelZ = activitySession.createField("Acceleration_Z", OnalysisApp.FITFIELD_SENSORACCELERATIONZ_HD, FitContributor.DATA_TYPE_SINT8, { :count => OnalysisApp.SAMPLERATE_ACCELERATION, :mesgType => FitContributor.MESG_TYPE_RECORD as Number, :units => Ui.loadResource(Rez.Strings.unitSensorAcceleration) as String });


I don't understand which part of the line the error is coming from as I have verified that $._data.sensorAccel is a integer array of the correct size and I am not attempting to convert it to a long anywhere. Thanks in advance for the help!
  • The last time I checked, although array types exist in the FIT specification, you can't actually write a FIT array type using Connect IQ (unless you count DATA_TYPE_STRING as an array).

    Here's a related thread:

    https://forums.garmin.com/developer/connect-iq/f/discussion/315947/fitcontributor-setdata-array

    DATA_TYPE_SINT8 refers to a single signed 8-bit integer, regardless of the value of :count. Similarly, all the other numerical CIQ FIT data types are single values, not arrays afaik (DATA_TYPE_*INT*, DATA_TYPE_FLOAT, and DATA_TYPE_DOUBLE)

    As per the documentation for the :count option in createField:

    > The number of elements to add to the Field if it is an Array

    (emphasis mine)

    > This is also the maximum combined size of strings plus null terminators if the type is DATA_TYPE_STRING (Default 1)

    My understanding is that :count will be ignored if the type is not DATA_TYPE_STRING. Yeah, the documentation is prolly a bit misleading or confusing in this respect. e.g. why write "if it is an Array" if the field cannot be an Array? (Especially since the capitalization of Array suggests that "Array" refers to the Monkey C Array type (i.e. not a String), and not just the generic concept of an array, which could include strings.

    If you are looking to record sensor data (accel, gyro or magnetometer) at a rate higher than 1 value per second to the FIT file, you might want to look into SensorLogger.

    [https://developer.garmin.com/connect-iq/api-docs/Toybox/SensorLogging/SensorLogger.html]

    https://developer.garmin.com/connect-iq/core-topics/sensors/#loggingaccelerometerdata

    My understanding is that SensorLogger is really meant to help devs test their apps by creating FIT files that can be played back in the sim with the recorded sensor data, not for end users to consume the data.

    Then again, end users can't really visualize array-type fields in Connect, either (afaik).

  • On a side note you have a few seemingly unnecessary casts in your code:

    - FitContributor.MESG_TYPE_RECORD as Number

    This should be unnecessary as MESG_TYPE_RECORD should already be a Number

    - $._data.sensorAccelX as Object (and similar casts)

    These should be unnecessary all types in Monkey C are derived from Object

    In general, I think that casts in Monkey C should be avoided as much as possible. (Same goes for TypeScript, imo). I wouldn't use a cast unless it's absolutely necessary, because when you use unnecessary casts:

    1) at best, you gain nothing (Monkey Types is a compile-time only type checking system; casts don't have any direct run-time effect, although they can obscure bugs which happen at run-time, by preventing them from being caught at compile-time)

    2) at worst, you hide an actual run-time error in your code, which would've been caught at compile-time without the cast

    3) either way you gain a false sense of security

    4) it can be a bad habit which leads to errors (see 2)

    The situation is worse in Monkey C compared to TypeScript, because at least TypeScript will give you a warning or error if you try to cast between 2 unrelated / incompatible types.

    But I've also seen subtle bugs in TypeScript due to unnecessary / subtly incorrect casts.

  • Thanks for the quick, detailed response! Seems weird that I am able to get an array into my fit record most of the time (and every time in the simulator) if that shouldn't work. Sensorlogger should do the job for what I need it for, this data goes back into another program so the user never needs to see the raw data.

    One thing I notices using sensorlogger though is that it seems to skip seconds in places/ have 2 records in the same second. Is there a way to handle this/ make sure it doesn't happen as I never seemed to have that issue when getting the data the other route. 

    Thanks for picking up my bad habits too!

  • Seems weird that I am able to get an array into my fit record most of the time (and every time in the simulator) if that shouldn't work.

    Hmmm. Maybe I was wrong about my previous assumption that array FIT data doesn't work in CIQ, if you're able to get it to work on some devices. Or maybe it only works with newer devices?

    When you're able to successfully write array data to FIT, I assume you can read all the data back? (i.e. all the data is actually stored, not just the first element as in the linked post from a couple of years ago). You should be able to verify this quickly with https://www.fitfileviewer.com.

    Might be worth filling out a bug report. Maybe the CIQ team can shed some light on this.

  • Yep all 25 values are there for each second, interestingly for both the fr645 (api v3.2) and fenix 6 (v3.4) (amongst others). Even when it crashes on the fr245m (v3.3) it still records 1 second on data, with all 25 accelerometer values written, before crashing which makes it even weirder. why does it write 1 record then crash writing the next one??

    Thanks for the help, I'll file a bug report and see what the team say. In the meantime sensorlogger should be a good enough work around

  • When you call setData, that doesn't force a write to the fit.  It's written the next time the fit is written.  And there is smart and every second recording.  Not only is there a limit to the number of fields, there is a limit on the amount of data that all fields combined try to write.

    So too many or too much data

    Also understand that 25 fields might be a bit extreme.  If the user installs another field that also tries to write it's own data, either of the DFs could be impacted.  I think it was Strava that first ran into this.

  • Can you log the data before you try to record it and compare if the recorded data is really what's logged or some garbage that was in the memory when the crash happened?