cduss Claude Opus 4.5 commited on
Commit
0c2e821
·
1 Parent(s): d3be31f

Add motor control via WebRTC data channel

Browse files

- Enable/Disable Motors buttons in the UI
- Motor status display
- Query motor mode when data channel opens
- Handle motor_mode responses from robot

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +63 -4
index.html CHANGED
@@ -185,8 +185,19 @@
185
 
186
  <!-- Control Panel -->
187
  <div class="card">
188
- <h2>3. Head Control</h2>
189
- <p style="color: #888; font-size: 0.9em;">Send head pose commands via data channel</p>
 
 
 
 
 
 
 
 
 
 
 
190
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
191
  <div>
192
  <label>Yaw (deg):</label>
@@ -238,6 +249,7 @@
238
  window.stopStream = stopStream;
239
  window.sendHeadPose = sendHeadPose;
240
  window.centerHead = centerHead;
 
241
  window.clearLog = clearLog;
242
 
243
  // Initialize on page load
@@ -570,6 +582,8 @@
570
  document.getElementById('stopStreamBtn').disabled = false;
571
  document.getElementById('sendPoseBtn').disabled = false;
572
  document.getElementById('centerBtn').disabled = false;
 
 
573
  } else if (peerConnection.iceConnectionState === 'failed') {
574
  updateWebrtcStatus('disconnected');
575
  log('Connection failed', 'error');
@@ -579,9 +593,27 @@
579
  peerConnection.ondatachannel = (event) => {
580
  log(`Data channel: ${event.channel.label}`, 'success');
581
  dataChannel = event.channel;
582
- dataChannel.onopen = () => log('Data channel open', 'success');
 
 
 
 
583
  dataChannel.onclose = () => log('Data channel closed');
584
- dataChannel.onmessage = (e) => log(`Received: ${e.data}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  };
586
 
587
  log('Requesting session with robot...');
@@ -639,6 +671,9 @@
639
  document.getElementById('stopStreamBtn').disabled = true;
640
  document.getElementById('sendPoseBtn').disabled = true;
641
  document.getElementById('centerBtn').disabled = true;
 
 
 
642
  }
643
 
644
  function updateWebrtcStatus(status) {
@@ -681,6 +716,30 @@
681
  document.getElementById('pitchInput').value = 0;
682
  sendHeadPose();
683
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
684
  </script>
685
  </body>
686
  </html>
 
185
 
186
  <!-- Control Panel -->
187
  <div class="card">
188
+ <h2>3. Motor & Head Control</h2>
189
+
190
+ <p style="color: #888; font-size: 0.9em; margin-bottom: 10px;">Motor control (must enable before moving)</p>
191
+ <div class="controls" style="margin-bottom: 15px;">
192
+ <button id="enableMotorsBtn" onclick="setMotorMode('enabled')" disabled style="background: #00c853;">Enable Motors</button>
193
+ <button id="disableMotorsBtn" onclick="setMotorMode('disabled')" disabled style="background: #ff5252;">Disable Motors</button>
194
+ </div>
195
+ <div style="margin-bottom: 15px;">
196
+ <span>Motors: </span>
197
+ <span id="motorStatus" class="status disconnected">Unknown</span>
198
+ </div>
199
+
200
+ <p style="color: #888; font-size: 0.9em;">Head pose commands</p>
201
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
202
  <div>
203
  <label>Yaw (deg):</label>
 
249
  window.stopStream = stopStream;
250
  window.sendHeadPose = sendHeadPose;
251
  window.centerHead = centerHead;
252
+ window.setMotorMode = setMotorMode;
253
  window.clearLog = clearLog;
254
 
255
  // Initialize on page load
 
582
  document.getElementById('stopStreamBtn').disabled = false;
583
  document.getElementById('sendPoseBtn').disabled = false;
584
  document.getElementById('centerBtn').disabled = false;
585
+ document.getElementById('enableMotorsBtn').disabled = false;
586
+ document.getElementById('disableMotorsBtn').disabled = false;
587
  } else if (peerConnection.iceConnectionState === 'failed') {
588
  updateWebrtcStatus('disconnected');
589
  log('Connection failed', 'error');
 
593
  peerConnection.ondatachannel = (event) => {
594
  log(`Data channel: ${event.channel.label}`, 'success');
595
  dataChannel = event.channel;
596
+ dataChannel.onopen = () => {
597
+ log('Data channel open', 'success');
598
+ // Query motor status when channel opens
599
+ dataChannel.send(JSON.stringify({ get_motor_mode: true }));
600
+ };
601
  dataChannel.onclose = () => log('Data channel closed');
602
+ dataChannel.onmessage = (e) => {
603
+ try {
604
+ const data = JSON.parse(e.data);
605
+ if (data.motor_mode) {
606
+ updateMotorStatus(data.motor_mode);
607
+ log(`Motor mode: ${data.motor_mode}`, 'info');
608
+ } else if (data.error) {
609
+ log(`Error: ${data.error}`, 'error');
610
+ } else {
611
+ log(`Received: ${e.data}`);
612
+ }
613
+ } catch {
614
+ log(`Received: ${e.data}`);
615
+ }
616
+ };
617
  };
618
 
619
  log('Requesting session with robot...');
 
671
  document.getElementById('stopStreamBtn').disabled = true;
672
  document.getElementById('sendPoseBtn').disabled = true;
673
  document.getElementById('centerBtn').disabled = true;
674
+ document.getElementById('enableMotorsBtn').disabled = true;
675
+ document.getElementById('disableMotorsBtn').disabled = true;
676
+ updateMotorStatus('unknown');
677
  }
678
 
679
  function updateWebrtcStatus(status) {
 
716
  document.getElementById('pitchInput').value = 0;
717
  sendHeadPose();
718
  }
719
+
720
+ // Motor Control
721
+ function setMotorMode(mode) {
722
+ if (!dataChannel || dataChannel.readyState !== 'open') {
723
+ log('Data channel not ready', 'error');
724
+ return;
725
+ }
726
+
727
+ dataChannel.send(JSON.stringify({ set_motor_mode: mode }));
728
+ log(`Setting motor mode to: ${mode}`);
729
+ }
730
+
731
+ function updateMotorStatus(mode) {
732
+ const el = document.getElementById('motorStatus');
733
+ if (!el) return;
734
+ el.textContent = mode.charAt(0).toUpperCase() + mode.slice(1);
735
+ if (mode === 'enabled') {
736
+ el.className = 'status connected';
737
+ } else if (mode === 'disabled') {
738
+ el.className = 'status disconnected';
739
+ } else {
740
+ el.className = 'status connecting';
741
+ }
742
+ }
743
  </script>
744
  </body>
745
  </html>