implemented minimal config (motors) updating

node_mode
Jake 2025-10-31 01:43:11 +08:00
parent 0fa8e04493
commit 9036014e21
4 changed files with 232 additions and 115 deletions

View File

@ -112,6 +112,12 @@ const DataMap = Object.freeze({
export function getModelType(major, minor) { export function getModelType(major, minor) {
const id = combine(major, minor);
return modelList.get(id) || "Unknown Model";
}
const modelList = new Map([ const modelList = new Map([
[combine(5, 0), "SCSXX"], [combine(5, 0), "SCSXX"],
[combine(5, 4), "SCS009"], [combine(5, 4), "SCS009"],
@ -173,8 +179,11 @@ export function getModelType(major, minor) {
[combine(9, 40), "SCS40-2"] [combine(9, 40), "SCS40-2"]
]); ]);
const id = combine(major, minor); export const reverseModelMap = new Map();
return modelList.get(id) || "Unknown Model"; for (const [combined, model] of modelList.entries()) {
const minor = combined >> 8;
const major = combined & 0xFF;
reverseModelMap.set(model, { major, minor });
} }
function combine(major, minor) { function combine(major, minor) {

View File

@ -50,16 +50,19 @@
<div class="channel-box mb-5"> <div class="channel-box mb-5">
<label class="form-label">Channel 0</label> <label class="form-label">Channel 0</label>
<div class="row mb-2"> <div class="row mb-2">
<div class="col-9"> <!-- <div class="col-9">
<select class="form-select"> <select class="form-select">
<option selected>SCS</option> <option selected>SCS</option>
<option>STS</option> <option>STS</option>
<option>SM</option> <option>SM</option>
</select> </select>
</div> </div> -->
<div class="col-3"> <div class="col-3">
<button id="btn_scan_channel_0" class="btn btn-primary w-100">Scan</button> <button id="btn_scan_channel_0" class="btn btn-primary w-100">Scan</button>
</div> </div>
<div class="col-3">
<button id="btn_apply_config_channel_0" class="btn btn-primary w-100">Apply To Config</button>
</div>
</div> </div>
<table id="channel0-motor-table" class="table table-bordered"> <table id="channel0-motor-table" class="table table-bordered">
<thead> <thead>
@ -85,6 +88,7 @@
<th data-key="MOVING">MOVING</th> <th data-key="MOVING">MOVING</th>
<th data-key="CURRENT_CURRENT">CURRENT</th> <th data-key="CURRENT_CURRENT">CURRENT</th>
<th data-key="VOLTAGE">VOLTAGE</th> <th data-key="VOLTAGE">VOLTAGE</th>
<th data-key="NAME">NAME</th>
</tr> </tr>
</thead> </thead>
@ -97,16 +101,19 @@
<div class="channel-box mb-5"> <div class="channel-box mb-5">
<label class="form-label">Channel 1</label> <label class="form-label">Channel 1</label>
<div class="row mb-2"> <div class="row mb-2">
<div class="col-9"> <!-- <div class="col-9">
<select class="form-select"> <select class="form-select">
<option selected>SCS</option> <option selected>SCS</option>
<option>STS</option> <option>STS</option>
<option>SM</option> <option>SM</option>
</select> </select>
</div> </div> -->
<div class="col-3"> <div class="col-3">
<button id="btn_scan_channel_1" class="btn btn-primary w-100">Scan</button> <button id="btn_scan_channel_1" class="btn btn-primary w-100">Scan</button>
</div> </div>
<div class="col-3">
<button id="btn_apply_config_channel_1" class="btn btn-primary w-100">Apply</button>
</div>
</div> </div>
<table id="channel1-motor-table" class="table table-bordered"> <table id="channel1-motor-table" class="table table-bordered">
<thead> <thead>
@ -132,6 +139,7 @@
<th data-key="MOVING">MOVING</th> <th data-key="MOVING">MOVING</th>
<th data-key="CURRENT_CURRENT">CURRENT</th> <th data-key="CURRENT_CURRENT">CURRENT</th>
<th data-key="VOLTAGE">VOLTAGE</th> <th data-key="VOLTAGE">VOLTAGE</th>
<th data-key="NAME">NAME</th>
</tr> </tr>
</thead> </thead>

110
script.js
View File

@ -1,5 +1,5 @@
import { SerialManager } from './serial.js'; import { SerialManager } from './serial.js';
import { ServoMotor, getModelType, writeData } from './feetechDefinitions.js'; import { ServoMotor, getModelType, reverseModelMap, writeData } from './feetechDefinitions.js';
import { CurveEditor } from './curveEditor.js'; import { CurveEditor } from './curveEditor.js';
import { Robot } from './robot.js'; import { Robot } from './robot.js';
import { NodeEditor } from './nodeeditor/NodeEditor.js'; import { NodeEditor } from './nodeeditor/NodeEditor.js';
@ -330,7 +330,7 @@ window.onload = () => {
for (let ch = 0; ch < dials.length; ch++) { for (let ch = 0; ch < dials.length; ch++) {
const value = dials[ch].value; const value = dials[ch].value;
motorPayloads.push({ motorId: ch, position: value }); motorPayloads.push({ motorId: dials[ch].motorID, position: value });
} }
const buffer = new ArrayBuffer(motorPayloads.length * 3); const buffer = new ArrayBuffer(motorPayloads.length * 3);
@ -343,6 +343,8 @@ window.onload = () => {
}); });
const payload = new Uint8Array(buffer); const payload = new Uint8Array(buffer);
console.log("SENDING POSITIONTS");
console.log(payload);
serial.sendSetPositions(payload); serial.sendSetPositions(payload);
} }
} }
@ -732,6 +734,92 @@ window.onload = () => {
document.getElementById('btn_apply_config_channel_0').onclick = async () => {
const table = document.querySelector('#channel0-motor-table tbody');
const rows = table.querySelectorAll('tr');
const motors = [];
rows.forEach(row => {
const cells = row.querySelectorAll('td');
const motor = {
MODEL: reverseModelMap.get(cells[0].textContent.trim()),
ID: parseInt(cells[1].textContent.trim()),
// MIN_ANGLE_LIMIT: parseInt(cells[2].textContent.trim()),
// MAX_ANGLE_LIMIT: parseInt(cells[3].textContent.trim()),
// POSITION: parseInt(cells[4].textContent.trim()),
// CW_DEAD_ZONE: parseInt(cells[5].textContent.trim()),
// CCW_DEAD_ZONE: parseInt(cells[6].textContent.trim()),
// OFFSET: parseInt(cells[7].textContent.trim()),
// MODE: parseInt(cells[8].textContent.trim()),
// TORQUE_ENABLE: parseInt(cells[9].textContent.trim()),
// ACCELERATION: parseInt(cells[10].textContent.trim()),
// GOAL_POSITION: parseInt(cells[11].textContent.trim()),
// GOAL_TIME: parseInt(cells[12].textContent.trim()),
// GOAL_SPEED: parseInt(cells[13].textContent.trim()),
// LOCK: parseInt(cells[14].textContent.trim()),
// CURRENT_SPEED: parseInt(cells[15].textContent.trim()),
// CURRENT_LOAD: parseInt(cells[16].textContent.trim()),
// TEMPERATURE: parseInt(cells[17].textContent.trim()),
// MOVING: parseInt(cells[18].textContent.trim()),
// CURRENT_CURRENT: parseInt(cells[19].textContent.trim()),
// VOLTAGE: parseInt(cells[20].textContent.trim()),
NAME: cells[21].textContent.trim()
};
motors.push(motor);
});
console.log("Compiled motor list:", motors);
await serial.sendConfigUpdate(encodeMotorConfig(motors));
// You can now use this list for saving, sending, or applying config
};
function encodeMotorConfig(motors) {
const robotName = "Mr Roboto";
const firmwareVersion = 1;
const bufferSize = 1024; // adjust as needed
const buffer = new ArrayBuffer(bufferSize);
const view = new DataView(buffer);
let offset = 0;
const encoder = new TextEncoder();
const nameBytes = encoder.encode(robotName);
const nameLength = Math.min(nameBytes.length, 255); // max 255 bytes
// 🔹 Encode robotName (length + bytes)
view.setUint8(offset++, nameLength);
for (let i = 0; i < nameLength; i++) {
view.setUint8(offset++, nameBytes[i]);
}
// 🔹 Encode firmwareVersion (2 bytes)
view.setUint16(offset, firmwareVersion, true); offset += 2;
// 🔹 Encode motor count (1 byte)
view.setUint8(offset++, motors.length);
// 🔹 Encode motor entries
motors.forEach(motor => {
const { major, minor } = motor.MODEL;
const modelValue = (minor << 8) | major;
view.setUint16(offset, modelValue, true); offset += 2; // MODEL
view.setUint16(offset, motor.ID, true); offset += 2; // ID
const motorNameBytes = encoder.encode(motor.NAME);
const motorNameLength = Math.min(motorNameBytes.length, 255);
view.setUint8(offset++, motorNameLength);
for (let i = 0; i < motorNameLength; i++) {
view.setUint8(offset++, motorNameBytes[i]);
}
});
return new Uint8Array(buffer.slice(0, offset));
}
@ -771,7 +859,8 @@ window.onload = () => {
motor.TEMPERATURE, motor.TEMPERATURE,
motor.MOVING, motor.MOVING,
motor.CURRENT_CURRENT, motor.CURRENT_CURRENT,
motor.VOLTAGE motor.VOLTAGE,
motor.NAME
]; ];
const modelType = motor.MODEL.startsWith('SCS') ? 'SCS' : const modelType = motor.MODEL.startsWith('SCS') ? 'SCS' :
@ -792,18 +881,23 @@ window.onload = () => {
td.setAttribute('contenteditable', 'true'); td.setAttribute('contenteditable', 'true');
td.setAttribute('data-type', 'number'); td.setAttribute('data-type', 'number');
if (index === 21) {
td.setAttribute('data-type', 'text');
} else {
td.setAttribute('data-type', 'number');
td.setAttribute('data-min', rangeMin.toString());
td.setAttribute('data-max', rangeMax.toString());
if (index === 1) { if (index === 1) {
td.setAttribute('data-min', '0'); td.setAttribute('data-min', '0');
td.setAttribute('data-max', '255'); td.setAttribute('data-max', '255');
} else if (index === 9) { // TORQUE ENABLE } else if (index === 9) {
td.setAttribute('data-min', '0'); td.setAttribute('data-min', '0');
td.setAttribute('data-max', '1'); td.setAttribute('data-max', '1');
} else if (index === 14) { // EEPROM LOCK } else if (index === 14) {
td.setAttribute('data-min', '0'); td.setAttribute('data-min', '0');
td.setAttribute('data-max', '1'); td.setAttribute('data-max', '1');
} else { }
td.setAttribute('data-min', rangeMin.toString());
td.setAttribute('data-max', rangeMax.toString());
} }
td.addEventListener('input', function () { td.addEventListener('input', function () {

View File

@ -14,6 +14,7 @@ const CMD_PLAY_FILE = 0x08;
const CMD_SCAN_CHANNEL = 0x09; const CMD_SCAN_CHANNEL = 0x09;
const CMD_WRITE_DATA = 0x10; const CMD_WRITE_DATA = 0x10;
const CMD_READ_DATA = 0x11; const CMD_READ_DATA = 0x11;
const CMD_WRITE_CONFIG_UPDATE = 0x12;
const CMD_START_POSITION_STREAM = 0x14; const CMD_START_POSITION_STREAM = 0x14;
const POSITION_STREAM = 0x15; const POSITION_STREAM = 0x15;
@ -96,6 +97,11 @@ export class SerialManager {
await this.send(CMD_WRITE_DATA, payload); await this.send(CMD_WRITE_DATA, payload);
} }
async sendConfigUpdate(payload) {
console.log(payload);
await this.send(CMD_WRITE_CONFIG_UPDATE, payload);
}
async requestPositionStreaming(stream) { //stream true/false async requestPositionStreaming(stream) { //stream true/false
await this.send(CMD_START_POSITION_STREAM, new Uint8Array([stream])); await this.send(CMD_START_POSITION_STREAM, new Uint8Array([stream]));
} }