Skip to content

Commit 9b597f0

Browse files
Add Debug Script to simulator menu with dynamic menu abstraction
- Debug Script loads main.py from GitHub raw URL - Debug challenge always fetches fresh code (no localStorage cache) - Abstract Challenge Simulator menu to single source in challenges.js - Both simulator.html and docs.html use Challenges.populateMenu() - Updated main.py to use hold_state instead of time.sleep - Removed def main() wrapper for Skulpt compatibility - Fixed ultrasonic warning system in aidriver.py
1 parent 7d325f1 commit 9b597f0

8 files changed

Lines changed: 172 additions & 219 deletions

File tree

_Firmware/AI_Driver_RP2040.uf2

0 Bytes
Binary file not shown.

_Firmware/build_info.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Build Date: Sat Jan 10 02:43:16 UTC 2026
1+
Build Date: Sat Jan 10 03:23:12 UTC 2026
22
MicroPython Version: v1.28.0-preview-62-g0fd084363 (0fd084363, 2026-01-09)
33
Custom Modules: 3 files
44
Build Host: codespaces-35b014

app/docs.html

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -233,60 +233,18 @@
233233
<a
234234
class="nav-link dropdown-toggle"
235235
href="#"
236-
id="simulatorDropdown"
236+
id="challengeDropdown"
237237
role="button"
238238
data-bs-toggle="dropdown"
239239
>
240240
<i class="bi bi-play-circle me-1"></i>Challenge Simulator
241241
</a>
242-
<ul class="dropdown-menu" aria-labelledby="simulatorDropdown">
243-
<li>
244-
<a class="dropdown-item" href="simulator.html?challenge=0">
245-
<i class="bi bi-wrench me-2"></i>Challenge 0: Fix the Code
246-
</a>
247-
</li>
248-
<li>
249-
<a class="dropdown-item" href="simulator.html?challenge=1">
250-
<i class="bi bi-arrow-up me-2"></i>Challenge 1: Straight
251-
Line
252-
</a>
253-
</li>
254-
<li>
255-
<a class="dropdown-item" href="simulator.html?challenge=2">
256-
<i class="bi bi-circle me-2"></i>Challenge 2: Drive a Circle
257-
</a>
258-
</li>
259-
<li>
260-
<a class="dropdown-item" href="simulator.html?challenge=3">
261-
<i class="bi bi-rulers me-2"></i>Challenge 3: Distance
262-
Sensor
263-
</a>
264-
</li>
265-
<li>
266-
<a class="dropdown-item" href="simulator.html?challenge=4">
267-
<i class="bi bi-square me-2"></i>Challenge 4: Drive a Square
268-
</a>
269-
</li>
270-
<li>
271-
<a class="dropdown-item" href="simulator.html?challenge=5">
272-
<i class="bi bi-exclamation-triangle me-2"></i>Challenge 5:
273-
Obstacle Avoidance
274-
</a>
275-
</li>
276-
<li><hr class="dropdown-divider" /></li>
277-
<li>
278-
<a class="dropdown-item" href="simulator.html?challenge=6">
279-
<i class="bi bi-signpost-split me-2"></i>Challenge 6: Maze
280-
Navigation
281-
</a>
282-
</li>
283-
<li>
284-
<a class="dropdown-item" href="simulator.html?challenge=7">
285-
<i class="bi bi-controller me-2"></i>Challenge 7: Gamepad
286-
Control
287-
</a>
288-
</li>
289-
</ul>
242+
<!-- Menu populated dynamically from Challenges.populateMenu() -->
243+
<ul
244+
class="dropdown-menu"
245+
id="challengeSimulatorMenu"
246+
aria-labelledby="challengeDropdown"
247+
></ul>
290248
</li>
291249

292250
<!-- Downloads Dropdown -->
@@ -499,5 +457,13 @@
499457
loadMarkdown(getDocFromUrl());
500458
});
501459
</script>
460+
<!-- Challenges module for dynamic menu -->
461+
<script src="js/challenges.js?v=14"></script>
462+
<script>
463+
// Populate the challenge simulator menu after challenges.js loads
464+
if (typeof Challenges !== "undefined") {
465+
Challenges.populateMenu("#challengeSimulatorMenu", "simulator");
466+
}
467+
</script>
502468
</body>
503469
</html>

app/js/app.js

Lines changed: 39 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,18 @@ function loadChallenge(challengeId) {
479479
// Clear any existing error markers
480480
Editor.clearAllMarkers();
481481

482-
// Try to load saved code, otherwise load starter code
483-
const savedCode = Editor.loadSavedCode(challengeId);
484-
if (savedCode) {
485-
Editor.setCode(savedCode);
486-
logDebug(`Loaded saved code for Challenge ${challengeId}`);
487-
} else {
482+
// Debug challenge always loads fresh from main.py (no saved code)
483+
// Other challenges try saved code first, then starter code
484+
if (challengeId === "debug") {
488485
loadStarterCode(challengeId);
486+
} else {
487+
const savedCode = Editor.loadSavedCode(challengeId);
488+
if (savedCode) {
489+
Editor.setCode(savedCode);
490+
logDebug(`Loaded saved code for Challenge ${challengeId}`);
491+
} else {
492+
loadStarterCode(challengeId);
493+
}
489494
}
490495

491496
// Store current challenge config
@@ -560,8 +565,31 @@ function loadChallenge(challengeId) {
560565

561566
/**
562567
* Load starter code for a challenge
568+
* For debug challenge, fetches from project/main.py
563569
*/
564-
function loadStarterCode(challengeId) {
570+
async function loadStarterCode(challengeId) {
571+
// Debug challenge loads from project/main.py via GitHub raw
572+
if (challengeId === "debug") {
573+
try {
574+
const response = await fetch(
575+
"https://raw.githubusercontent.com/TempeHS/AIDriver_MicroPython_Challanges/refs/heads/main/project/main.py"
576+
);
577+
if (response.ok) {
578+
const code = await response.text();
579+
Editor.setCode(code);
580+
logDebug("[App] Loaded starter code from project/main.py");
581+
return;
582+
}
583+
} catch (err) {
584+
console.warn("[App] Could not fetch project/main.py:", err);
585+
}
586+
// Fallback if fetch fails
587+
Editor.setCode(
588+
"# Could not load project/main.py\n# Check your internet connection\n"
589+
);
590+
return;
591+
}
592+
565593
const starterCodes = getStarterCodes();
566594
const code = starterCodes[challengeId] || "# No starter code available\n";
567595
Editor.setCode(code);
@@ -571,9 +599,9 @@ function loadStarterCode(challengeId) {
571599
/**
572600
* Reset to starter code (discard changes)
573601
*/
574-
function resetToStarterCode() {
602+
async function resetToStarterCode() {
575603
Editor.clearSavedCode(App.currentChallenge);
576-
loadStarterCode(App.currentChallenge);
604+
await loadStarterCode(App.currentChallenge);
577605
logDebug("[App] Code reset to starter code");
578606
}
579607

@@ -1853,60 +1881,11 @@ function hideLoading() {
18531881

18541882
/**
18551883
* Get starter codes for all challenges
1884+
* Note: Debug challenge code is fetched from project/main.py in loadStarterCode()
18561885
*/
18571886
function getStarterCodes() {
18581887
return {
1859-
debug: `# Debug Script: Hardware Sanity Test
1860-
# Runs a sequence of movements and distance readings
1861-
# This mirrors project/main.py for testing on real hardware
1862-
1863-
from aidriver import AIDriver, hold_state
1864-
import aidriver
1865-
1866-
aidriver.DEBUG_AIDRIVER = True
1867-
1868-
print("Initialising AIDriver hardware test...")
1869-
my_robot = AIDriver()
1870-
1871-
print("Starting tests in 3 seconds. Ensure clear space around the robot.")
1872-
hold_state(3)
1873-
1874-
# Test 1: Drive Forward
1875-
print("Test 1: drive_forward")
1876-
my_robot.drive_forward(200, 200)
1877-
hold_state(2)
1878-
my_robot.brake()
1879-
hold_state(1)
1880-
1881-
# Test 2: Drive Backward
1882-
print("Test 2: drive_backward")
1883-
my_robot.drive_backward(200, 200)
1884-
hold_state(2)
1885-
my_robot.brake()
1886-
hold_state(1)
1887-
1888-
# Test 3: Rotate Right
1889-
print("Test 3: rotate_right")
1890-
my_robot.rotate_right(200)
1891-
hold_state(2)
1892-
my_robot.brake()
1893-
hold_state(1)
1894-
1895-
# Test 4: Rotate Left
1896-
print("Test 4: rotate_left")
1897-
my_robot.rotate_left(200)
1898-
hold_state(2)
1899-
my_robot.brake()
1900-
hold_state(1)
1901-
1902-
# Test 5: Ultrasonic Sensor
1903-
print("Test 5: ultrasonic distance readings")
1904-
for i in range(5):
1905-
distance = my_robot.read_distance()
1906-
hold_state(0.5)
1907-
1908-
print("All hardware tests completed.")
1909-
`,
1888+
// debug: loaded dynamically from project/main.py
19101889
0: `# Challenge 0: Fix the Code
19111890
# This code has errors - can you find and fix them?
19121891

app/js/python-runner.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,15 @@ const PythonRunner = {
6060
// Initialize builtin files
6161
Sk.builtinFiles = Sk.builtinFiles || { files: {} };
6262

63-
// Add aidriver module as pure Python with JS interop
64-
Sk.builtinFiles["files"]["src/lib/aidriver.py"] =
65-
this.getAIDriverPythonModule();
63+
// Register aidriver as a JavaScript module (for proper suspension support)
64+
// This uses AIDriverStub.getModule() which has promiseToSuspension for hold_state
65+
Sk.externalLibraries = Sk.externalLibraries || {};
66+
Sk.externalLibraries["aidriver"] = {
67+
path: "virtual://aidriver",
68+
dependencies: [],
69+
};
6670

67-
console.log("[PythonRunner] Registered aidriver Python module");
71+
console.log("[PythonRunner] Registered aidriver JS module");
6872
},
6973

7074
/**
@@ -444,7 +448,7 @@ def ticks_diff(t1, t2):
444448
handleRead(filename) {
445449
console.log("[PythonRunner] handleRead called for:", filename);
446450

447-
// Handle aidriver module - check various possible paths
451+
// Handle aidriver module - return the Python module
448452
if (
449453
filename === "src/lib/aidriver.py" ||
450454
filename === "./aidriver.py" ||

app/simulator.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,8 @@ <h5 class="modal-title">
631631
<script src="js/mazes.js?v=25"></script>
632632
<script src="js/gamepad.js?v=10"></script>
633633
<script src="js/aidriver-stub.js?v=10"></script>
634-
<script src="js/python-runner.js?v=42"></script>
634+
<script src="js/python-runner.js?v=43"></script>
635635
<script src="js/editor.js?v=13"></script>
636-
<script src="js/app.js?v=45"></script>
636+
<script src="js/app.js?v=48"></script>
637637
</body>
638638
</html>

docs/Assembly_Instructions.md

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -76,52 +76,64 @@
7676
> To avoid damaging your computer or robot place it on the floor in an area with enough space for it to move safely before powering it on.
7777
7878
```python
79-
from time import sleep
79+
from aidriver import AIDriver, hold_state
8080
import aidriver
81-
from aidriver import AIDriver
82-
import sys
8381

84-
"""Hardware test for the AIDriver robot.
85-
Relies on aidriver.DEBUG_AIDRIVER for detailed motor/sensor logs.
82+
"""Hardware sanity test for the AIDriver robot.
83+
84+
Runs a short sequence of movements and distance readings.
85+
Most details are reported via the AIDriver debug logger.
8686
"""
8787

88-
aidriver.DEBUG_AIDRIVER = True
89-
90-
try:
91-
driver = AIDriver()
92-
except Exception as e:
93-
# Minimal error reporting in case the library is missing or broken
94-
print("AIDriver init failed:", e)
95-
sys.exit()
96-
97-
sleep(5)
98-
99-
# Test 1: Drive Forward
100-
driver.drive_forward(200, 200)
101-
sleep(2)
102-
driver.brake()
103-
sleep(3)
104-
105-
# Test 2: Drive Backward
106-
driver.drive_backward(200, 200)
107-
sleep(2)
108-
driver.brake()
109-
sleep(3)
110-
111-
# Test 3: Rotate Right
112-
driver.rotate_right(200)
113-
sleep(2)
114-
driver.brake()
115-
sleep(3)
116-
117-
# Test 4: Rotate Left
118-
driver.rotate_left(200)
119-
sleep(2)
120-
driver.brake()
121-
sleep(3)
122-
123-
# Test 5: Ultrasonic Sensor
124-
for _ in range(5):
125-
driver.read_distance()
126-
sleep(1)
88+
89+
def main():
90+
aidriver.DEBUG_AIDRIVER = True
91+
92+
print("Initialising AIDriver hardware test...")
93+
94+
try:
95+
robot = AIDriver()
96+
except Exception as exc:
97+
print("Failed to initialise AIDriver:", exc)
98+
print("Check that 'aidriver.py' is in the 'lib' folder on the device.")
99+
return
100+
101+
print("Starting tests in 3 seconds. Ensure clear space around the robot.")
102+
hold_state(3)
103+
104+
# Test 1: Drive Forward
105+
print("Test 1: drive_forward")
106+
robot.drive_forward(200, 200)
107+
hold_state(2)
108+
robot.brake()
109+
hold_state(1)
110+
111+
# Test 2: Drive Backward
112+
print("Test 2: drive_backward")
113+
robot.drive_backward(200, 200)
114+
hold_state(2)
115+
robot.brake()
116+
hold_state(1)
117+
118+
# Test 3: Rotate Right
119+
print("Test 3: rotate_right")
120+
robot.rotate_right(200)
121+
hold_state(2)
122+
robot.brake()
123+
hold_state(1)
124+
125+
# Test 4: Rotate Left
126+
print("Test 4: rotate_left")
127+
robot.rotate_left(200)
128+
hold_state(2)
129+
robot.brake()
130+
hold_state(1)
131+
132+
# Test 5: Ultrasonic Sensor
133+
print("Test 5: ultrasonic distance readings")
134+
for i in range(5):
135+
distance = robot.read_distance()
136+
hold_state(0.5)
137+
138+
print("All hardware tests completed.")
127139
```

0 commit comments

Comments
 (0)