Skip to content

Commit b9d374c

Browse files
authored
Merge pull request #120 from openUC2/mergemaster
Mergemaster
2 parents ed34f8b + 2da99ef commit b9d374c

13 files changed

Lines changed: 1675 additions & 4 deletions
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# CAN OTA Update Module
2+
3+
The CAN OTA (Over-The-Air) update module allows you to remotely update firmware on UC2 CAN satellite devices through WiFi connections.
4+
5+
## Overview
6+
7+
The OTA process consists of these steps:
8+
9+
1. **Send OTA Command**: Tell a CAN device to connect to WiFi and start an OTA server
10+
2. **Device Setup**: The device connects to WiFi and starts ArduinoOTA
11+
3. **Status Response**: Device reports back its IP address and status
12+
4. **Firmware Upload**: Use PlatformIO to upload new firmware wirelessly
13+
14+
## Quick Start
15+
16+
```python
17+
import uc2rest
18+
19+
# Connect to UC2 main controller
20+
ESP32 = uc2rest.UC2Client(serialport="/dev/ttyUSB0")
21+
22+
# Define callback to handle OTA responses
23+
def ota_callback(response):
24+
if response["success"]:
25+
print(f"Device {response['canId']} ready at {response['ip']}")
26+
print(f"Upload with: {ESP32.canota.get_platformio_upload_command(response['canId'])}")
27+
else:
28+
print(f"OTA failed: {response['statusMsg']}")
29+
30+
# Register callback
31+
ESP32.canota.register_callback(0, ota_callback)
32+
33+
# Start OTA on Motor X
34+
ESP32.canota.start_motor_ota("X", "WiFiName", "WiFiPassword")
35+
```
36+
37+
## API Reference
38+
39+
### Main Methods
40+
41+
#### `start_ota_update(can_id, ssid, password, timeout=300000)`
42+
Send OTA command to any CAN device.
43+
44+
**Parameters:**
45+
- `can_id` (int): CAN ID of target device
46+
- `ssid` (str): WiFi network name
47+
- `password` (str): WiFi password
48+
- `timeout` (int): OTA timeout in milliseconds (default: 5 minutes)
49+
50+
**Example:**
51+
```python
52+
ESP32.canota.start_ota_update(20, "MyWiFi", "mypassword")
53+
```
54+
55+
#### Convenience Methods
56+
57+
- `start_motor_ota(axis, ssid, password)` - Motors X/Y/Z (CAN IDs 11/12/13)
58+
- `start_laser_ota(laser_id, ssid, password)` - Laser controllers (CAN ID 20+)
59+
- `start_led_ota(ssid, password)` - LED controller (CAN ID 30)
60+
61+
### Callback System
62+
63+
#### `register_callback(key, callback_function)`
64+
65+
Register functions to handle OTA status updates.
66+
67+
**Callback Function Format:**
68+
```python
69+
def ota_callback(ota_response):
70+
# ota_response contains:
71+
# - canId: CAN ID of device
72+
# - status: 0=success, 1=wifi_failed, 2=ota_failed
73+
# - statusMsg: Human readable message
74+
# - ip: Device IP address (if successful)
75+
# - hostname: Device hostname (e.g., "UC2-CAN-14.local")
76+
# - success: True if status == 0
77+
pass
78+
```
79+
80+
**Callback Keys:**
81+
- `0`: General OTA events (recommended for most use cases)
82+
- `1-9`: Device-specific callbacks (automatically mapped from CAN ID)
83+
84+
### Utility Methods
85+
86+
#### `get_ota_hostname(can_id)`
87+
Get expected hostname for a device in OTA mode.
88+
89+
```python
90+
hostname = ESP32.canota.get_ota_hostname(20) # Returns "UC2-CAN-14.local"
91+
```
92+
93+
#### `get_platformio_upload_command(can_id)`
94+
Generate PlatformIO upload command for OTA firmware update.
95+
96+
```python
97+
cmd = ESP32.canota.get_platformio_upload_command(20)
98+
# Returns: "platformio run -t upload --upload-port UC2-CAN-14.local"
99+
```
100+
101+
## Device CAN ID Reference
102+
103+
| Device Type | Axis/ID | CAN ID | Convenience Method |
104+
|-------------|---------|--------|-------------------|
105+
| Motor X | X | 11 | `start_motor_ota("X", ...)` |
106+
| Motor Y | Y | 12 | `start_motor_ota("Y", ...)` |
107+
| Motor Z | Z | 13 | `start_motor_ota("Z", ...)` |
108+
| Laser | 0 | 20 | `start_laser_ota(0, ...)` |
109+
| LED Controller | - | 30 | `start_led_ota(...)` |
110+
111+
## Status Codes
112+
113+
- **0**: Success - Device connected to WiFi and OTA server started
114+
- **1**: WiFi connection failed - Check SSID/password
115+
- **2**: OTA start failed - Device error
116+
117+
## Complete Example
118+
119+
```python
120+
import uc2rest
121+
import time
122+
123+
# Connect to UC2
124+
ESP32 = uc2rest.UC2Client(serialport="/dev/ttyUSB0", DEBUG=True)
125+
126+
# Setup callback
127+
def handle_ota(response):
128+
device_id = response["canId"]
129+
130+
if response["success"]:
131+
print(f"✅ Device {device_id} ready!")
132+
print(f" IP: {response['ip']}")
133+
print(f" Hostname: {response['hostname']}")
134+
135+
# Generate upload command
136+
upload_cmd = ESP32.canota.get_platformio_upload_command(device_id)
137+
print(f" Upload: {upload_cmd}")
138+
139+
else:
140+
print(f"❌ Device {device_id} failed: {response['statusMsg']}")
141+
142+
ESP32.canota.register_callback(0, handle_ota)
143+
144+
# Start OTA on multiple devices
145+
devices = [
146+
("Motor X", lambda: ESP32.canota.start_motor_ota("X", "WiFi", "pass")),
147+
("Motor Y", lambda: ESP32.canota.start_motor_ota("Y", "WiFi", "pass")),
148+
("Laser", lambda: ESP32.canota.start_laser_ota(0, "WiFi", "pass")),
149+
("LED", lambda: ESP32.canota.start_led_ota("WiFi", "pass"))
150+
]
151+
152+
for name, start_func in devices:
153+
print(f"Starting OTA for {name}...")
154+
start_func()
155+
time.sleep(2) # Small delay between commands
156+
157+
# Wait for responses
158+
print("Waiting for OTA responses...")
159+
time.sleep(30)
160+
```
161+
162+
## Firmware Upload
163+
164+
After a device reports successful OTA setup:
165+
166+
1. **Verify connection**: Ping the device hostname
167+
```bash
168+
ping UC2-CAN-14.local
169+
```
170+
171+
2. **Upload firmware**: Use the generated PlatformIO command
172+
```bash
173+
platformio run -t upload --upload-port UC2-CAN-14.local
174+
```
175+
176+
3. **Monitor upload**: Watch for upload progress and completion
177+
178+
## Error Handling
179+
180+
Common issues and solutions:
181+
182+
- **WiFi connection failed**: Check SSID/password, ensure network is reachable
183+
- **OTA start failed**: Device may be busy, try again after a few seconds
184+
- **Timeout**: Increase timeout parameter or check device connectivity
185+
- **No response**: Verify CAN device is connected and responding to CAN commands
186+
187+
## Integration with Existing Code
188+
189+
The CAN OTA module integrates seamlessly with existing UC2 workflows:
190+
191+
```python
192+
# Normal motor operation
193+
ESP32.motor.move_x(steps=1000, speed=1000)
194+
195+
# Update motor firmware via OTA
196+
ESP32.canota.start_motor_ota("X", "WiFi", "password")
197+
198+
# Continue with normal operation after update
199+
ESP32.motor.move_x(steps=-1000, speed=1000)
200+
```

DOCUMENTATION/HOME_XY_Usage.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Home XY - Multi-Motor Homing
2+
3+
The `home_xy` function allows you to home multiple motors simultaneously, which is more efficient than homing them individually.
4+
5+
## Basic Usage
6+
7+
```python
8+
import uc2rest
9+
10+
# Connect to UC2 device
11+
ESP32 = uc2rest.UC2Client(serialport="/dev/cu.SLAB_USBtoUART", baudrate=115200)
12+
13+
# Home X and Y axes simultaneously
14+
ESP32.home.home_xy(
15+
axes=["X", "Y"],
16+
speeds=[15000, 15000],
17+
directions=[1, -1],
18+
timeouts=[200, 400],
19+
isBlocking=True
20+
)
21+
```
22+
23+
## Function Parameters
24+
25+
- **axes**: List of axes to home (e.g., `["X", "Y"]` or `[1, 2]`)
26+
- **speeds**: List of speeds for each axis or single speed for all
27+
- **directions**: List of directions (-1 or 1) for each axis or single direction for all
28+
- **endposreleases**: List of endpos release values or single value for all
29+
- **endstoppolarities**: List of endstop polarities or single value for all
30+
- **timeouts**: List of timeouts (ms) for each axis or single timeout for all
31+
- **isBlocking**: If True, wait for all motors to finish homing
32+
- **preMove**: If True, move away from endstop before homing
33+
34+
## Generated JSON Command
35+
36+
The function generates a command like this:
37+
38+
```json
39+
{
40+
"task": "/home_act",
41+
"home": {
42+
"steppers": [
43+
{"stepperid": 2, "timeout": 400, "speed": 15000, "direction": -1, "endstoppolarity": 1},
44+
{"stepperid": 1, "timeout": 200, "speed": 15000, "direction": 1, "endstoppolarity": 1}
45+
]
46+
}
47+
}
48+
```
49+
50+
## Axis Mapping
51+
52+
- **"X"** or **1**: X-axis motor
53+
- **"Y"** or **2**: Y-axis motor
54+
- **"Z"** or **3**: Z-axis motor
55+
- **"A"** or **0**: A-axis motor
56+
57+
## Response Handling
58+
59+
The function expects multiple responses:
60+
1. One acknowledgment response when the command is received
61+
2. One response per motor when each motor completes homing
62+
63+
## Callbacks
64+
65+
You can register a callback to monitor homing status:
66+
67+
```python
68+
def home_callback(is_homed):
69+
motor_names = ["A", "X", "Y", "Z"]
70+
for i, homed in enumerate(is_homed):
71+
status = "HOMED" if homed else "NOT HOMED"
72+
print(f"Motor {motor_names[i]}: {status}")
73+
74+
ESP32.home.register_callback(0, home_callback)
75+
```
76+
77+
## Examples
78+
79+
### Home X and Y with different parameters
80+
```python
81+
ESP32.home.home_xy(
82+
axes=["X", "Y"],
83+
speeds=[15000, 12000], # Different speeds
84+
directions=[1, -1], # X positive, Y negative
85+
timeouts=[200, 400] # Different timeouts
86+
)
87+
```
88+
89+
### Home all axes with same parameters
90+
```python
91+
ESP32.home.home_xy(
92+
axes=["X", "Y", "Z"],
93+
speeds=10000, # Same speed for all
94+
directions=-1, # All negative direction
95+
timeouts=5000 # Same timeout for all
96+
)
97+
```
98+
99+
### Using stepper IDs directly
100+
```python
101+
ESP32.home.home_xy(
102+
axes=[1, 2], # Stepper IDs 1 and 2
103+
speeds=[15000, 15000],
104+
directions=[1, -1],
105+
timeouts=[200, 400]
106+
)
107+
```
108+
109+
## Advantages
110+
111+
- **Efficiency**: Home multiple motors in parallel instead of sequentially
112+
- **Time saving**: Reduces total homing time significantly
113+
- **Flexibility**: Different parameters for each motor
114+
- **Consistency**: Uses internal parameter defaults when not specified

0 commit comments

Comments
 (0)