|
| 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 | +``` |
0 commit comments