Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NF] Added Raspberry Pico Board visualization #6

Merged
merged 1 commit into from
Nov 29, 2024
Merged
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ $global.FIRMWARE=my_awesome_binary.elf
include @run_firmware.resc
```

# Board visualization
There is possibility to visualize board using python visualization plugin.
> [!IMPORTANT]
> Only Raspberry Pico based boards are currently supported
> You can add buttons or leds and they will be automatically registered
> But totally different boards are not supported yet

To use it create python virtual env, then inside virtualenv:
```
pip3 install -r visualization/requirements.txt
renode --console your_simulation.resc

inside renode console:
(rasbperry_pico) startVisualization 8080
```
and open localhost:8080 in your web browser.

Current visualization is ugly, but it works!


# Multi Node simulation.
Many RP2040 simulators may interwork together. I am using that possibility in full MSPC simulation. To interwork between them GPIOConnector may be used, please check existing usage (`simulation` directory):
[MSPC Board Simulation](https://github.com/matgla/mspc-south-bridge/)
Expand Down
3 changes: 3 additions & 0 deletions boards/initialize_custom_board.resc
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
$machine_name?="raspberry_pico"
$visualization_path?=$ORIGIN/../visualization

include $ORIGIN/../cores/initialize_peripherals.resc
machine LoadPlatformDescription $platform_file
sysbus LoadELF $ORIGIN/../bootroms/rp2040/b2.elf

include $ORIGIN/../visualization/visualization.py

setVisualizationPath $visualization_path

16 changes: 12 additions & 4 deletions emulation/peripherals/gpio/rp2040_gpio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ public bool GetPullUp(int pin)
public void SetPullDown(int pin, bool state)
{
pullDown[pin] = state;
if (!IsPinOutput(pin) && state == true)
if (state == true)
{
State[pin] = false;
Connections[pin].Set(false);
Expand All @@ -812,7 +812,7 @@ public void SetPullDown(int pin, bool state)
public void SetPullUp(int pin, bool state)
{
pullUp[pin] = state;
if (!IsPinOutput(pin) && state == true)
if (state == true)
{
State[pin] = true;
Connections[pin].Set(true);
Expand Down Expand Up @@ -1247,14 +1247,22 @@ private void EnableInterruptsForCore(int core, int startingPin, ulong value)
bool levelLow = (value & (1u << (i * 4))) != 0;
if (levelLow)
{
this.Log(LogLevel.Noisy, "Enabling level low interrupt for pin: {0}", pin);
this.Log(LogLevel.Noisy, "Enabling level low interrupt for pin: {0}, current: {1}", pin, State[pin]);
if (!State[pin])
{
IRQ[core].Set(true);
}
}
irqProc[core, pin].LevelLow = levelLow;

bool levelHigh = (value & (1u << (i * 4 + 1))) != 0;
if (levelHigh)
{
this.Log(LogLevel.Noisy, "Enabling level high interrupt for pin: {0}", pin);
this.Log(LogLevel.Noisy, "Enabling level high interrupt for pin: {0}, current: {1}", pin, State[pin]);
if (State[pin])
{
IRQ[core].Set(true);
}
}
irqProc[core, pin].LevelHigh = levelHigh;

Expand Down
1 change: 1 addition & 0 deletions run_firmware.resc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
$machine_name?="rasbperry_pico"
$platform_file?=$ORIGIN/boards/raspberry_pico.repl
$visualization_file?=$ORIGIN/visualization/raspberry_pico
include $ORIGIN/boards/initialize_custom_board.resc

sysbus LoadELF $global.FIRMWARE
Expand Down
Empty file added visualization/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions visualization/assets/button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions visualization/assets/led.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions visualization/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>

<head>
<link rel="stylesheet" href="./raspberry_pico/assets/style.css">
<script src="messages.js"> </script>
</head>

<body>
<div id="app">
<div id="container">
<svg class="board" version="1.1" width="100%" height="100%">
<image xlink:href="./raspberry_pico/assets/Raspberry_Pi_Pico_top.jpg" y="0" x="0" preserveAspectRatio="1"
height="100%" width="100%" />
</svg>
</div>
<div id="widgets">
<div id="leds"> </div>
<div id="buttons"> </div>
</div>
</body>

</html>

Empty file added visualization/led.js
Empty file.
1 change: 1 addition & 0 deletions visualization/licenses.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
raspberry pico - https://commons.wikimedia.org/wiki/File:Raspberry_Pi_Pico_top.jpg
164 changes: 164 additions & 0 deletions visualization/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
var ws;

i = 0;
ledColors = ["red", "green", "blue", "orange", "pink"];

function getNextColor() {
colorIndex = i++ % ledColors.length;
console.log("Getting: ", colorIndex);

return ledColors[colorIndex];
}

function changeLedState(led, state, color = null) {
svg = led.querySelector("svg");
if (svg) {
const circle = svg.querySelector("#On");
if (circle && !state) {
if (circle.style != null) {
circle.style.display = "none";
}
}
else if(circle) {
if (circle.style != null) {
circle.style.display = "block";
}
}
if (color)
{
console.log(color, " is ")
}
if (circle && color != null)
{
console.log("Setting color: " + color)
const bulb = circle.querySelector("circle");
if (bulb)
{
bulb.style.fill = color;
}
}
}
}

function registerLed(name, state) {
if (name == "led") {
console.log("adding board led handler");
return;
}
console.log("adding user led: ", name)
var ledContainer = document.createElement("div");
ledContainer.className = "ledElement"

var led = document.createElement("div");
fetch("./assets/led.svg")
.then(response => response.text())
.then(svgContent => {
led.innerHTML = svgContent
led.id = name;
led.className = "led";
console.log("Adding led");
changeLedState(led, state, getNextColor());
var ledText = document.createElement("div");
ledText.innerHTML += name;
ledContainer.appendChild(ledText)
ledContainer.appendChild(led)
document.getElementById("leds").appendChild(ledContainer);
})
.catch(error => console.error("Error loading SVG: ", error));
}

function sendMessage(obj) {
ws.send(JSON.stringify(obj));
}

function registerButton(name) {
var buttonsContainer = document.createElement("div");
buttonsContainer.className = "buttonElement";
var button = document.createElement("div");
button.className = "button";
button.id = name;

fetch("./assets/button.svg")
.then(response => response.text())
.then(svgContent => {
button.innerHTML = svgContent;
var buttonText = document.createElement("div");
buttonText.innerHTML += name;
buttonsContainer.appendChild(buttonText);
buttonsContainer.appendChild(button);
document.getElementById("buttons").appendChild(buttonsContainer);
})
.catch(error => console.error("Error loading SVG: ", error));

button.addEventListener('mousedown', function () {
console.log("Button " + name + " pressed");
let message = {
"type": "action",
"target": "button",
"name": name,
"action": "press"
};
sendMessage(message);
});

button.addEventListener('mouseup', function () {
console.log("Button " + name + "released");
let message = {
"type": "action",
"target": "button",
"name": name,
"action": "release"
};
sendMessage(message);
})

document.getElementById("buttons").appendChild(button);
}

function ledStateChange(msg) {
changeLedState(document.getElementById(msg["name"]), msg["state"]);
}

function registerAsset(msg) {
if (msg["peripheral_type"] == "led") {
registerLed(msg["name"], msg["state"]);
}
if (msg["peripheral_type"] == "button") {
registerButton(msg["name"]);
}
}

function processMessage(msg) {
if (msg["msg"] == "register") {
registerAsset(msg);
return;
}
if (msg["msg"] == "state_change") {
ledStateChange(msg);
return;
}
console.log("Got unhandled message: ", msg);
}

window.addEventListener('DOMContentLoaded', (event) => {
let interactive = document.getElementsByClassName("interactive");
ws = new WebSocket("ws://" + location.host + "/ws");

ws.onopen = function () {
console.log("WebSocket is open");
}

ws.onmessage = function (e) {
const obj = JSON.parse(e.data);
processMessage(obj);
}

ws.onclose = function () {
console.log("WebSocket is close")
}

ws.onerror = function (e) {
console.log("WebSocket error:")
console.log(e)
}
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading