Skip to content

Commit 2d7339c

Browse files
Switched the app to just send the full diagram.json from Wokwi and let the onboard parser handle it (so colors will sync from your project), queries whether an Arduino is actually plugged into the jumperless (by checking if the RESET pins are pulled), fixed the saved projects file not being found, quickened detection / flashing / exit stuff
1 parent c1ce2bc commit 2d7339c

8 files changed

Lines changed: 1419 additions & 248 deletions
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Arduino Detection and Input Handling Fixes
2+
3+
## Issues Fixed
4+
5+
### 1. Arduino Presence Detection Not Working
6+
7+
**Problem:** App reported "Arduino not detected" even when Jumperless responded with `Y,Y`
8+
9+
**Root Causes:**
10+
- Response was getting mixed with other serial output (menu text, debug messages)
11+
- Timeout was too short (200ms)
12+
- Parsing expected exact "Y,Y" format with no whitespace or extra text
13+
14+
**Solution:**
15+
```python
16+
# 1. Flush serial buffer before checking
17+
if ser.in_waiting > 0:
18+
ser.read(ser.in_waiting)
19+
time.sleep(0.05)
20+
21+
# 2. Longer timeout
22+
time.sleep(0.3) # Increased from 0.2s
23+
24+
# 3. Read more data
25+
response = ser.read(ser.in_waiting or 100) # Read up to 100 bytes
26+
27+
# 4. Parse line-by-line to find Y,Y even in messy output
28+
lines = response.split('\n')
29+
for line in lines:
30+
if ',' in line and len(line) <= 10:
31+
parts = line.split(',')
32+
if len(parts) == 2:
33+
is_connected = 'Y' in parts[0].upper()
34+
is_present = 'Y' in parts[1].upper()
35+
return (is_connected, is_present)
36+
```
37+
38+
### 2. User Input Not Accepted on macOS
39+
40+
**Problem:** Prompt "Choice (Y/N/A/S):" appeared but input was ignored
41+
42+
**Root Causes:**
43+
- `select.select()` doesn't work in bundled macOS apps or GUI contexts
44+
- stdin might not be a proper file descriptor in packaged apps
45+
- Signal-based approaches (SIGALRM) can conflict with GUI event loops
46+
47+
**Solution:** Use Queue-based threading
48+
```python
49+
import queue
50+
51+
input_queue = queue.Queue()
52+
53+
def get_input():
54+
try:
55+
result = input().strip().upper()
56+
input_queue.put(result)
57+
except Exception:
58+
input_queue.put(None)
59+
60+
input_thread = threading.Thread(target=get_input, daemon=True)
61+
input_thread.start()
62+
63+
# Wait up to 10 seconds for input
64+
try:
65+
user_input = input_queue.get(timeout=10)
66+
except queue.Empty:
67+
user_input = None # Timeout
68+
```
69+
70+
**Why this works:**
71+
- ✅ Works in GUI apps and bundled executables
72+
- ✅ Works on all platforms (Windows, macOS, Linux)
73+
- ✅ Proper timeout handling without signals
74+
- ✅ Thread-safe with daemon thread
75+
- ✅ No external dependencies
76+
77+
### 3. Added Debug Output
78+
79+
**Enhancement:** Always show what input was received
80+
```python
81+
safe_print(f"Received input: '{user_input}' (type: {type(user_input).__name__})", Fore.CYAN)
82+
```
83+
84+
This helps diagnose:
85+
- Whether input is being captured
86+
- If there's a type mismatch (str vs bytes)
87+
- If there are hidden characters
88+
89+
## Testing
90+
91+
After these fixes, the Arduino flash flow should work properly:
92+
93+
```
94+
New Arduino sketch for slot 0 - flashing...
95+
Arduino flash started in background for slot 0...
96+
⚠️ Arduino not detected on Jumperless
97+
No Arduino detected. Do you want to:
98+
[Y] Flash anyway (maybe Arduino is connected differently)
99+
[N] Skip this flash
100+
[A] Always flash without asking
101+
[S] Never flash when Arduino not detected
102+
Choice (Y/N/A/S): S
103+
Received input: 'S' (type: str)
104+
✓ Will skip flashing when Arduino not present
105+
```
106+
107+
**If Arduino IS detected:**
108+
```
109+
New Arduino sketch for slot 0 - flashing...
110+
Arduino flash started in background for slot 0...
111+
Arduino detected and ready
112+
Compiling Arduino sketch...
113+
[flash proceeds normally]
114+
```
115+
116+
## Files Modified
117+
118+
- `JumperlessWokwiBridge.py` - Fixed `check_arduino_presence()` and user input handling
119+
120+
## Related Issues
121+
122+
- macOS bundled apps have limited stdin/stdout access
123+
- GUI frameworks can interfere with signal handlers
124+
- Queue-based threading is the most portable solution
125+
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Arduino Presence Detection and Input Handling Fix
2+
3+
## Issues Fixed
4+
5+
### 1. Arduino Presence Detection Failure
6+
**Problem:** The `check_arduino_presence()` function was failing to detect Arduino even when "Y,Y" response was sent by the Jumperless. The function would always return `(False, False)`, causing unnecessary prompts.
7+
8+
**Root Cause:** The `serial_term_in()` thread continuously reads from the serial port. When `check_arduino_presence()` sent "A?" and waited for response, the `serial_term_in()` thread would consume the "Y,Y" response before `check_arduino_presence()` could read it.
9+
10+
**Solution:** Wrapped the serial communication in `check_arduino_presence()` with `serial_lock`:
11+
```python
12+
with serial_lock:
13+
# Flush, write, wait, and read - all protected from other threads
14+
ser.write(b"A?")
15+
time.sleep(0.4)
16+
response = ser.read(ser.in_waiting or 100).decode('utf-8', errors='ignore')
17+
```
18+
19+
This ensures `serial_term_in()` cannot read the response while `check_arduino_presence()` is waiting for it.
20+
21+
### 2. Input Prompt Issues
22+
**Problem:**
23+
- User had to press Enter twice to submit input
24+
- Input "N" was interpreted as "Y" (wrong response)
25+
26+
**Root Cause:** The `handle_interactive_input_simple()` function (called by `serial_term_out()` thread) was running with terminal in raw mode when `interactive_mode = True`. This caused:
27+
1. The interactive handler to consume the first Enter keypress
28+
2. The `input()` call to get an empty string (from buffered data or partial read)
29+
3. Empty string being treated as "Y" by the logic `if user_input == 'Y' or user_input == ''`
30+
31+
**Solution:** Before calling `input()`, temporarily disable interactive mode and restore terminal settings:
32+
```python
33+
old_interactive_state = interactive_mode
34+
if interactive_mode:
35+
interactive_mode = False
36+
time.sleep(0.1) # Let serial_term_out exit interactive handler
37+
38+
# Restore normal terminal settings
39+
if sys.platform != "win32" and 'original_terminal_settings' in globals():
40+
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, original_terminal_settings)
41+
42+
# Flush input buffer
43+
sys.stdin.flush()
44+
45+
# Now input() works correctly
46+
user_input = input().strip().upper()
47+
48+
# Restore states
49+
interactive_mode = old_interactive_state
50+
```
51+
52+
### 3. Debug Output Added
53+
Added clear status messages so users can see what's happening:
54+
```python
55+
safe_print(f"Arduino detection: connected={is_connected}, present={is_present}", Fore.CYAN)
56+
```
57+
58+
When Arduino is detected, it now clearly states:
59+
```
60+
Arduino detection: connected=True, present=True
61+
✓ Arduino detected and ready - proceeding with flash
62+
```
63+
64+
## Testing
65+
Test the fixes by:
66+
1. Loading a Wokwi project with Arduino sketch
67+
2. Verify you see "Arduino detection: connected=True, present=True"
68+
3. Verify flash proceeds without prompt when Arduino is present
69+
4. Disconnect Arduino and try again
70+
5. Verify prompt appears and accepts input correctly (single Enter press)
71+
6. Verify "N" actually skips the flash
72+
73+
## Technical Details
74+
75+
### Serial Lock Usage
76+
The `serial_lock` is a threading.Lock that prevents concurrent access to the serial port. It's used throughout the codebase but was missing from `check_arduino_presence()`.
77+
78+
### Interactive Mode
79+
Interactive mode is used for character-by-character input (like a terminal emulator). When enabled:
80+
- Terminal is set to raw mode (no line buffering)
81+
- `serial_term_out()` calls `handle_interactive_input()` which reads stdin character-by-character
82+
- Must be disabled for `input()` to work correctly
83+
84+
### Thread Safety
85+
Multiple threads interact with serial and stdin:
86+
- `serial_term_in()` - reads from serial port
87+
- `serial_term_out()` - reads from stdin, writes to serial
88+
- `handle_interactive_input_simple()` - character-by-character stdin reading
89+
- Main thread - calls `input()` for prompts
90+
91+
Proper coordination using locks and state flags is essential.
92+
93+
### 4. Empty Sketch Detection
94+
**Problem:** The system would attempt to flash empty Arduino sketches (containing only empty `setup()` and `loop()` functions), wasting time and resources.
95+
96+
**Solution:** Added validation to detect empty sketches by:
97+
1. Removing comments and whitespace from the sketch
98+
2. Checking if only `void setup(){}` and `void loop(){}` remain
99+
3. Returning success without flashing if sketch is empty
100+
101+
```python
102+
# Strip comments and whitespace
103+
stripped_sketch = re.sub(r'//.*?$|/\*.*?\*/', '', sketch_content, flags=re.MULTILINE | re.DOTALL)
104+
stripped_sketch = re.sub(r'\s+', '', stripped_sketch)
105+
106+
# Check for empty pattern
107+
if 'voidsetup(){}' in stripped_sketch and 'voidloop(){}' in stripped_sketch:
108+
if stripped_sketch.count('{') <= 2: # Only setup and loop braces
109+
return True # Skip flash, not an error
110+
```
111+
112+
### 5. Variable Scope Bug Fix
113+
**Problem:** Exception handler referenced `old_menu_state` and `old_interactive_state` before they were assigned, causing "local variable referenced before assignment" error.
114+
115+
**Solution:** Initialize these variables to `None` before the try block:
116+
```python
117+
old_menu_state = None
118+
old_interactive_state = None
119+
120+
try:
121+
old_menu_state = menuEntered
122+
old_interactive_state = interactive_mode
123+
# ... rest of code
124+
except Exception as e:
125+
# Safe to reference old_menu_state and old_interactive_state here
126+
if old_menu_state is not None:
127+
menuEntered = old_menu_state
128+
```
129+
130+
## Files Modified
131+
- `JumperlessWokwiBridge.py` - check_arduino_presence() and flash_arduino_sketch()
132+
133+
## Related Issues
134+
This fix resolves the issues where:
135+
- Arduino flash would prompt even when Arduino was connected
136+
- User input wasn't properly received during flash prompts
137+
- Double Enter key press was needed for input confirmation
138+
- "local variable referenced before assignment" error in exception handler
139+
- Empty Arduino sketches would trigger unnecessary flash operations
140+

0 commit comments

Comments
 (0)