ghostdashboard / skins

SDK and sample code for creating skins for the Ghost Dashboard.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Ghost Dashboard Skins SDK

Welcome to the Ghost Dashboard Skins GitHub repository! This platform is dedicated to those passionate about automotive customization and software development. We aim to provide you with the tools and knowledge to create stunning, personalized skins for the Ghost Dashboard using web technologies.

⚠ IMPORTANT

  • The content of this repository is intended to be used with the Ghost Dashboard product only.
  • No other use is allowed without the author's express written permission.
  • No other application will be compatible with the content of this repository.

🎨 Customization at Your Fingertips

We focus on enabling you to craft custom skins that bring your unique vision to life. With the Ghost Dashboard as your canvas, you can design a personalized interface that reflects your style and enhances your driving experience.

This is why we have built a powerful engine that allows you to create skins using the same technologies that power the web:

  • HTML: Use this simple yet powerful markup language to structure your dashboard skin.
  • CSS: Get creative with styles and animations to give your dashboard a unique flair.
  • JavaScript: Add interactivity and dynamic content to your skins with robust scripting options.
  • Images & Vectors: From icons to full-scale backgrounds, integrate visuals that resonate with your style.
  • Multimedia Content: Embed various media types to create a truly multimedia experience on your dashboard.

By combining these technologies, we can compile the UI into a lightweight package that can be easily deployed to the Ghost Dashboard's hardware. That means you can focus on creating your vision without worrying about the technical details.

πŸ› οΈ Resources for Easy Development

We've stocked our repositories with resources to make skin development as intuitive as possible:

  • Documentation: Comprehensive guides and references to help you start and advance your skin creation journey.
  • Examples: Dive into sample skins to see the possibilities and get inspiration for your own designs.
  • Tools: Utilities and scripts to streamline the development and testing of your skins.

Development

πŸ”₯ Performance and Hardware Considerations

When developing skins for the Ghost Dashboard, it's crucial to consider the performance implications and hardware limitations to ensure a seamless and responsive user experience.

The hardware is equipped with a quad-core ARM processor running at 1.44GHz and 512MB of RAM dedicated to the UI, with video memory managed automatically.

The canvas size is 1280x480. While a responsive layout isn't necessary, scalable skins are recommended to perform well across different display specifications.

The rendering resolution is automatically scaled to 1x, 1.5x, or 2x, depending on the hardware's screen resolution.

⚑ Best Practices and Performance Tweaks

1. Understand Mobile Development Paradigms:

  • Threading and Events: A solid grasp of threads, queues, and event handling in JavaScript is vital. Optimize event handling and minimize blocking operations to maintain UI responsiveness.

2. Optimize Animations and Graphics:

  • Harware-accelerated CSS3 and WebGL: Utilize these for creating animations, layouts, typography, images, masks, and clipped paths.
  • Use will-change Sparingly: While will-change in CSS can boost performance, it's computationally intensive. Use it judiciously to optimize for specific properties only when necessary.
  • Compress Images and Vectors: Use tools like TinyPNG for PNG compression and Vecta Nano for vector compression to reduce memory footprint.
  • Efficient Video Use: Embed low-resolution videos (e.g., 1000x300 30fps for a banner intro animation or a 480x480 30fps duplicated twice for a gauge intro animation) to conserve resources. Remember that low resolution is different from low quality/definition. Use high-quality videos with low resolution to ensure a crisp image.
  • Prioritize Real-time Responsiveness: Focus on rendering the most recent data quickly. This is key in real-time UIs.
  • Use requestAnimationFrame: This native method is ideal for triggering UI updates, aiming for a smooth 60fps performance.
  • Avoid Heavy JavaScript Operations: Steer clear of intensive JS tasks like complex calculations, recursive loops, and extensive data transformations. If you must use them, consider using Web Workers to offload them to a separate thread.

3. Leverage Optimized Library Functions:

  • Utilize Provided Libraries: The devkit includes a library of optimized functions. Using these can significantly improve performance and reduce the need for custom code.

4. General Optimization Techniques:

  • Minimize DOM Access: Accessing the DOM can be expensive. Reduce direct DOM manipulations and batch updates when possible.
  • Efficient CSS Selectors: Use simple selectors and avoid deeply nested or overly complex selectors.
  • Memory Management: Be mindful of memory usage. Clean up objects and detach event listeners when no longer needed to prevent memory leaks.
  • Check this Gist: DOM Performance

5. Testing and Profiling:

  • Regular Performance Testing: Continuously test your skin's performance, especially after adding new features or making significant changes.
  • Profiling Tools: Use browser profiling tools to identify bottlenecks and optimize areas where the skin is underperforming.

6. Sessions and Storage:

  • Sessions: If you need to use a session to store data, you can use the sessionStorage object. It's a key/value store that persists until the user switches to another skin or turns off the Ghost Dashboard. It's important to note that the sessionStorage object is shared between all skins, so you should use a unique key to avoid conflicts with other skins.
  • Storage: If you need to store data permanently, use the localStorage object. It's a key/value store that persists even after the user switches to another skin or turns off the Ghost Dashboard. It's important to note that the localStorage object is shared between all skins, so you should use a unique key to avoid conflicts with other skins.

πŸš€ Getting Started

  • Clone the repo
  • Make sure you have Node.js installed
  • Run npm run serve to start the development server
  • If asked, allow the installation of the serve package
  • Open the browser and navigate to http://localhost:3210
  • We recommend using the Mozilla Firefox browser for development

πŸ›  Building your skin - Please read carefully

Once the development server is running, you can start building your skin.

  • To start, duplicate the base folder and rename it to your skin's name. Then, move it inside the community folder and start your development there.
    • Use simple names for your skin's folder. e.g. duck, space-drift, cookies-n-cream, etc.
    • Be careful not to pick a name already used by another skin.
    • You can use the available files as the base skeleton for your development. It contains the basic structure to render the UI and retrieve real-time data from the dashboard's hardware.
    • Check the community folder for examples and sample files on how to build your skin.
    • Create a README.md file inside your skin's folder to describe your skin's name (the one the user will see on the selection list), features, inspiration and anything else you want to share with the community.
    • Provide some screenshots of your skin in action.
    • Read the section "Load your skin (dev)" below for more information on how to load your skin.
  • Once you are happy with your final product, you can open a PR (Pull Request) against the main branch and wait for an admin to review it.
  • Your skin will be tested on a real Ghost Dashboard hardware to ensure it works as expected.
  • Once approved, your skin will be available in the next software update release.

πŸ“š Documentation

⚑ Using the built-in tools

When you first navigate to http://localhost:3210, the devkit will load the base skin.

βšͺ Load your skin (dev)

To load the skin you are working on, you can use the Load dev skin button in the control panel.
The SDK will ask you to input the name you gave to your skin's folder. Once you do that, the SDK will load your skin and save your choice in the browser's local storage.
The next time you click the Load dev skin button, the SDK will automatically load your skin without asking you again.
If your skin does not load, ensure you have entered the correct name and that your skin's folder is inside the community folder.
To reset the saved skin, you can use the Reset settings button in the control panel.

Note

To debug Javascript and network errors, use the browser's developer tools console (F12).

βšͺ Emulate hardware connection

To emulate a hardware connection, you can use the Server connection toggle in the control panel.
This will toggle the connected class on the container element, which you can use to style and animate your skin accordingly.
Use this flag to set your skin in a "connected" state, meaning the UI successfully connects to the hardware.

βšͺ Emulate Real-time data

You can use the Real-time data toggle in the control panel to emulate hardware data.
This will trigger a simulated data stream that you can use to test your skin's UI.
Make sure to test your skin thoroughly with different combinations of settings, since they can change the behaviour of the UI.

βšͺ Emulate animations

To emulate animations, you can use the Animation IN and Animation OUT buttons in the control panel.
These buttons will toggle the anim-in and anim-out classes on the container element, which you can use to style and animate your skin accordingly.
When the skin is loaded, the anim-in class is automatically added to the container element. This is programatically called as the last step of the skin's loading process - usually the classList.add('anim-in') method is present at the end of the callback function called by window.onload, which gives you flexibility to delay the animation until the skin is fully loaded (if needed).

βšͺ Load example skins

To load an example skin, you can use the Load '<skin name>' example button in the control panel.

βšͺ Load base skin

To load the base skin, you can use the Load base skin button in the control panel.

βšͺ Changing colours

To change the skin's colours, you can use the Change colors button in the control panel.
This will open a modal window where you can change the skin's colours.
The modal will show a list of all the colours available for the end user to customize when using your skin.
Each button indicates the name of the colour it will change, and the current colour value.
Remember to hit the Save button to save your changes.

βšͺ Changing settings

To change the skin's settings, you can use the Change settings button in the control panel.
This will open a modal window where you can change the skin's settings.
The modal will show a list with all the settings available for the end user to customize when using the dashboard.
Each combination of settings will trigger a different behaviour in the skin's UI, so make sure to test your skin thoroughly with different combinations of settings.
Remember to hit the Save button to save your changes.

βšͺ Reset settings

You can use the Reset settings button in the control panel to reset the skin's settings.
This will reset all the settings to their default values, including the skin name you provided when you first loaded it.


⚑ Available settings

Customization is a key feature of the Ghost Dashboard, and we want to give you the tools to create skins that can be personalized by the end user.

You can access the settings object by using the DASH_OPTIONS global variable, which is available on the global scope and can be accessed anywhere in your script.

The defaultSettings.js file contains the default values for all the settings and should not be changed since it's automatically generated by the SDK, but can be used as a reference.

The list below contains all the settings available for the end user to customize when using your skin.
Each setting has a unique ID (Object Key) that you can use to identify it and apply the changes to your skin's UI.

βšͺ General settings

Key Description Type Default
rpmM Limiter for RPM gauge (x1000) number 8
sLigt Shift light trigger number 5250
redline Redline trigger number 5500
icon Icon style - 1: mono, 2: color number 1
clt Coolant temperature gauge max value number 110
mat Manifold air temperature gauge max value number 110
pOil Oil pressure gauge max value number 10
tOil Oil temperature gauge max value number 120
pFuel Fuel pressure gauge max value number 6
pBoost Boost pressure gauge max value number 2
tKm Base total km to mimic the car's OEM odometer number 0
sCan Source for CAN data - 1: Enabled, 2: Disabled number 1
aSpd Speed algorithm - 1: Higher accuracy, 2: Higher refresh rate number 1
sRpm Source for RPM data - 1: Internal module (Basic), 2: CAN number 2
sVss Source for Speed data - 1: Internal module (Basic), 2: CAN number 1
sClt Source for Coolant Temperature data - 1: Internal module (Basic), 2: CAN number 2
batt Battery voltage gauge max value number 20
spdM Speedometer max value number 300
theme Skin settings - check theme object: below object -

Note

Note that the user can select the maximum scale for the RPM gauge, from 6 to 10, which means your skin should support it.
If you are using an image for the RPM gauge, you will need to provide five different files to support the different scales.
Check the simple skin for an example on how to implement this feature.


theme object:

Key Description Type Default
colors Color settings - check table below object -
active Active skin string base

colors object:

Key Description Type Default
cMain Main color string #00d3ff
cSec Secondary color string #e43f01
cRed Redline color string #b70000
cBg Background color string #000000
cRpm RPM gauge color string #00d3ff
const { rpmM, theme } = DASH_OPTIONS;
const { cMain, cRpm } = theme.colors;
const maxRPM = rpmM * 1000;
// ...
setRootCSS('--main-color', cMain)
setRootCSS('--rpm-color', cRpm)

const updateRPM = (rpm) => {
   setRootCSS('--rpm-deg', `${(294 - ((+rpm / maxRpm) * 294))}deg`)
}

⚑ Available libraries

βšͺ client.js

This is the main library that you will use to build your skin.
It contains all the functions you need to interact with the dashboard's hardware and real-time data.
The library is available in the assets folder and is already imported in the index.html file.
Since we use a local static concept, the order of importing the scripts in the index.html file is critical and should follow the order below:

<script src="../assets/defaultSettings.js" type="text/javascript"></script> <!-- default settings -->
<script src="../assets/client.js" type="text/javascript"></script> <!-- client library -->
<script src="./script.js" type="text/javascript"></script> <!-- skin's script -->

Note

The SDK automatically generates the defaultSettings.js and client.js files and contains the default settings and methods for the skins, respectively. Its content should not be changed.
Since the client.js file is imported directly inside the HTML file, every method will be available on the global scope and can be accessed anywhere in the subsequent scripts.


⏺ Available methods:

openConnection(callback: () => void): void

Start the connection to the server and calls callback after a successful connection. This method must be called programmatically right after the intro animation is finished.

This is a void method and doesn't return anything.
Once the connection is established, the connected class is automatically added to the container element, and both basicData and canData objects will start to be hydrated with real-time data.

Every time you need to read the most recent bits from the data streams, you can access the basicData and canData objects, which are available on the global scope and can be accessed anywhere in your script.

callback: A function to be called after a successful connection to the server.

openConnection(() => bindRealtimeData());

checkCache(element: HTMLElement | string, value: string): boolean

Checks if the given element has the given value cached. If it does, returns true and does nothing. If it doesn't, returns false and caches the value.

element: The element to check the cache of. If a HTMLElement is passed, its id will be used as key to check the cache. If a string is passed, it will be used as the key to check the cache.

value: The value to check the cache for.

const rpm = document.getElementById('rpm');
const rpmValue = 120;
if (!checkCache(rpm, rpmValue)) {
  // do something
}

setText(element: HTMLElement, text: string | number): void

Sets the text of the given element to the given text. This is a high performance method that relies on fast caching and should be used to set the text of elements that are updated frequently - like speedometer, RPM number, temperatures, etc.

element: The element to set the text of.

text: The text to set.

const speed = document.getElementById('speed');
setText(speed, canData.vss);

setRootCSS(prop: string, value: string | number): void

Sets the value of the given CSS property on the root element of the page. Also backed by fast caching and should be used to set the value of CSS properties that are updated frequently - like colours, bars, element position/rotation/scale, etc.

prop: The CSS property to set/update.

value: The value to set.

const { cRpm } = DASH_OPTIONS.theme.colors;
setRootCSS('--gauge-background-color', cRpm);

setGaugeValue(element: HTMLElement, value: string | number, barValue: string | number): void

Combines setText and setRootCSS to set the text of the given element and the value of the bar related to it. Useful when using dynamic gauges.

Note that the element will have its text set to the given value, and barValue will be set as a CSS property on the root element of the page with the name --<element-id>-bar. Ensure that the --<element-id>-bar property is set on the CSS file with default values for a better experience.

element: The element to set the text of.

value: The text to set.

barValue: The value for the bar.

const speed = document.getElementById('speed');
setGaugeValue(speed, 120, 0.6); // sets the text to 120 and the bar value to 0.6
#speed-bar {
  width: 300px;
  transform: scaleX(var(--speed-bar));
}

setIconOpacity(icon: string, value: string | number): void

Sets the opacity of the given icon. Useful to "turn on" the signal icons like turn, high beam, e-brake, alternator, engine light, etc. This method is a wrapper around setRootCSS.

icon: The name of the icon - the root property will be set as --<icon-name>-icon.

value: The state of the signal coming from the Basic data set.

setIconOpacity('turnLeft', 1); // turns on the turn signal icon
setIconOpacity('highBeam', 0); // turns off the high beam icon

zeroFixed(value: string | number): string

Returns the given value as a number with no decimal places. Useful to format numbers like speedometer, RPM, etc.

Fractional values will be rounded to the nearest integer.

value: The value to format.

zeroFixed(120.5); // output: 121
zeroFixed(1.75); // output: 2

oneFixed(value: string | number): string

Returns the given value as a number with one decimal place. Useful to format numbers like TPS, battery, temperatures, etc.

value: The value to format.

oneFixed(12.34); // output: 12.3
oneFixed(1.75); // output: 1.8

twoFixed(value: string | number): string

Returns the given value as a number with two decimal places. Useful to format numbers like map, boost, etc.

value: The value to format.

twoFixed(12.3456); // output: 12.35
twoFixed(1.765); // output: 1.77

safeReturn(object: any, key: string, defaultValue: string | number): string | number

Returns the value of the given key from the given object. If the key doesn't exist, it returns the default value.

Useful to avoid errors when the data set is not complete (e.g. first batch of real-time data).

object: The object to get the value from.

key: The key to get the value of.

defaultValue: The default value to return if the key doesn't exist.

safeReturn(data, 'tps', 0); // returns the value of data.tps or 0 if it doesn't exist

mapFormat(value: string | number): string

Formats the MAP value to a more readable format, considering 1 BAR (100 KPA) as the relative start point for both vacuum and boost. It adds a negative sign to indicate vacuum values.

Only works with values in BAR. e.g. 0.2, 0.35, 1.2, 10.3, etc.

value: The value to format.

mapFormat(0.2); // output: -.2 (vacuum)
mapFormat(1.2); // output: 0.2 (boost)

boostFormat(value: string | number): string

Formats the boost value to a more readable format, considering 1 BAR (100 KPA) as the relative start point. Any value below 1 BAR is considered vacuum and will return zero.

It only works with values in BAR. e.g. 0.2, 0.35, 1.2, 10.3, etc.

value: The value to format.

boostFormat(0.2); // output: 0
boostFormat(1.2); // output: 0.2

mapBarFormat(value: string | number): string

Returns the value to be used when representing the MAP and Boost values in the same bar.

The output is always a fraction of the input for both negative and positive values, so it can be used to scale (scaleX or scaleY) the bar on both sides of the zero point.

It varies from -0.5 to 0 for vacuum and from 0 to 0.5 for boost. The "max boost pressure" value the user sets is used automatically and will represent 0.5 as the maximum positive output.

To be used with the value coming from mapbar property within the CAN data set.

Check the CAN data set section below for more information. Only works when the CAN data set is available.

value: The value to format.

setRootCSS('--map-gauge-bar', mapBarFormat(0.4)); // input: 0.4 BAR -> output: 0.1 considering 2 BAR as the max boost pressure
setRootCSS('--map-gauge-bar', mapBarFormat(1.7)); // input: 1.7 BAR -> output: 0.284 considering 3 BAR as the max boost pressure
setRootCSS('--map-gauge-bar', mapBarFormat(-0.4)); // input: -0.4 BAR -> output: -0.2 (boost is ignored)
#map-gauge-bar {
  width: 300px;
  left: 50%;
  transform: scaleX(var(--map-gauge-bar));
}

set__channel__Bar(value: string | number, asPercentage: boolean = false): void

Shorthand methods to set the value of the bar element inside the given gauge. Useful when using dynamic gauges.

setCLTBar(value: string | number, asPercentage: boolean = false): void
setMATBar(value: string | number, asPercentage: boolean = false): void
setOilPressBar(value: string | number, asPercentage: boolean = false): void
setFuelPressBar(value: string | number, asPercentage: boolean = false): void
setBoostBar(value: string | number, asPercentage: boolean = false): void
setOilTempBar(value: string | number, asPercentage: boolean = false): void
setFuelLevelBar(value: string | number, asPercentage: boolean = false): void
setBatteryBar(value: string | number, asPercentage: boolean = false): void
setSpeedBar(value: string | number, asPercentage: boolean = false): void
setRPMBar(value: string | number, asPercentage: boolean = false): void
setTPSBar(value: string | number, asPercentage: boolean = false): void
setAFRBar(value: string | number, asPercentage: boolean = false): void
setLambdaBar(value: string | number, asPercentage: boolean = false): void

It also accepts a second parameter to set the value as a percentage of the max value and include the '%' at the end. Defaults to false, which will return the value as a fraction of the max value (for use with scaleX or scaleY).

It only works when the CAN data set is available.

The result of these methods is the same as calling setRootCSS with the --<gauge-name>-gauge-bar property. Ensure that the --<gauge-name>-gauge-bar property is set on the CSS file with default values for a better experience.

Reminder: Some values don't start at zero, which can affect the correct representation of the bar - e.g. AFR start at 8 (8 to 20) - so passing the value with the correct offset is recommended.

value: The actual value to be calculated by the method.

asPercentage: Whether to return the value as a percentage of the max value or as a fraction of the max value. Defaults to false.

setCLTBar(80); // input: 80 -> output: 0.66 - assuming 120 as the max value for CLT
setMATBar(80, true); // input: 80 -> output: 72.7% - assuming 110 as the max value for MAT
setOilPressBar(3); // input: 3 -> output: 0.3 - assuming 10 as the max value for oil pressure

fuelLevelFormat(value: string | number): string

Formats the fuel level value to a safely readable format. It returns a range from 0 to 100, removing any decimal places and values out of range.

value: The value to format.

fuelLevelFormat(72.25); // output: 72
fuelLevelFormat(103); // output: 100

loadOdo(odoTotal: HTMLElement, odoTrip: HTMLElement, value: string | number): void

Sets the value of both odometer elements. It is useful to load the values every time the skin is loaded by passing zero as the value.

The value to be passed is the increment of the odometer, not the total value. e.g. If the total odometer is 1000 and the trip odometer is 200, passing 1.24 will set the total odometer to 1001 and the trip odometer to 201.

Fractional values will be rounded to the nearest integer.

odoTotal: The element to set the total odometer value of.

odoTrip: The element to set the trip odometer value of.

value: The value to set.

 // Saved values - totalOdo: 1000, tripOdo: 200
loadOdo(totalOdo, tripOdo, 0); // totalOdo: 1000, tripOdo: 200
loadOdo(totalOdo, tripOdo, 1.74); // totalOdo: 1002, tripOdo: 202

updateOdo(odoTotal: HTMLElement, odoTrip: HTMLElement, value: string | number): void

This method is similar to loadOdo but doesn't update the odometer unless necessary. It was designed to be used within the real-time data stream and depends on a specific property within both Basic and CAN data sets - odoNow.

Updating the odometer is a critical task and must be implemented within your skin's scripting with good priority. It's recommended to call this method inside your bindRealtimeData method.

odoTotal: The element to set the total odometer value of.

odoTrip: The element to set the trip odometer value of.

value: The value to set.

const useCANForVSS = useCanChannel('sVss'); // 'sVss' = Source VSS in User Settings
updateOdo(kmTotal, kmTrip, useCANForVSS ? canData.odoNow : basicData.odoNow)

useCanChannel(channel?: string): boolean

Returns true if the given channel is available and should be used to get data from the CAN data set. If no channel is given, it returns true if the CAN data set is available.

This method can be used to verify what source the user has selected to get the data from the given channel. It must be used to check every channel that can be available on both CAN and Basic data sets. e.g. RPM, VSS, CLT, etc.

If the user has selected CAN as the source for the given channel, it will return true, and you should use the CAN data set to get the data. If the user has selected Internal Module (Basic) as the source, it will return false, and you should use the Basic data set to get the data.

channel: The channel to check. Defaults to undefined. Please take a look at the data set section below for a list of available channels.

const useCANForRPM = useCanChannel('sRpm');  // 'sRpm' = Source RPM in User Settings - returns true if CAN is selected
// ...
const bindRealtimeData = () => {
  updateRPM(useCANForRPM ? canData.rpm : safeReturn(basicData, 'RPM'))
}

⚑ Available data sets

βšͺ Basic data set

The Basic data set is available on all Ghost Dashboard hardware and contains the most common channels used in automotive applications. It is the default data set and is always available, even if the user has selected CAN as the source for a given channel or disabled it completely.

To read the data from this data set, you can access the basicData object, which is available on the global scope and can be accessed anywhere in your script.

Available channels:

Channel Description Type Unit Decimal places Min Max
rpm Engine speed number RPM 0 0 10000
kmh Vehicle speed (higher refresh rate, lower accuracy) number km/h 0 0 999
kmhF Vehicle speed (lower refresh rate, higher accuracy) number km/h 0 0 999
clt Coolant temperature number Β°C 0 -40 150
lvlFuel Fuel level (higher refresh rate, lower accuracy) number % 0 0 100
lvlFuelF Fuel level (lower refresh rate, higher accuracy) number % 0 0 100
odoNow Trip odometer number km 3 0 100
turnLeft Turn signal left number binary - 0 1
turnRight Turn signal right number binary - 0 1
parkLights Parking lights number binary - 0 1
fogLights Fog lights number binary - 0 1
auxLights Auxiliary lights number binary - 0 1
highBeam High beam number binary - 0 1
eBrake E-brake number binary - 0 1
battAlt Alternator/Battery number binary - 0 1
ECUErr Engine light number binary - 0 1
oilSwitch Oil pressure switch number binary - 0 1
rearDefrost Rear windshield defroster number binary - 0 1
fan Readiator Fan On number binary - 0 1
openDoor Door open number binary - 0 1
airbag Airbag number binary - 0 1

Note

All binary values represent a reverse logic, meaning 0 is ON and 1 is OFF.


βšͺ CAN data set

The CAN data set is only available on Ghost Dashboard hardware that supports CAN and contains the most common channels used in automotive performance instrumentation. It is available when the user enables the CAN communication and there's a valid CAN connection.

To read the data from this data set, you can access the canData object, which is available on the global scope and can be accessed anywhere in your script.

Available channels:

Channel Description Type Unit Decimal places Min Max
rpm Engine speed number RPM 0 0 10000
vss Vehicle speed number km/h 0 0 999
tps Throttle position number % 1 0 100
map Manifold absolute pressure number BAR 2 0 10
clt Coolant temperature number Β°C 0 -40 150
mat Manifold air temperature number Β°C 0 -40 150
oilPress Oil pressure number BAR 1 0 20
oilTemp Oil temperature number Β°C 0 -40 150
fuelPress Fuel pressure number BAR 1 0 20
gear Gear string - 0 P/N/R 10
lambda Lambda number - 2 0 2
afr Air/fuel ratio number - 2 8 20
batt Battery voltage number V 2 0 20
boost Boost pressure number BAR 2 0 20
odoNow Trip odometer number km 3 0 999
mapbar Special map/boost bar value string - 2 0 20

Note

  • The gear channel returns a string value, which can be P, N, R, or a number from 1 to 10.
  • The mapbar channel is a special value used by the mapBarFormat method to calculate the value for the map/boost bar. It is not meant to be used directly.
  • Besides the global canData object, the global variable isECUOnline is also available and can be used to check if the ECU is online and sending data. It returns true if the ECU is online and false if it's offline.
const useCAN = useCanChannel();  // returns true if CAN is enabled and available
const useCANForRPM = useCanChannel('sRpm');  // 'sRpm' = Source RPM in User Settings - returns true if CAN is selected
const useCANForVSS = useCanChannel('sVss');  // 'sVss' = Source VSS in User Settings - returns true if CAN is selected
// ...
const updateRPM = (rpm) => { ... }
const updateVSS = (vss) => { ... }

const bindRealtimeData = () => {
   updateRPM(useCANForRPM ? canData.rpm : safeReturn(basicData, 'RPM'))
   updateVSS(useCANForVSS ? canData.vss : safeReturn(basicData, 'kmh'))
}
// ...

πŸ“ Collaborating

Have you found a bug or have a suggestion?
Feel free to create issues and pull requests to help us improve the SDK and the skins.

Any suggestions, improvements and feedback are welcome and appreciated.


πŸ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

The fonts used in the skins are licensed under the SIL Open Font License and can be found for free at Google Fonts.

The icons used in the skins were created by nrsleonardo


πŸ“ Changelog

All notable changes to this project will be documented in the CHANGELOG file.


πŸ“ž Support and Contact

If you have any questions or suggestions, please feel free to open an issue or contact us at:

About

SDK and sample code for creating skins for the Ghost Dashboard.

License:MIT License


Languages

Language:HTML 62.8%Language:CSS 28.7%Language:JavaScript 8.5%