How to add developer fileld to record type FIT_MESG_RECORD

Hi,

For the last two months, I have been trying to add a developer field to the record message (like in the example with doughnuts earned) in C, but I am still doing something wrong.

My custom field is EGT in °C.

Please help.


int main(void)
{
FILE *fp;

data_crc = 0;
fp = fopen("bjx.fit", "wb");
WriteFileHeader(fp);

//tim
FIT_DATE_TIME timestamp = 1730214502 - 631148400; // 2021-09-08T01:46:40-0600Z in seconds since the FIT Epoch of 1989-12-31T:00:00:00Z
FIT_DATE_TIME start_time = timestamp;

// Write file id message.
{
FIT_UINT8 local_mesg_number = 0;
FIT_FILE_ID_MESG file_id_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_FILE_ID], &file_id_mesg);

file_id_mesg.time_created = timestamp;
file_id_mesg.type = FIT_FILE_ACTIVITY;
file_id_mesg.manufacturer = FIT_MANUFACTURER_DEVELOPMENT;
file_id_mesg.product = 0;
file_id_mesg.serial_number=0x01020304;
WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_FILE_ID], FIT_FILE_ID_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &file_id_mesg, FIT_FILE_ID_MESG_SIZE, fp);
}


// Write Device Info message.
{
FIT_UINT8 local_mesg_number = 0;
volatile FIT_DEVICE_INFO_MESG device_info_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_DEVICE_INFO], &device_info_mesg);

device_info_mesg.timestamp = timestamp;
device_info_mesg.device_index = FIT_DEVICE_INDEX_CREATOR;
device_info_mesg.manufacturer = FIT_MANUFACTURER_DEVELOPMENT;
device_info_mesg.product = 0; // USE A UNIQUE ID FOR EACH OF YOUR PRODUCTS
strcpy(device_info_mesg.product_name, "MotoMonitor+"); // Max 20 Chars
device_info_mesg.serial_number = 0x01020304;
device_info_mesg.software_version = 110; // 1.0 * 100
device_info_mesg.hardware_version = 12;
device_info_mesg.battery_voltage=12*256;
printf("id %d\r\n", FIT_MESG_DEVICE_INFO);
WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_DEVICE_INFO], FIT_DEVICE_INFO_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &device_info_mesg, FIT_DEVICE_INFO_MESG_SIZE, fp);
}


// Write Event message - START Event
{
FIT_UINT8 local_mesg_number = 1;
FIT_EVENT_MESG event_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_EVENT], &event_mesg);

event_mesg.timestamp = timestamp;
event_mesg.event = FIT_EVENT_TIMER;
event_mesg.event_type = FIT_EVENT_TYPE_START;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_EVENT], FIT_EVENT_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &event_mesg, FIT_EVENT_MESG_SIZE, fp);
}

// Write Event message - START Event
{
FIT_UINT8 local_mesg_number = 1;
FIT_EVENT_MESG event_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_EVENT], &event_mesg);

event_mesg.timestamp = timestamp;
event_mesg.event = FIT_EVENT_TIMER;
event_mesg.event_type = FIT_EVENT_TYPE_STOP_ALL;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_EVENT], FIT_EVENT_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &event_mesg, FIT_EVENT_MESG_SIZE, fp);
}

//FIT_MESG_DEVELOPER_DATA_ID
{
FIT_UINT8 local_mesg_number = 1;
FIT_DEVELOPER_DATA_ID_MESG developer_id_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_DEVELOPER_DATA_ID], &developer_id_mesg);

snprintf(developer_id_mesg.application_id,16,"0000000000000");
developer_id_mesg.developer_data_index = 10;
developer_id_mesg.application_version=111;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_DEVELOPER_DATA_ID], FIT_DEVELOPER_DATA_ID_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &developer_id_mesg, FIT_DEVELOPER_DATA_ID_MESG_SIZE, fp);
}



{

WriteMessageDefinition(2,fit_mesg_defs[FIT_MESG_FIELD_DESCRIPTION],FIT_FIELD_DESCRIPTION_MESG_DEF_SIZE,fp);
FIT_FIELD_DESCRIPTION_MESG wlasne_pole;
wlasne_pole.developer_data_index = 0;
wlasne_pole.field_definition_number = 1;
wlasne_pole.fit_base_type_id = FIT_BASE_TYPE_UINT16;
wlasne_pole.fit_base_unit_id = FIT_FIT_BASE_UNIT_OTHER;
wlasne_pole.native_mesg_num = 0;
wlasne_pole.offset = 0;
sprintf(wlasne_pole.field_name,"EGT");
sprintf(wlasne_pole.units,"st.C");
WriteMessage(2,&wlasne_pole,FIT_FIELD_DESCRIPTION_MESG_SIZE,fp);

}


{
FIT_UINT8 local_mesg_number = 5;
FIT_DEV_FIELD_DEF devx;
devx.def_num=1;
devx.size = 1;
devx.dev_index=0;


WriteMessageDefinitionWithDevFields(local_mesg_number,fit_mesg_defs[FIT_MESG_RECORD], FIT_RECORD_MESG_DEF_SIZE,1,&devx,fp);
//WriteMessageDefinitionWithDevFields(local_mesg_number,fit_mesg_defs[FIT_MESG_FLYELECTRONICSDATA], FIT_FLYELECTRONICSDATA_MESG_SIZE,6,&,fp);

//WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_RECORD], FIT_RECORD_MESG_DEF_SIZE, fp);
timestamp = 1742419069 - 631148400;
for (int i = 0; i < 6; i++)
{
{
printf("x\r\n");
FIT_RECORD_MESG record_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_RECORD], &record_mesg);

record_mesg.timestamp = timestamp;
// Fake Record Data of Various Signal Patterns
record_mesg.distance = i; // Ramp
record_mesg.speed = 1; // Flatline
record_mesg.heart_rate = ((FIT_UINT8)((sin(TWOPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine
record_mesg.cadence = ((FIT_UINT8)(i % 255)); // Sawtooth
record_mesg.power = ((FIT_UINT16)((i % 255) < 127 ? 150 : 250)); // Square
record_mesg.enhanced_altitude = 1;
record_mesg.position_lat = 0;
record_mesg.position_long = ((FIT_SINT32)(i * SC_PER_M));
record_mesg.distance = timestamp;
record_mesg.ball_speed = timestamp;

WriteMessage(local_mesg_number, &record_mesg, FIT_RECORD_MESG_SIZE, fp);
static uint16_t cht=0x0B;
//cht++;
WriteDeveloperField(&cht,sizeof(cht),fp);
timestamp++;
printf("x\r\n");

}
}
}

/*
// Write Record messages.

//The message definition only needs to be written once.
{

for (uint8_t i = 0; i < 5; i++)
{
{
FIT_FLYELECTRONICSDATA_MESG fly_data;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_FLYELECTRONICSDATA], &fly_data);
fly_data.timestamp = timestamp+(i*60);
fly_data.altitude = i*10;
fly_data.cht = i;
fly_data.egt = i*2;
fly_data.rpm = rand(1000);
// Fake Record Data of Various Signal Patterns

printf("DANE POMIAROWE %d\r\n",i);
WriteMessage(2, &fly_data, FIT_FLYELECTRONICSDATA_MESG_SIZE, fp);

//WriteDeveloperField(&i,1,fp);
timestamp++;
}
}

}
*/
// Write Event message - STOP Event
{
FIT_UINT8 local_mesg_number = 0;
FIT_EVENT_MESG event_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_EVENT], &event_mesg);

event_mesg.timestamp = timestamp;
event_mesg.event = FIT_EVENT_TIMER;
event_mesg.event_type = FIT_EVENT_TYPE_STOP;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_EVENT], FIT_EVENT_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &event_mesg, FIT_EVENT_MESG_SIZE, fp);
}

// Write Lap message.
{
FIT_UINT8 local_mesg_number = 0;
FIT_LAP_MESG lap_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_LAP], &lap_mesg);

lap_mesg.message_index = 0;
lap_mesg.timestamp = timestamp;
lap_mesg.start_time = start_time;
lap_mesg.total_elapsed_time = (timestamp - start_time) * 1000;
lap_mesg.total_timer_time = (timestamp - start_time) * 1000;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_LAP], FIT_LAP_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &lap_mesg, FIT_LAP_MESG_SIZE, fp);
}

// Write Session message.
{
FIT_UINT8 local_mesg_number = 0;
FIT_SESSION_MESG session_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_SESSION], &session_mesg);

session_mesg.message_index = 0;
session_mesg.timestamp = timestamp;
session_mesg.start_time = start_time;
session_mesg.total_elapsed_time = (timestamp - start_time) * 1000;
session_mesg.total_timer_time = (timestamp - start_time) * 1000;
session_mesg.sport = FIT_SPORT_STAND_UP_PADDLEBOARDING;
session_mesg.sub_sport = FIT_SUB_SPORT_GENERIC;
session_mesg.first_lap_index = 0;
session_mesg.num_laps = 1;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_SESSION], FIT_SESSION_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &session_mesg, FIT_SESSION_MESG_SIZE, fp);
}

// Write Activity message.
{
FIT_UINT8 local_mesg_number = 0;
FIT_ACTIVITY_MESG activity_mesg;
Fit_InitMesg(fit_mesg_defs[FIT_MESG_ACTIVITY], &activity_mesg);

activity_mesg.timestamp = timestamp;
activity_mesg.num_sessions = 1;
activity_mesg.total_timer_time = (timestamp - start_time) * 1000;

int timezoneOffset = -7 * 3600;
activity_mesg.local_timestamp = timestamp + timezoneOffset;

WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_ACTIVITY], FIT_ACTIVITY_MESG_DEF_SIZE, fp);
WriteMessage(local_mesg_number, &activity_mesg, FIT_ACTIVITY_MESG_SIZE, fp);
}


// Write CRC.
fwrite(&data_crc, 1, sizeof(FIT_UINT16), fp);

// Update file header with data size.
WriteFileHeader(fp);

fclose(fp);

return 0;
}

  • I took a look at the code you posted, and it appears to be a modification of c/encode.c in the FIT SDK.

    Here's what I did to get it running (on macos):

    - used FitSDKRelease_21.141.00

    - combined your code with encode.c as necessary

    - built with gcc, including the necessary FIT SDK source files (see next comment)

    Here's a diff file with the changes I made to get it to work properly:

    [you can't use it to literally patch the code you posted, as it's based on your code *after* I combined it with encode.c, formatted it, and got it to a state where it would compile. it's just here as an easy way to see what was changed]

    --- test-orig.c
    +++ test.c
    @@ -145,7 +145,7 @@
             Fit_InitMesg(fit_mesg_defs[FIT_MESG_DEVELOPER_DATA_ID], &developer_id_mesg);
     
             snprintf(developer_id_mesg.application_id, 16, "0000000000000");
    -        developer_id_mesg.developer_data_index = 10;
    +        developer_id_mesg.developer_data_index = 0;
             developer_id_mesg.application_version = 111;
     
             WriteMessageDefinition(local_mesg_number, fit_mesg_defs[FIT_MESG_DEVELOPER_DATA_ID], FIT_DEVELOPER_DATA_ID_MESG_DEF_SIZE, fp);
    @@ -156,11 +156,11 @@
     
             WriteMessageDefinition(2, fit_mesg_defs[FIT_MESG_FIELD_DESCRIPTION], FIT_FIELD_DESCRIPTION_MESG_DEF_SIZE, fp);
             FIT_FIELD_DESCRIPTION_MESG wlasne_pole;
    +        Fit_InitMesg(fit_mesg_defs[FIT_MESG_FIELD_DESCRIPTION], &wlasne_pole);
             wlasne_pole.developer_data_index = 0;
             wlasne_pole.field_definition_number = 1;
             wlasne_pole.fit_base_type_id = FIT_BASE_TYPE_UINT16;
    -        wlasne_pole.fit_base_unit_id = FIT_FIT_BASE_UNIT_OTHER;
    -        wlasne_pole.native_mesg_num = 0;
    +        wlasne_pole.native_mesg_num = FIT_MESG_NUM_RECORD;
             sprintf(wlasne_pole.field_name, "EGT");
             sprintf(wlasne_pole.units, "st.C");
             WriteMessage(2, &wlasne_pole, FIT_FIELD_DESCRIPTION_MESG_SIZE, fp);
    @@ -170,7 +170,7 @@
             FIT_UINT8 local_mesg_number = 5;
             FIT_DEV_FIELD_DEF devx;
             devx.def_num = 1;
    -        devx.size = 1;
    +        devx.size = sizeof(FIT_UINT16);
             devx.dev_index = 0;
     
             WriteMessageDefinitionWithDevFields(local_mesg_number, fit_mesg_defs[FIT_MESG_RECORD], FIT_RECORD_MESG_DEF_SIZE, 1, &devx, fp);

    [1/2]

  • The problems were:

    1) the developer data index was 10 in the developer data id message, but 0 in the field description message and the developer field definition within the record message definition. This value needs to be the same in all 3 places. This value uniquely identifies the developer within the following messages, so that multiple developers can write developer data.

    2) the EGT field description message was uninitialized, meaning any unspecified fields were uninitialized (either to 0 or a "random" number, depending on the compiler), instead of the proper "invalid value" default. This could lead issues such as scale and offset being 0. (scale shouldn't be 0, as the FIT decoder will divide the field value by scale, if it's specified.)

    3) in the EGT field description, the native mesg num was 0, but it needs to be 20 [MESG_NUM_RECORD] because the EGT developer field is intended to be used in record messages

    4) in the developer field definition section of the record message definition, the field size was 1, but it should be 2, to match the EGT (UINT16) field size in bytes 

    - 4) led to corruption of record data (e.g. 4 records were parsed by FIT File viewer instead of 6, and most of the data was corrupt)

    - 1) and 3) would lead to the defined EGT field not being associated with the developer data in the record messages, even if 4) was fixed

    Screenshot of working [*] output file in fitfileviewer.com:

    [*] the FIT file actually still has a few issues like invalid CRC and bad timestamps

    Full source code (test.c):

    https://pastebin.com/yMAhnn07

    To build:

    - save source code from pastebin to c/test.c under FIT SDK root

    - open terminal in FIT SDK root

    cd c
    gcc -o test test.c fit_crc.c fit.c fit_example.c -D FIT_USE_STDINT_H

    [2/2]

  • Thank you very much for your help.

    Some of these errors (like size=1) were due to chaotic error searching.

    Your explanations helped me a lot and if you are ever in Poland I invite you for a beer or other favorite drink :).