Read-only Configuration Examples

If you haven’t created a custom firmware for the OpenXC VI yet, we recommend the getting started with custom data guide.

For all examples, the name field for a message is optional but strongly encouraged to help keep track of the mapping between a message ID and something human readable.

When an example refers to “sending” a simple vehicle message or CAN message, it means sending to the app developer via one of the output interfaces (e.g. USB, Bluetooth) and not sending to the CAN bus. For examples of configuring writable messages and signals that do write back to the CAN bus, see the write configuration examples.

One Bus, One Numeric Signal

We want to read a single, numeric signal from a high speed bus on controller 1. The signal is 7 bits wide, starting from bit 5 in message ID 0x102. We want the name of the signal for OpenXC app developers to be my_openxc_measurement.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7
                }
            }
        }
    }
}

With this configuration, the VI will publish the received CAN signal using the OpenXC simple vehicle message format, e.g. when using the JSON output format:

{"name": "my_openxc_measurement", "value": 42}

Transformed Numeric Signal

We want to read the same signal as in the One Bus, One Numeric Signal example, but we want to transform the value with a factor and offset before publishing it. The value on CAN must be multiplied by -1.0 and offset by 1400.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "factor": -1.0,
                    "offset": 1400
                }
            }
        }
    }
}

We added the factor and offset attributes to the signal.

One Bus, One Boolean Signal

We want to read a boolean signal from a high speed bus on controller 1. The signal is 1 bits wide, starting from bit 32 in message ID 0x103. We want the name of the signal for OpenXC app developers to be my_boolean_measurement. Because it is a boolean type, the value will appear as true or false in the JSON for app developers.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x103": {
            "bus": "hs",
            "signals": {
                "My_Boolean_Signal": {
                    "generic_name": "my_boolean_measurement",
                    "bit_position": 32,
                    "bit_size": 1,
                    "decoder": "booleanDecoder"
                }
            }
        }
    }
}

We set the decoder for the signal to the booleanDecoder, one of the built-in signal decoders - this will transform the numeric value from the bus (a 0 or 1) into first-class boolean values (true or false).

With this configuration, the VI will publish the received CAN signal using the OpenXC simple vehicle message format, e.g. when using the JSON output format:

{"name": "my_boolean_measurement", "value": true}

One Bus, One State-based Signal

We want to read a signal from a high speed bus on controller 1 that has numeric values corresponding to a set of states - what we call a state-based signal

The signal is 3 bits wide, starting from bit 28 in message ID 0x104. We want the name of the signal for OpenXC app developers to be active_state. There are 6 valid states from 0-5, and we want those to appears as the state strings a through f in the JSON for app developers.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x104": {
            "bus": "hs",
            "signals": {
                "My_State_Signal": {
                    "generic_name": "active_state",
                    "bit_position": 28,
                    "bit_size": 3,
                    "states": {
                        "a": [0],
                        "b": [1],
                        "c": [2],
                        "d": [3],
                        "e": [4],
                        "f": [5]
                    }
                }
            }
        }
    }
}

We set the states field for the signal to a JSON object, mapping the string value for each state to the numerical values to which it corresponds. This automatically will set the decoder to the stateDecoder, one of the built-in signal decoder functions.

With this configuration, the VI will publish the received CAN signal using the OpenXC simple vehicle message format, e.g. when using the JSON output format:

{"name": "active_state", "value": "a"}

Combined State-based Signal

We want to read the same state-based signal from One Bus, One State-based Signal but we want the values 0-3 on the bus to all correspond with state a and values 4-5 to the string state b.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x104": {
            "bus": "hs",
            "signals": {
                "My_State_Signal": {
                    "generic_name": "active_state",
                    "bit_position": 28,
                    "bit_size": 3,
                    "states": {
                        "a": [0, 1, 2, 3],
                        "b": [4, 5]
                    }
                }
            }
        }
    }
}

Each state string maps to an array - this can seem unnecessary when you only have 1 numeric value for each state, but it allows combined mappings as in this example.

Two Buses, Two Signals

We want to read two numeric signals - one from a message on a high speed bus on controller 1, and the other from a message on a medium speed bus on controller 2.

The signal on the high speed bus is 12 bits wide, starting from bit 11 in message ID 0x108. We want the name of the signal for OpenXC app developers to be my_first_measurement.

The signal on the medium speed bus 14 bits wide, starting from bit 0 in message ID 0x90. We want the name of the signal for OpenXC app developers to be my_second_measurement.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        },
        "ms": {
            "controller": 2,
            "speed": 125000
        }
    },
    "messages": {
        "0x108": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_first_measurement",
                    "bit_position": 11,
                    "bit_size": 12
                }
            }
        },
        "0x90": {
            "bus": "ms",
            "signals": {
                "My_Other_Signal": {
                    "generic_name": "my_second_measurement",
                    "bit_position": 0,
                    "bit_size": 14
                }
            }
        }
    }
}

We added the second bus to the buses field and assigned it to controller 2. We added the second message object and made sure to set its bus field to ms.

With this configuration, the VI will publish the received CAN signal using the OpenXC simple vehicle message format, e.g. when using the JSON output format:

{"name": "my_first_measurement", "value": 42}
{"name": "my_second", "value": 942}

Limited Simple Vehicle Message Signal Rate

We want to read the same signal as in the One Bus, One Numeric Signal example, but we want it to be sent at a maximum of 5Hz. We want the firmware to pick out messages at a regular period, but we don’t care which data is dropped in order to stay under the maximum.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "max_frequency": 5
                }
            }
        }
    }
}

We set the max_frequency field of the signal to 5 (meaning 5Hz) - the firmware will automatically handle skipping messages to stay below this limit.

Limited Simple Message Rate if Unchanged

We want the same signal from Limited Simple Vehicle Message Signal Rate at a limited rate, but we don’t want to lose any information - if the value of the signal changes, we want it to be sent regardless of the max frequency. Repeated, duplicate signal values are fairly common in vehicles, where a signal is sent at a steady frequency even if the value hasn’t changed. For this example, we want to preserve all information - if a signal changes, we want to make sure the data is sent.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "max_frequency": 5,
                    "force_send_changed": true
                }
            }
        }
    }
}

We added the force_send_changed field to the signal, which will make sure the signal is sent immediately when the value changes. This rate limiting is lossless.

Send Signal on Change Only

We want to limit the rate of a signal as in Limited Simple Message Rate if Unchanged, but we want to be more strict - the signal should only be published if it actually changes.

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "send_same": false
                }
            }
        }
    }
}

We accomplish this by setting the send_same field to false. This is most appropriate for boolean and state-based signals where the transition is most important. Considering that a host device may connect to the VI after the message has been sent, using this field has the potential of making it difficult to tell the current state of the vehicle on startup - you have to wait for a state change before knowing any values. For that reason, we’ve moved away from using this for most firmware (using a combination of a max_frequency of 1Hz and force_send_changed == true) but the option is still available.

Separate Files for Message Sets

Starting from the Two Buses, Two Signals example, we want to split up the configuration into mutiple files because it’s getting too big and hard to follow. This will especially be true as we add more message and signals.

Starting from this complete configuration:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        },
        "ms": {
            "controller": 2,
            "speed": 125000
        }
    },
    "messages": {
        "0x108": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_first_measurement",
                    "bit_position": 11,
                    "bit_size": 12
                }
            }
        },
        "0x90": {
            "bus": "ms",
            "signals": {
                "My_Other_Signal": {
                    "generic_name": "my_second_measurement",
                    "bit_position": 0,
                    "bit_size": 14
                }
            }
        }
    }
}

we move the messages that we want to read from the hs bus to the file hs.json:

{
    "messages": {
        "0x108": {
            "signals": {
                "My_Signal": {
                    "generic_name": "my_first_measurement",
                    "bit_position": 11,
                    "bit_size": 12
                }
            }
        }
    }
}

and we move the messages that we want to read from the ms bus to the file ms.json:

{
    "messages": {
        "0x90": {
            "signals": {
                "My_Other_Signal": {
                    "generic_name": "my_second_measurement",
                    "bit_position": 0,
                    "bit_size": 14
                }
            }
        }
    }
}

Notice in both of these files, the messages no longer have the bus attribute - we’re instead going to specify that in the top level configuration:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        },
        "ms": {
            "controller": 2,
            "speed": 125000
        }
    },
    "mappings": [
        {"mapping": "hs.json", "bus": "hs"},
        {"mapping": "ms.json", "bus": "ms"}
    ]
}

The primary advantage of using separate files is readability, but it also makes the message definitions more re-usable between vehicle platforms and buses. For example, we could quickly parse all of the messages from the ms.json mapping file from the hs bus instead of ms by flipping the bus attribute in the top-level config file.

Mapped from a DBC File

If you use Vector DBC files to store your “gold standard” CAN signal definitions, you can save some effort by exporting the DBC to an XML file and merging it with your VI configuration file. You won’t need to manually copy the bit_position, bit_size, factor and offset attributes.

If we are to implement One Bus, One Numeric Signal manually, we would use this configuration file:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "messages": {
        "0x102": {
            "bus": "hs",
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "factor": -1.0,
                    "offset": 1400
                }
            }
        }
    }
}

If the message and signal is defined in a DBC file, we can save some effort. Using a program like Vector CANdb++, export the DBC file to XML. Place the XML file in the same directory as your JSON configuration file. We need to first split up the configuration into a mapped messages file and a top-level config, as in Separate Files for Message Sets example.

In our config.json:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "mappings": [
         {"mapping": "hs.json", "bus": "hs"}
    ]
}

and in hs.json:

{
    "messages": {
        "0x102": {
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement",
                    "bit_position": 5,
                    "bit_size": 7,
                    "factor": -1.0,
                    "offset": 1400
                }
            }
        }
    }
}

Now that we have the DBC exported to an XML file (we’ll assume it’s named exported-hs.xml), we can remove the bit_position, bit_size, factor and offset fields and let them be imported from the XML - the only thing required is a generic_name:

In our config.json:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        }
    },
    "mappings": [
         {"mapping": "hs.json",
            "bus": "hs",
            "database": "exported-hs.xml"}
    ]
}

and in hs.json:

{
    "messages": {
        "0x102": {
            "signals": {
                "My_Signal": {
                    "generic_name": "my_openxc_measurement"
                }
            }
        }
    }
}

It’s not huge savings for 1 signal, but once you get a dozen it can save a lot of effort and opportunities for bugs.

Same Message ID, Two Buses

One shortcoming of a single configuration file is that you can’t define a CAN message with the same ID to exist on two different, buses. For example, this isn’t value JSON because the 0x100 key is repeated:

{
    "messages": {
        "0x100": {
            "bus": "hs"
         },
         "0x100": {
            "bus": "ms"
         }
    }
}

Instead, you can use mappings files as in Separate Files for Message Sets and put the messages for each bus in separate files. Here’s the main configuration file:

{   "buses": {
        "hs": {
            "controller": 1,
            "speed": 500000
        },
        "ms": {
            "controller": 2,
            "speed": 125000
        }
    },
    "mappings": [
        {"mapping": "hs.json", "bus": "hs"},
        {"mapping": "ms.json", "bus": "ms"}
    ]
}

and here’s hs.json:

{
    "messages": {
        "0x100": {
             "My_Signal": {
                 "generic_name": "my_first_measurement",
                 "bit_position": 3,
                 "bit_size": 7
             }
        }
    }
}

and finally, ms.json:

{
    "messages": {
        "0x100": {
            "signals": {
                "My_Other_Signal": {
                    "generic_name": "my_second_measurement",
                    "bit_position": 0,
                    "bit_size": 14
                }
            }
        }
    }
}

The two different CAN messages with the same ID can co-exist in these separate files, linked as mappings through the main config.