Provides a simple converter API in C for the complete J2735 (2024) specification, with libraries and command-line tools compiled for multiple platforms to convert messages between UPER, JER, and XER encodings. Includes:
- A WebAssembly library that can run in any browser, and a Node.js command line app that can run on any platform that supports Node.js.
- A demo website that uses the WebAssembly library.
- A native CLI tool and library for Windows 11.
- A native CLI tool and library for Linux.
/- Top level contains CMake build files, Dockerfiles, and build scripts/src- A simple converter API written in C, with CLI interface and conversion functions./generated- A C codec for the SAE J2735 (2024) V2X message set generated from the ASN.1 specification by the open source asn1c compiler./custom- Edited overrides for the generated files./build- Executable filesv2x.wasm- The WebAssembly binary.v2x.js- Javascript wrapper for the WebAssembly binary.app.js- A Node.js command line app that uses the WebAssembly library.convert-v2x.exe- Command line tool for Windows.v2x.dll- Shared library for Windows.convert-v2x, - Command line tool for Linux.libv2x.so.1.0.0- Shared library for Linux.
/site- Demo website that uses the WebAssembly library.
Try the live demo here:
https://salmon-grass-077f66e1e.6.azurestaticapps.net/
The converter tool here also uses the WebAssembly library:
https://cvtools.neaeraconsulting.com/j2735-codec
The WebAssembly app can be run on the command line using node.js. This should work on any platform that supports Node. Tested in Windows 11 Powershell and Ubuntu WSL.
Prerequisites:
cd build
npm install
# Read SPAT hex from stdin
cat ../examples/spat.hex | npx run-func app.js main "uper" "xer" "MessageFrame"
# Pass SSM hex as argument
npx run-func app.js convert "uper" "jer" "MessageFrame" "001e1562b3aec8bf060000629008125881c628047a004140"
# Convert SSM XML to UPER hex
npx run-func app.js convert "xer" "uper" "MessageFrame" "<MessageFrame><messageId>30</messageId><value><SignalStatusMessage><timeStamp>177070</timeStamp><second>51391</second><sequenceNumber>3</sequenceNumber><status><SignalStatus><sequenceNumber>0</sequenceNumber><id><id>6308</id></id><sigStatus><SignalStatusPackage><requester><id><entityID>9620718A</entityID></id><request>1</request><sequenceNumber>15</sequenceNumber><role><transit/></role></requester><inboundOn><lane>16</lane></inboundOn><status><rejected/></status></SignalStatusPackage></sigStatus></SignalStatus></status></SignalStatusMessage></value></MessageFrame>"Requires Linux.
cd build
cat ../examples/spat.hex | ./convert-v2x uper jer MessageFrameRequires Windows.
Use Powershell
cd build
cat ..\examples\spat.hex | .\convert-v2x.exe uper jer MessageFrameTo run the demo locally, clone this repository, then run /site in a web server. Opening the index.html file directly in a browser does not work. Instead, run the static site locally using an IDE or in nginx using Docker.
To run the pre-compiled static website using an IDE:
- From Jetbrains CLion or WebStorm, right-click on
site/index.htmland select "Run". - From VScode, right-click on
site/index.html, and select "Open with live server"
Or use Docker to deploy in nginx:
Prerequisites:
docker compose up --build -dand point a browser to:
http://localhost:8789/index.html
The WebAssembly and native libraries present the same API. They allow JavaScript in the browser and native C code to call the same functions to convert J2735 messages between UPER, XER, and JER encodings.
The API functions are:
function
size_t convert_str(
const char *pdu_name,
const char *from_encoding,
const char *to_encoding,
const char *ibuf,
char *buf,
size_t max_buf_len);Converts a string representation of a J2735 PDU from one encoding to another.
Arguments:
pdu_name
: String with the J2375 PDU, e.g., "MessageFrame", "BasicSafetyMessage".
from_encoding
: String with the name of the encoding of the input ("JER", "XER", or "UPER").
to_encoding
: Target encoding for the output string ("JER", "XER", or "UPER").
ibuf
: The input string in UPER hex, XER, or JER format.
buf
: (output) The buffer to store the converted output string.
max_buf_len
: The maximum length of the output buffer.
Returns: : The length of the converted output string.
size_t convert_bytes(
const char * pdu_name,
const char * from_encoding,
const char * to_encoding,
const uint8_t * ibuf,
size_t ibuf_len,
uint8_t * obuf,
size_t max_obuf_len);Arguments:
Convert a byte array representation of a J2735 PDU from one encoding to another. UPER format is rww bytes, not hex. JER and XER are 8-bit text.
pdu_name
: String with the J2375 PDU, e.g., "MessageFrame", "BasicSafetyMessage".
from_encoding
: String with the name of the encoding of the input ("JER", "XER", or "UPER").
to_encoding
: Target encoding for the output ("JER", "XER", or "UPER").
ibuf
: The input byte array in raw UPER, or XER or JER text format.
ibuf_len
: The length of the input byte array.
obuf
: The buffer to store the output byte array.
max_obuf_len
: The maximum length of the output buffer.
Returns: : The length of the converted output byte array.
int main(int ac, char *av[]) {
char *line = NULL;
size_t size;
if (getline(&line, &size, stdin) == -1) {
printf("Error reading stdin, no line received.\n");
}
const size_t out_buf_size = 0xFFFFu;
char * out_buf = malloc(out_buf_size);
convert_str(av[3], av[1], av[2], line, out_buf, out_buf_size);
printf("%s\n", out_buf);
free(out_buf);
}function convert(fromValue, toValue, pduValue, inputValue) {
try {
const pduPtr = Module.stringToNewUTF8(pduValue);
const hexPtr = Module.stringToNewUTF8(inputValue);
const toEncPtr = Module.stringToNewUTF8(toValue);
const fromEncPtr = Module.stringToNewUTF8(fromValue);
const bufSize = 65536;
const bufPtr = Module._malloc(bufSize);
Module._memset(bufPtr, 0, bufSize);
const encSize = Module._convert_str(pduPtr, fromEncPtr, toEncPtr, hexPtr, bufPtr, bufSize);
const resultValue = Module.UTF8ToString(bufPtr);
Module._free(pduPtr);
Module._free(hexPtr);
Module._free(toEncPtr);
Module._free(fromEncPtr);
Module._free(bufPtr);
console.log("Converted to " + encSize + " bytes of " + toValue?.toUpperCase());
return resultValue;
} catch (e) {
console.error(e);
return '';
}
}The CLI tool and WebAssembly library can be re-built using the Dockerfile and scripts or manually as described below.
- Docker
docker compose -f docker-compose-wasm.yml up --build -dAllow the container to run and execute the run script. Eventually it copies the executable files to the build volume and leaves the container running.
Run the following command from the host to copy files built by the container from the shared volume to the site directory (Don't copy the generated html file, because the site directory contains customized html.)
cp build/v2x.js build/v2x.wasm site- WSL (Ubuntu) command line
- Python
- CMake
- Git
- Emscripten
To build a command line tool.
The emscripten build is fairly slow in WSL, so it is useful build a normal executable CLI with the clang compiler to check for any issues first.
sudo apt install clang
sudo apt install cmakeexport CC=/usr/bin/clang
cmake .
cmake --build .cmake .
cmake --build . --verbosecmake --build . --target cleancat examples/spat.hex | ./convert-v2x uper jer MessageFrameTo re-create the WebAssembly file and associated Javascript file:
Follow instructions: https://emscripten.org/docs/getting_started/downloads.html
# Get the emsdk repo
git clone https://github.com/emscripten-core/emsdk.git
# Enter that directory
cd emsdk
# Fetch the latest version of the emsdk (not needed the first time you clone)
git pull
# Download and install the latest SDK tools.
./emsdk install latest
# Make the "latest" SDK "active" for the current user. (writes .emscripten file)
./emsdk activate latest
# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.shFrom the root directory of the project:
# Clean
cmake --build . --target clean
# and Manually delete folder "CMakeFiles", and files cmake_install.cmake, CMakeCache.txt, and Makefile
# Configure with Emscripten toolchain
# This is an example. Substitute the actual path to Emscripten.cmake on your machine.
cmake -DCMAKE_TOOLCHAIN_FILE=/mnt/c/Users/ivan/asn1/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" .
# Make verbosely. Verify it uses emcc as compiler
# This step is pretty slow, takes a couple hours to complete.
make VERBOSE=1
# Compile to wasm, js and html
#
# Module options:
# Use -sMODULARIZE to generate a modularized Javascript wrapper.
# Use -sEXPORT_ES6 to use ES module system instead of the default commonJS modules
# Use -sMINIFY_HTML=0 to make HTML readable
# JS module ref: https://emscripten.org/docs/compiling/Modularized-Output.html
emcc libv2x.a -o v2x.html -O2 -Wno-unused-main -sMINIFY_HTML=0 -sMODULARIZE=1 -sEXPORT_ES6=1 -sEXPORTED_FUNCTIONS=_convert_str,_malloc,_free,_memset -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,stringToNewUTF8,UTF8ToString
# Copy built files to site directory
# (Don't copy the generated html file, because the site directory contains customized html.)
cp v2x.js v2x.wasm site
The Windows exe and dll were built using the same source code Visual Studio 2022 with the Clang toolset and CMake, with the settings file CMakeSettings.json.
The JER dialect implemented by the asn1c codec requires that elements of a SEQUENCE be provided in the same order as the tags in the ASN.1 specification. This is a limitation of the existing open source asn1c compiler, but is not required by the JER standard, which follows the JSON standard and allows elements to be in any order.
The simple C API header in src/convert.h will facilitate integration with the Java language using the Foreign Function and Memory (FFM) API using the fextract tool to generate Java bindings. This is planned for a future release.
We welcome contributions to this open source project! Whether you want to report bugs, suggest new features, improve documentation, or submit code, your input is appreciated.
To report an issue:
- First check this README and all other documentation in this repo, and existing issues, to see if your problem or suggestion has already been addressed.
- Create a new issue here: https://github.com/neaeraconsulting/multiplatform-v2x-codecs/issues
- Provide a clear and concise description of the issue or feature request, including details of the development environment, platform, operating system, or browser used, and list specific and detailed steps to reproduce the issue.
To contribute:
- Fork the repository and create a new branch for your changes.
- Make your changes, following the existing code style.
- Submit a pull request with a clear description of your changes and the problem they solve.
Please ensure your contributions comply with the project's license and include appropriate tests or examples where applicable.
Thank you for helping improve this project!
All code in this repository is licensed under the Apache 2.0 License, except for skeleton files in the /generated directory from the asn1c project with header comments indicating the BSD license.
Copyright © 2025 Neaera Consulting, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright (c) 2003-2017 Lev Walkin vlm@lionet.info and contributors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.