Spaces:
Running
Running
| // Import WASM module and functions from npm (published version) | |
| import init, { | |
| connect, | |
| enable_torque, | |
| disable_torque, | |
| forward_kinematics, | |
| inverse_kinematics, | |
| start_fk_stream, | |
| replay_recording, | |
| stop, | |
| // Video Stream (optional - for future use) | |
| connect_video_stream, | |
| disconnect_video_stream, | |
| is_using_camera_fallback, | |
| // Audio Stream (optional - for future use) | |
| connect_audio_stream, | |
| disconnect_audio_stream, | |
| is_using_microphone_fallback, | |
| } from 'https://unpkg.com/reachy-mini@0.6.1/index.js'; | |
| // Elements | |
| const btnConnect = document.getElementById('btn-connect'); | |
| const btnTorqueOn = document.getElementById('btn-torque-on'); | |
| const btnTorqueOff = document.getElementById('btn-torque-off'); | |
| const btnFK = document.getElementById('btn-fk'); | |
| const btnIK = document.getElementById('btn-ik'); | |
| const btnRecord = document.getElementById('btn-record'); | |
| const btnReplay = document.getElementById('btn-replay'); | |
| const btnStop = document.getElementById('btn-stop'); | |
| const statusIndicator = document.getElementById('status-indicator'); | |
| const output = document.getElementById('output'); | |
| let isConnected = false; | |
| // Logging | |
| function log(message, type = 'info') { | |
| console.log(`[${type}] ${message}`); | |
| const line = document.createElement('div'); | |
| line.className = 'output-line'; | |
| const timestamp = new Date().toLocaleTimeString(); | |
| const icon = type === 'success' ? '✓' : type === 'error' ? '✗' : 'ℹ'; | |
| line.textContent = `[${timestamp}] ${icon} ${message}`; | |
| output.appendChild(line); | |
| output.scrollTop = output.scrollHeight; | |
| } | |
| // Initialize WASM | |
| try { | |
| console.log('Initializing WASM...'); | |
| await init(); | |
| log('WASM module loaded successfully', 'success'); | |
| console.log('connect function:', typeof connect); | |
| } catch (err) { | |
| console.error('WASM init error:', err); | |
| log(`Failed to load WASM module: ${err.message}`, 'error'); | |
| log('Make sure you run from a local server: python3 -m http.server 8080', 'error'); | |
| } | |
| // Connection | |
| btnConnect.addEventListener('click', async () => { | |
| console.log('Connect button clicked'); | |
| try { | |
| btnConnect.disabled = true; | |
| btnConnect.textContent = 'Connecting...'; | |
| log('Attempting to connect to Reachy Mini...'); | |
| console.log('Calling connect()...'); | |
| const result = await connect(null); | |
| console.log('Connect result:', result); | |
| isConnected = true; | |
| statusIndicator.textContent = 'Connected'; | |
| statusIndicator.className = 'status status-connected'; | |
| btnConnect.textContent = 'Connected'; | |
| // Enable other buttons | |
| [btnTorqueOn, btnTorqueOff, btnFK, btnIK, btnRecord, btnReplay, btnStop].forEach(btn => { | |
| btn.disabled = false; | |
| }); | |
| log('Successfully connected to Reachy Mini!', 'success'); | |
| } catch (err) { | |
| console.error('Connection error:', err); | |
| btnConnect.disabled = false; | |
| btnConnect.textContent = 'Connect to Robot'; | |
| log(`Connection failed: ${err}`, 'error'); | |
| } | |
| }); | |
| // Torque Control | |
| btnTorqueOn.addEventListener('click', async () => { | |
| try { | |
| log('Enabling torque...'); | |
| await enable_torque(); | |
| log('Torque enabled', 'success'); | |
| } catch (err) { | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| btnTorqueOff.addEventListener('click', async () => { | |
| try { | |
| log('Disabling torque...'); | |
| await disable_torque(); | |
| log('Torque disabled', 'success'); | |
| } catch (err) { | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| // Forward Kinematics | |
| btnFK.addEventListener('click', () => { | |
| try { | |
| log('Testing forward kinematics...'); | |
| const angles = [0, 0, 0, 0, 0, 0]; // 6 head motors at 0 degrees | |
| const pose = forward_kinematics(angles); | |
| log(`Input angles: [${angles.join(', ')}]`); | |
| log(`Output pose [x, y, z, roll, pitch, yaw]: [${[...pose].map(v => v.toFixed(2)).join(', ')}]`, 'success'); | |
| } catch (err) { | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| // Inverse Kinematics | |
| btnIK.addEventListener('click', () => { | |
| try { | |
| log('Testing inverse kinematics...'); | |
| const pose = [0, 0, 0, 0, 0, 0]; // x, y, z, roll, pitch, yaw | |
| const joints = inverse_kinematics(pose); | |
| log(`Input pose [x, y, z, roll, pitch, yaw]: [${pose.join(', ')}]`); | |
| log(`Output joints (degrees): [${[...joints].map(v => v.toFixed(2)).join(', ')}]`, 'success'); | |
| } catch (err) { | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| // Recording | |
| btnRecord.addEventListener('click', async () => { | |
| try { | |
| log('Recording movement for 10 seconds...'); | |
| btnRecord.disabled = true; | |
| await start_fk_stream(10000); // 10 seconds | |
| btnRecord.disabled = false; | |
| log('Recording complete', 'success'); | |
| } catch (err) { | |
| btnRecord.disabled = false; | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| btnReplay.addEventListener('click', async () => { | |
| try { | |
| log('Replaying recorded movement...'); | |
| btnReplay.disabled = true; | |
| await replay_recording(); | |
| btnReplay.disabled = false; | |
| log('Replay complete', 'success'); | |
| } catch (err) { | |
| btnReplay.disabled = false; | |
| log(`Error: ${err}`, 'error'); | |
| } | |
| }); | |
| btnStop.addEventListener('click', async () => { | |
| try { | |
| log('Stopping current operation...'); | |
| await stop(); | |
| log('Stopped', 'success'); | |
| } catch (err) { | |
| log(`Error: ${err.message}`, 'error'); | |
| } | |
| }); | |
| log('Ready to connect. Click "Connect to Robot" to begin.'); | |