<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.engineersofinnovation.nl/index.php?action=history&amp;feed=atom&amp;title=Dashboard</id>
	<title>Dashboard - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.engineersofinnovation.nl/index.php?action=history&amp;feed=atom&amp;title=Dashboard"/>
	<link rel="alternate" type="text/html" href="https://wiki.engineersofinnovation.nl/index.php?title=Dashboard&amp;action=history"/>
	<updated>2026-09-20T10:12:40Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>https://wiki.engineersofinnovation.nl/index.php?title=Dashboard&amp;diff=337&amp;oldid=prev</id>
		<title>Aran Dokoupil: Created page with &quot;The '''dashboard''' is the e-paper screen the pilot reads while sailing. It is mounted on the '''left side of the steering wheel''' in the cockpit, and shows speed, battery power, state of charge, temperatures and the clock. It measures nothing itself: it listens to the whole CAN bus and draws whatever the other nodes are saying.  The dashboard showing live data  == At a glance ==  {| class=&quot;wikit...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.engineersofinnovation.nl/index.php?title=Dashboard&amp;diff=337&amp;oldid=prev"/>
		<updated>2026-08-17T18:59:32Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;The &amp;#039;&amp;#039;&amp;#039;dashboard&amp;#039;&amp;#039;&amp;#039; is the e-paper screen the pilot reads while sailing. It is mounted on the &amp;#039;&amp;#039;&amp;#039;left side of the steering wheel&amp;#039;&amp;#039;&amp;#039; in the cockpit, and shows speed, battery power, state of charge, temperatures and the clock. It measures nothing itself: it listens to the whole &lt;a href=&quot;/wiki/CAN-bus&quot; title=&quot;CAN-bus&quot;&gt;CAN bus&lt;/a&gt; and draws whatever the other nodes are saying.  &lt;a href=&quot;/wiki/File:Dashboard-epaper-interface-with-data.jpeg&quot; title=&quot;File:Dashboard-epaper-interface-with-data.jpeg&quot;&gt;thumb|500px|The dashboard showing live data&lt;/a&gt;  == At a glance ==  {| class=&amp;quot;wikit...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;The '''dashboard''' is the e-paper screen the pilot reads while sailing. It is mounted on the '''left side of the steering wheel''' in the cockpit, and shows speed, battery power, state of charge, temperatures and the clock. It measures nothing itself: it listens to the whole [[CAN-bus|CAN bus]] and draws whatever the other nodes are saying.&lt;br /&gt;
&lt;br /&gt;
[[File:Dashboard-epaper-interface-with-data.jpeg|thumb|500px|The dashboard showing live data]]&lt;br /&gt;
&lt;br /&gt;
== At a glance ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Dashboard summary&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Value&lt;br /&gt;
|-&lt;br /&gt;
| Panel || Waveshare 5.79&amp;quot; e-paper, 792 × 272, black and white, SSD1683 controller&lt;br /&gt;
|-&lt;br /&gt;
| Location || Cockpit, left of the steering wheel&lt;br /&gt;
|-&lt;br /&gt;
| Controller board || STM32L471RGT6 — the '''same PCB''' as the [[Height sensors|height sensor controller]]&lt;br /&gt;
|-&lt;br /&gt;
| Bus || CAN 2.0A, 1 Mbit/s, 11-bit standard identifiers&lt;br /&gt;
|-&lt;br /&gt;
| Transmits || Nothing, except replies to bootloader queries on &amp;lt;code&amp;gt;0x038&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Update rate || At most once per second, and only when the picture actually changed&lt;br /&gt;
|-&lt;br /&gt;
| Application type || &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; — its address in the bootloader protocol&lt;br /&gt;
|-&lt;br /&gt;
| Firmware || Rust, &amp;lt;code&amp;gt;embassy&amp;lt;/code&amp;gt;, binary &amp;lt;code&amp;gt;dashboard&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Firmware source || &amp;lt;code&amp;gt;firmware/&amp;lt;/code&amp;gt; in the [https://github.com/Engineers-of-Innovation/eoi-can eoi-can] monorepo&lt;br /&gt;
|-&lt;br /&gt;
| Screen source || &amp;lt;code&amp;gt;draw-display/&amp;lt;/code&amp;gt; in the same repo, shared with the simulator&lt;br /&gt;
|-&lt;br /&gt;
| Hardware source || Altium project &amp;lt;code&amp;gt;AKD-CAN_Interface&amp;lt;/code&amp;gt; (Altium 365)&lt;br /&gt;
|-&lt;br /&gt;
| Field update || Over CAN, no debug probe needed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== What is on the screen ==&lt;br /&gt;
&lt;br /&gt;
The screen is a fixed layout — nothing moves, and a value that goes missing is replaced by dashes in the same place rather than disappearing.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Screen regions&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Shows&lt;br /&gt;
|-&lt;br /&gt;
| Left column || '''Net Power''' in watts as the headline, with '''Power In''' and '''Power Out''' beneath it&lt;br /&gt;
|-&lt;br /&gt;
| Centre || '''Speed''' in km/h, large, with the GNSS fix state under it (&amp;lt;code&amp;gt;3D fix&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;2D fix&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;No fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;---&amp;lt;/code&amp;gt; when the receiver is silent)&lt;br /&gt;
|-&lt;br /&gt;
| Right column || '''State of Charge''' in percent as the headline, over a 2 × 2 grid of temperatures: Motor, Driver, the hottest MPPT, and the hottest battery thermistor&lt;br /&gt;
|-&lt;br /&gt;
| Under the speed || Four '''warning icons''', each in a fixed slot&lt;br /&gt;
|-&lt;br /&gt;
| Bottom row || '''Current Time''', '''Race Time''', '''Time to Empty'''&lt;br /&gt;
|-&lt;br /&gt;
| Top line, small || Firmware version and git hash, centred; the [[Datalogger|datalogger]]'s WiFi IP on the left when it reports one&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
'''Power.''' Currents leaving the battery are negative on the bus, so the three figures are: ''Power In'' = charge current × pack voltage, ''Power Out'' = (motor + peripherals) × pack voltage, and ''Net Power'' = the sum of all three. '''A positive net power means the battery is charging.'''&lt;br /&gt;
&lt;br /&gt;
'''Temperatures.''' The MPPT cell names the unit it is showing — &amp;lt;code&amp;gt;MPPT F3&amp;lt;/code&amp;gt; forward, &amp;lt;code&amp;gt;MPPT R0&amp;lt;/code&amp;gt; aft — because it always shows whichever MPPT is currently hottest. The Motor cell reads the standalone NTC node on &amp;lt;code&amp;gt;0x219&amp;lt;/code&amp;gt; only; the VESC reports a motor temperature too, but it is broken on this boat and is deliberately never used as a fallback.&lt;br /&gt;
&lt;br /&gt;
'''Missing data.''' Every value has a '''5 second''' staleness timeout. If nothing has arrived for a value within that window it draws as dashes. Dashes therefore mean &amp;quot;not being reported&amp;quot;, not &amp;quot;zero&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Warning icons ===&lt;br /&gt;
&lt;br /&gt;
Each icon appears only while its condition holds, in its own fixed slot, so a warning is always in the same place.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Icon !! Raised when&lt;br /&gt;
|-&lt;br /&gt;
| Battery || Battery state, charge FET or discharge FET is anything other than normal&lt;br /&gt;
|-&lt;br /&gt;
| Low charge || State of charge below '''15 %'''&lt;br /&gt;
|-&lt;br /&gt;
| Over-temperature || Motor above '''50 °C''', driver above '''70 °C''', any MPPT above '''80 °C''', or a battery thermistor above '''45 °C'''&lt;br /&gt;
|-&lt;br /&gt;
| Throttle || The [[Throttle|throttle]] reports any error flag&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A stale or missing value never raises an icon — the dashes already show that data is being lost.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
The board is the Altium project &amp;lt;code&amp;gt;AKD-CAN_Interface&amp;lt;/code&amp;gt;, the '''same PCB as the [[Height sensors|height sensor controller]]'''. Where that board fits four RS-485 channels, this one fits the e-paper panel on SPI2.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Display connections&lt;br /&gt;
|-&lt;br /&gt;
! Signal !! Pin&lt;br /&gt;
|-&lt;br /&gt;
| SPI2 SCK / MOSI / MISO || PB13 / PB15 / PB14&lt;br /&gt;
|-&lt;br /&gt;
| Chip select || PC6&lt;br /&gt;
|-&lt;br /&gt;
| Data/command || PC9&lt;br /&gt;
|-&lt;br /&gt;
| Reset || PC8&lt;br /&gt;
|-&lt;br /&gt;
| Busy || PA8&lt;br /&gt;
|-&lt;br /&gt;
| Panel power || PB12, held on for the life of the firmware&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
SPI runs at '''2.5 MHz''' and is DMA-backed, so a full framebuffer transfer (~86 ms) does not block the CAN receive task. Those DMA channels — &amp;lt;code&amp;gt;DMA1_CH4&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;CH5&amp;lt;/code&amp;gt; — are the only pair SPI2 can use, and they are the ones I²C2 would need, which is '''why this board has no onboard temperature sensor''' where its sister boards do.&lt;br /&gt;
&lt;br /&gt;
24 V arrives on the CAN connector and is stepped down on the board, exactly as on the height sensor unit. The safety line passes straight through and is not connected to the microcontroller.&lt;br /&gt;
&lt;br /&gt;
== Refreshing ==&lt;br /&gt;
&lt;br /&gt;
E-paper is slow and wears with every drive cycle, so the firmware avoids refreshes it does not need:&lt;br /&gt;
&lt;br /&gt;
* The framebuffer is hashed after each render. '''An identical picture is not sent to the panel at all.'''&lt;br /&gt;
* Every 60th refresh is a '''full''' refresh, which clears the ghosting that the fast differential refresh accumulates. The other 59 are differential.&lt;br /&gt;
* A refresh takes between '''0.7 and 2.5 seconds'''; the render itself costs about half a second. The loop is floored at one second per iteration.&lt;br /&gt;
&lt;br /&gt;
So the screen updates roughly once a second while values are changing, and sits still when they are not.&lt;br /&gt;
&lt;br /&gt;
== CAN ==&lt;br /&gt;
&lt;br /&gt;
The dashboard uses an '''accept-all''' hardware filter — it needs the whole bus — and decodes the identifiers it knows about. Everything else is ignored.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ What the screen is built from&lt;br /&gt;
|-&lt;br /&gt;
! Source !! Identifiers !! Used for&lt;br /&gt;
|-&lt;br /&gt;
| [[Battery|BMS]] || &amp;lt;code&amp;gt;0x100&amp;lt;/code&amp;gt;–&amp;lt;code&amp;gt;0x107&amp;lt;/code&amp;gt; || State of charge, pack voltage, the three currents, temperatures, battery/charge/discharge states&lt;br /&gt;
|-&lt;br /&gt;
| GNSS || &amp;lt;code&amp;gt;0x200&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x201&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x204&amp;lt;/code&amp;gt; || Fix state, speed, clock&lt;br /&gt;
|-&lt;br /&gt;
| [[Motordriver|VESC]] || &amp;lt;code&amp;gt;0x1009&amp;lt;/code&amp;gt; || Driver (FET) temperature&lt;br /&gt;
|-&lt;br /&gt;
| Motor NTC node || &amp;lt;code&amp;gt;0x219&amp;lt;/code&amp;gt; || Motor temperature&lt;br /&gt;
|-&lt;br /&gt;
| [[MPPT]] / [[GaN MPPT]] || &amp;lt;code&amp;gt;0x400&amp;lt;/code&amp;gt;–&amp;lt;code&amp;gt;0x4FF&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x700&amp;lt;/code&amp;gt;–&amp;lt;code&amp;gt;0x77F&amp;lt;/code&amp;gt; || MPPT temperatures, per-panel power&lt;br /&gt;
|-&lt;br /&gt;
| [[Throttle]] || &amp;lt;code&amp;gt;0x1337&amp;lt;/code&amp;gt; || Throttle error flags&lt;br /&gt;
|-&lt;br /&gt;
| [[Datalogger]] || &amp;lt;code&amp;gt;0x205&amp;lt;/code&amp;gt; || WiFi IP shown on the stamp line&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The full bus-wide reference is [https://github.com/Engineers-of-Innovation/eoi-can/blob/main/CAN_MESSAGES.md CAN_MESSAGES.md].&lt;br /&gt;
&lt;br /&gt;
'''The dashboard originates no traffic.''' The only frames it ever sends are answers to the host's bootloader-protocol queries.&lt;br /&gt;
&lt;br /&gt;
== Firmware update over CAN ==&lt;br /&gt;
&lt;br /&gt;
The first 80 KB of flash holds a CAN bootloader, so the board can be updated in the cockpit without a debug probe. It owns application type &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt;, giving it the identifier block &amp;lt;code&amp;gt;0x037&amp;lt;/code&amp;gt; (command) / &amp;lt;code&amp;gt;0x038&amp;lt;/code&amp;gt; (response) / &amp;lt;code&amp;gt;0x039&amp;lt;/code&amp;gt; (write data); the bootloader's hardware filter rejects every other block, so a command aimed at another board cannot touch this one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
eoi-flash-tool scan                          # what is on the bus?&lt;br /&gt;
eoi-flash-tool flash dashboard               # target comes from the ELF&lt;br /&gt;
eoi-flash-tool --board dashboard version     # which build is on it?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The version and git hash of whatever is running are also printed along the top of the screen, so the running build can be read off the panel without a laptop.&lt;br /&gt;
&lt;br /&gt;
The protocol, the flash layout and the addressing scheme are shared with the other boards and are documented in full on the [[Rudder controller]] page.&lt;br /&gt;
&lt;br /&gt;
== Working on the screen without hardware ==&lt;br /&gt;
&lt;br /&gt;
The whole layout lives in the &amp;lt;code&amp;gt;draw-display&amp;lt;/code&amp;gt; crate and is shared by the firmware, a desktop simulator and a Raspberry Pi framebuffer tool — so a screen change can be seen without flashing anything.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd eoi-can-display-simulator&lt;br /&gt;
cargo run -- -ccan0                          # live, against a real bus&lt;br /&gt;
EG_SIMULATOR_DUMP=screenshot.png cargo run   # write a PNG instead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The layout is checked at compile time: the positions are const arithmetic, and assertions fail the build if two regions would overlap or a value would not fit its cell. Font metrics are pinned by tests, so a regenerated font that drifts breaks the build rather than the picture.&lt;br /&gt;
&lt;br /&gt;
A second layout, '''foiling''', exists for the foiling trim screen and runs on its own board. Same code, same decoder, different arrangement — pick it with &amp;lt;code&amp;gt;--layout foiling&amp;lt;/code&amp;gt; in the simulator.&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reading the board without a debugger&lt;br /&gt;
|-&lt;br /&gt;
! Symptom !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Green LED toggling once per second || Application running normally&lt;br /&gt;
|-&lt;br /&gt;
| Blue LED flickering || CAN frames arriving — it toggles once per frame&lt;br /&gt;
|-&lt;br /&gt;
| Red LED on briefly || A panel refresh is in progress&lt;br /&gt;
|-&lt;br /&gt;
| Red LED double-flashing || Sitting in the bootloader, waiting or flashing&lt;br /&gt;
|-&lt;br /&gt;
| Everything reads dashes || Nothing is arriving on the bus. Check bus wiring and termination before suspecting the board.&lt;br /&gt;
|-&lt;br /&gt;
| One column reads dashes || That node is silent; the dashboard is fine&lt;br /&gt;
|-&lt;br /&gt;
| Screen frozen but LEDs normal || The picture has not changed — identical frames are deliberately skipped&lt;br /&gt;
|-&lt;br /&gt;
| Ghosting or grey smears || Normal between full refreshes; cleared by the next one, at most 60 refreshes away&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A 4-second independent hardware watchdog is petted once per second, so wedged firmware resets itself. If no frames arrive for several loop iterations the firmware logs the CAN peripheral state and re-arms the receive interrupts by itself. Detailed logging is available over SWD through &amp;lt;code&amp;gt;defmt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Known gaps ==&lt;br /&gt;
&lt;br /&gt;
* '''Race Time is empty.''' The block is drawn and labelled, but nothing on the bus signals a race start, so it always shows dashes.&lt;br /&gt;
* '''Time to Empty is empty.''' Nothing calculates or broadcasts it yet. The BMS field exists but is not fed to the screen.&lt;br /&gt;
* '''The clock is hardcoded to UTC+2.''' Correct for Dutch summer time and wrong by an hour the rest of the year. There is no time zone handling.&lt;br /&gt;
* '''No onboard temperature sensor.''' Unavoidable on this board — SPI2's DMA channels are the ones I²C2 would need. The dashboard therefore reports no board temperature of its own.&lt;br /&gt;
* '''No user input.''' The board has a sealed rotary switch fitted, but no firmware reads it. The screen cannot be paged or configured from the cockpit.&lt;br /&gt;
* '''Per-panel MPPT power is decoded but not shown.''' The data is collected and available to the layout; there is no room for it on the dashboard screen.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Height sensors]] — the same PCB, in its other configuration&lt;br /&gt;
* [[Rudder controller]] — sister board, and the full bootloader protocol&lt;br /&gt;
* [[CAN-bus]] — bus wiring, cable pinout and the bus-wide identifier map&lt;br /&gt;
* [[Battery]] — where the power and state of charge figures come from&lt;br /&gt;
* [[Instrumentation Panel]] — the switch panel in the same cockpit&lt;br /&gt;
* [[Datalogger]] — records the same bus the dashboard displays&lt;br /&gt;
* [[Altium PCB]] — house PCB design conventions&lt;/div&gt;</summary>
		<author><name>Aran Dokoupil</name></author>
	</entry>
</feed>