Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion projects/dashboard/include/Display.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "StartingMotors.hpp"
#include "generated/can/veh_bus.hpp"
#include "generated/can/veh_messages.hpp"
#include "inc/Tuning.hpp"

class Display {
public:
Expand Down Expand Up @@ -44,7 +45,6 @@ class Display {
std::optional<State> transition_;
State state_ = State::LOGO;
Screen* screen_;

LogoScreen logo_screen;
ProfileSelect profile_select;
ConfirmMenu confirm_menu;
Expand Down
11 changes: 11 additions & 0 deletions projects/dashboard/include/Tuning.hpp

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we need a tuning menu, the driver should just be able to go

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include "inc/Screen.hpp"

class Tuning : public Screen {
public:
Tuning(Display* display);

void CreateGUI() override;
void Update() override;
};
7 changes: 7 additions & 0 deletions projects/dashboard/src/ProfileSelect.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "ProfileSelect.hpp"

#include <string>

#include "Display.hpp"
#include "generated/can/veh_bus.hpp"

Expand Down Expand Up @@ -35,7 +37,12 @@ void ProfileSelect::Update() {
if (display_->enter.PosEdge()) {
display_->selected_profile =
static_cast<Profile>(lv_roller_get_selected(roller));
// if (static_cast<std::string>(
// GetProfileName(display_->selected_profile)) == "TUNING") {
// display_->ChangeState(State::TUNING);
// } else {
display_->ChangeState(State::CONFIRM_SELECTION);
// }

} else if (display_->scroll.PosEdge()) {
int new_position = lv_roller_get_selected(roller) + 1;
Expand Down
26 changes: 26 additions & 0 deletions projects/dashboard/src/platforms/linux/fc_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,32 @@ def run(self):
lambda ds: ds["State"] == "WAIT_SELECTION_ACK",
)
print(f"Profile:\t{ds['Profile']}")

if ds["Profile"] == "Tuning":
while True:
self.bus.set_filters(
[
{
"can_id": dash_msg.frame_id,
"can_mask": 0x7FF,
"extended": False,
},
{
"can_id": rberry_msg.frame_id,
"can_mask": 0x7FF,
"extended": False,
},
]
)
tune_msg = self.bus.recv()
# print(f"ID: {hex(tune_msg.arbitration_id)}, DLC: {tune_msg.dlc}, data: {tune_msg.data.hex()}")

tune = dbc.decode_message(tune_msg.arbitration_id, tune_msg.data)
if tune_msg.arbitration_id == 260:
print(tune)
break
# print(tune)

sleep(DELAY)
self.FcStatus["ConfigReceived"] = True

Expand Down
3 changes: 2 additions & 1 deletion projects/veh.dbc
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ BO_ 310 SuspensionTravel34: 2 LVC
SG_ STP4 : 8|8@1+ (1,0) [0|255] "" FC

BO_ 340 TuningParams: 8 RPI
SG_ aggressiveness : 0|8@1+ (1,0) [0|100] "" FC
SG_ damping : 0|8@1+ (1,0) [0|100] "" FC
SG_ aggressiveness : 8|8@1+ (1,0) [0|100] "" FC

CM_ "4xn - General / debugging info";

Expand Down
58 changes: 58 additions & 0 deletions scripts/canflash/CanFlashApp.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ui looks good

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from nicegui import ui
from nicegui.events import UploadEventArguments
from flash_logic import save_uploaded_file, flash_file
from constants import ECU_CONFIG
import os


class CanFlashApp:
def __init__(self):
self.starting_ecu = "Select ECU"
self.selected_ecu = self.starting_ecu
self.uploaded_file_path = None
self.build_ui()

def handle_upload(self, file: UploadEventArguments):
self.uploaded_file_path = save_uploaded_file(self.selected_ecu, file.name, file.content.read())
self.flash_button.enable()

def handle_flash(self):
exception = flash_file(self.selected_ecu, self.uploaded_file_path)

if not exception:
uploaded_file_name = os.path.split(self.uploaded_file_path)[-1]
ui.notify(
f"Successfully flashed {uploaded_file_name} to {self.selected_ecu}",
position="center",
)

# Reset upload box and disable flash button
self.upload_box.reset()
self.flash_button.disable()

# Reset ECU selection after a short delay (or else the select box won't reset)
ui.timer(0.1, lambda: self.ecu_select.set_value(self.starting_ecu), once=True)
else:
ui.notify(str(exception), type="negative")


def build_ui(self):
ui.markdown("Welcome to CAN Flash! Select an ECU and upload a file to flash. See [docs](https://macformula.github.io/racecar/) for more information.")

# ECU Selection
self.ecu_select = ui.select([self.starting_ecu] + list(ECU_CONFIG.keys()))
self.ecu_select.bind_value(self, "selected_ecu")

# File Upload
self.upload_box = ui.upload(
label="Select File",
on_upload=self.handle_upload,
auto_upload=True,
).props("accept=.bin")
self.upload_box.bind_enabled_from(
self.ecu_select, "value", lambda v: v != self.starting_ecu
)

# Flash Button
self.flash_button = ui.button("Flash", on_click=self.handle_flash)
self.flash_button.disable()
87 changes: 87 additions & 0 deletions scripts/canflash/LiveTuning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from nicegui import ui, events
import subprocess


class LiveTuning:

def __init__(self, iface: str = "vcan0") -> None:
self.iface = iface
self.aggressiveness = 0
self.dampness = 0
self._build_ui()

def _slider_row(
self,
title: str,
field_name: str,
init_val: int = 0,
*,
on_change_cb,
):
with ui.row().classes("items-center gap-4 w-full"):
ui.label(title).classes("w-32 text-right")

slider = (
ui.slider(min=0, max=100, value=init_val, on_change=on_change_cb)
.props("step=1")
.classes("flex-1")
)

number = (
ui.number(value=init_val, min=0, max=100)
.props("style='width:80px'")
)

number.bind_value_from(slider, "value").bind_value_to(slider, "value")

setattr(self, f"{field_name}_slider", slider)
setattr(self, f"{field_name}_number", number)

def _build_ui(self) -> None:
"""Compose the entire visible UI."""
with ui.card().classes("w-full max-w-md mx-auto mt-14 p-6 shadow-lg"):
ui.label("Live Tuning").classes("text-2xl font-semibold mb-6 text-center")

# Aggressiveness row
self._slider_row(
"Aggressiveness",
"aggressiveness",
init_val=self.aggressiveness,
on_change_cb=lambda e: self._on_change("aggressiveness", e),
)

ui.separator()

# Dampness row
self._slider_row(
"Dampness",
"dampness",
init_val=self.dampness,
on_change_cb=lambda e: self._on_change("dampness", e),
)

ui.button(
"Submit",
icon="send",
on_click=self._submit,
).classes("w-full bg-primary text-white mt-6")

def _on_change(self, field: str, e: events.ValueChangeEventArguments) -> None:
# Clamp, validate, and store slider changes
value = e.value or 0
value = max(0, min(100, int(value)))
setattr(self, field, value)
print(f"{field.capitalize()} set to {value}")

def _submit(self, _=None) -> None:
print(f"Submit pressed (agg={self.aggressiveness}, damp={self.dampness})")
subprocess.run(
["bash", "./scripts/tune.sh",
str(self.aggressiveness), str(self.dampness), self.iface],
check=True,
)


if __name__ in {"__mp_main__", "__main__"}:
LiveTuning()
ui.run()
2 changes: 1 addition & 1 deletion scripts/canflash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ Run the server with the following command:
python3 main.py
```

Access the UI by opening [http://localhost:8000/](http://localhost:8000/) in your web browser.
Access the UI by opening [http://localhost:8000/](http://localhost:8000/) in your web browser.
3 changes: 0 additions & 3 deletions scripts/canflash/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
UPLOAD_DIR = "uploads"

# ECU Configuration with GPIO pins and CAN message IDs

# WARNING: These GPIO pins are not correct and need to be fixed. They correspond to
# the Raspberry PI gpio pin which connects to each ECU's boot pin.
ECU_CONFIG = {
"FrontController": {
"gpio_pin": 8,
Expand Down
114 changes: 49 additions & 65 deletions scripts/canflash/main.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,58 @@
from nicegui import ui
from nicegui.events import UploadEventArguments
from flash_logic import save_uploaded_file, flash_file
from constants import ECU_CONFIG
import os


class CanFlashApp:
def __init__(self):
self.starting_ecu = "Select ECU"
self.selected_ecu = self.starting_ecu
self.uploaded_file_path = None
self.build_ui()

def handle_upload(self, file: UploadEventArguments):
self.uploaded_file_path = save_uploaded_file(self.selected_ecu, file.name, file.content.read())
self.flash_button.enable()

def handle_flash(self):
exception = flash_file(self.selected_ecu, self.uploaded_file_path)

if not exception:
uploaded_file_name = os.path.split(self.uploaded_file_path)[-1]
ui.notify(
f"Successfully flashed {uploaded_file_name} to {self.selected_ecu}",
position="center",
)

# Reset upload box and disable flash button
self.upload_box.reset()
self.flash_button.disable()

# Reset ECU selection after a short delay (or else the select box won't reset)
ui.timer(0.1, lambda: self.ecu_select.set_value(self.starting_ecu), once=True)
else:
ui.notify(str(exception), type="negative")


def build_ui(self):
ui.colors(primary="#AA1F26")
ui.markdown("Welcome to CAN Flash! Select an ECU and upload a file to flash. See [docs](https://macformula.github.io/racecar/) for more information.")

# ECU Selection
self.ecu_select = ui.select([self.starting_ecu] + list(ECU_CONFIG.keys()))
self.ecu_select.bind_value(self, "selected_ecu")

# File Upload
self.upload_box = ui.upload(
label="Select File",
on_upload=self.handle_upload,
auto_upload=True,
).props("accept=.bin")
self.upload_box.bind_enabled_from(
self.ecu_select, "value", lambda v: v != self.starting_ecu
)

# Flash Button
self.flash_button = ui.button("Flash", on_click=self.handle_flash)
self.flash_button.disable()


@ui.page("/")
from CanFlashApp import CanFlashApp
from LiveTuning import LiveTuning
from contextlib import contextmanager

@contextmanager
def frame():
ui.colors(primary="#AA1F26")
with ui.header(elevated=True).classes('width-full text-white bg-[#AA1F26] '):
ui.label("Raspberry Pi Resources").classes("text-xl font-bold mr-4 ")
ui.button("Home", on_click=lambda : ui.navigate.to('/')).classes("!bg-[#df535a]")
ui.button('Can Flash', on_click= lambda : ui.navigate.to("/canflash")).classes("!bg-[#df535a]")
ui.button('Tuning', on_click= lambda : ui.navigate.to("/tune")).classes("!bg-[#df535a]")
yield

@ui.page("/canflash")
def main_page():
CanFlashApp()


with frame():
CanFlashApp()

@ui.page("/tune")
def tuning_page():
with frame():
LiveTuning()

@ui.page('/')
def home() -> None:
with frame(): # keep the shared header
with ui.column().classes('items-center q-mt-xl gap-4'):
ui.label('Welcome to the Formula‑Electric Raspberry Pi Interface') \
.classes('text-2xl font-bold')

ui.markdown(
'This dashboard lets you interact with the **Formula Electric** car’s '
'Raspberry Pi. \n'
'Use it to *live‑tune* parameters while the car is running or to '
'*flash* new firmware over CAN. '
'More modules will be added soon!'
).classes('max-w-xl text-center')

with ui.row().classes('gap-4'):
ui.button('Go to Live Tuning', on_click=lambda: ui.navigate.to('/tune')) \
.classes('!bg-[#df535a] text-white')
ui.button('Go to CAN Flash', on_click=lambda: ui.navigate.to('/canflash')) \
.classes('!bg-[#df535a] text-white')

ui.link('Read the full documentation',
'https://macformula.github.io/racecar/',
new_tab=True) \
.classes('text-primary underline q-mt-md')
if __name__ in {"__main__", "__mp_main__"}:
ui.run(
title="CAN Flash",
favicon="favicon.ico",
show=False,
dark=None,
port=8000,
)
)
2 changes: 1 addition & 1 deletion scripts/canflash/scripts/setup.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pip install -r requirements.txt
sudo apt update

# Install dependencies
sudo apt install -y libgtk-3-dev build-essential gcc g++ pkg-config make hostapd libqrencode-dev libpng-dev iptables
sudo apt install -y libgtk-3-dev build-essential gcc g++ pkg-config make hostapd libqrencode-dev libpng-dev iptables can-utils

# Clone repo
cd
Expand Down
2 changes: 1 addition & 1 deletion scripts/canflash/scripts/teardown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sudo rm -f /lib/systemd/system/canflash.service

# Remove installed packages
echo "Removing installed packages..."
sudo apt remove --purge -y libgtk-3-dev build-essential gcc g++ pkg-config make hostapd libqrencode-dev libpng-dev iptables
sudo apt remove --purge -y libgtk-3-dev build-essential gcc g++ pkg-config make hostapd libqrencode-dev libpng-dev iptables can-utils
sudo apt autoremove -y
sudo apt clean

Expand Down
Loading
Loading