diff --git a/controller/embedded/src/comm/cobsserial.h b/controller/embedded/src/comm/cobsserial.h index d80eb76f70490a0431240c4e41c447b75c937a40..2fcf26559db8eff626b1a0743ca73a4f8f519b0b 100644 --- a/controller/embedded/src/comm/cobsserial.h +++ b/controller/embedded/src/comm/cobsserial.h @@ -20,7 +20,6 @@ no warranty is provided, and users accept all liability. #include "syserror.h" #include "./drivers/indicators.h" -#define VPUSB_NUM_SPACES 8 #define VPUSB_SPACE_SIZE 1028 class COBSSerial { diff --git a/controller/embedded/src/comm/syserror.cpp b/controller/embedded/src/comm/syserror.cpp index 91ccf0f6c80d9b93c90f232563e8101fb32316c3..2918520deee2650a1a04c4d590eb11bf15acc4f1 100644 --- a/controller/embedded/src/comm/syserror.cpp +++ b/controller/embedded/src/comm/syserror.cpp @@ -20,12 +20,9 @@ boolean writeLenBytes(unsigned char* dest, uint16_t* dptr, uint16_t len){ */ // config-your-own-ll-escape-hatch -void sysError(String msg){ - return; - // whatever you want, - //ERRLIGHT_ON; +void debugmsg(String msg){ uint32_t len = msg.length(); - errBuf[0] = PK_LLERR; // the ll-errmsg-key + errBuf[0] = DEXKEY_DEBUGMSG; // the ll-errmsg-key errBuf[1] = len & 255; errBuf[2] = (len >> 8) & 255; errBuf[3] = (len >> 16) & 255; diff --git a/controller/embedded/src/comm/syserror.h b/controller/embedded/src/comm/syserror.h index 85728bf2bf74a9aeaa59f3112bc7bcde3fc56804..b7ae8c77ca4c1303942d2cf60584fd08fc7bd0ba 100644 --- a/controller/embedded/src/comm/syserror.h +++ b/controller/embedded/src/comm/syserror.h @@ -6,6 +6,8 @@ #include "cobs.h" #include "ts.h" -void sysError(String msg); +#define DEXKEY_DEBUGMSG 11 + +void debugmsg(String msg); #endif diff --git a/controller/embedded/src/comm/ts.cpp b/controller/embedded/src/comm/ts.cpp index 730071146c7066ca234059ca247bf8d8e8aec045..3349d6309b4809fce2fdd875c8cef5246d213a25 100644 --- a/controller/embedded/src/comm/ts.cpp +++ b/controller/embedded/src/comm/ts.cpp @@ -39,6 +39,35 @@ void ts_writeUint32(uint32_t val, unsigned char *buf, uint16_t *ptr){ buf[(*ptr) ++] = (val >> 24) & 255; } +// pls recall the union, +union chunk_float32 { + uint8_t bytes[4]; + float f; +}; + +/* +chunk_float32 dur_chunk = { .bytes = { bPck[ptr ++], bPck[ptr ++], bPck[ptr ++], bPck[ptr ++] } } ; +float duration = dur_chunk.f; +*/ + +union chunk_int32 { + uint8_t bytes[4]; + int32_t num; +}; + +void ts_readInt32(int32_t *val, unsigned char *buf, uint16_t *ptr){ + chunk_int32 wc = { .bytes = { buf[(*ptr) ++], buf[(*ptr) ++], buf[(*ptr) ++], buf[(*ptr) ++] }}; + *val = wc.num; +} + +void ts_writeInt32(int32_t val, unsigned char *buf, uint16_t *ptr){ + chunk_int32 wc = { .num = val }; + buf[(*ptr) ++] = wc.bytes[0]; + buf[(*ptr) ++] = wc.bytes[1]; + buf[(*ptr) ++] = wc.bytes[2]; + buf[(*ptr) ++] = wc.bytes[3]; +} + void ts_writeString(String val, unsigned char *buf, uint16_t *ptr){ uint32_t len = val.length(); buf[(*ptr) ++] = len & 255; diff --git a/controller/embedded/src/comm/ts.h b/controller/embedded/src/comm/ts.h index 4c70937a19fd766f7bd75de48c80291d5f1e7888..83820e343c4723889979e31cf7a902b603fa3a99 100644 --- a/controller/embedded/src/comm/ts.h +++ b/controller/embedded/src/comm/ts.h @@ -14,77 +14,16 @@ no warranty is provided, and users accept all liability. #include <arduino.h> -// -------------------------------------------------------- Routing (Packet) Keys - -#define PK_PPACK 77 -#define PK_PTR 88 -#define PK_DEST 99 -#define PK_LLERR 44 -#define PK_PORTF_KEY 11 -#define PK_PORTF_INC 3 -#define PK_BUSF_KEY 12 -#define PK_BUSF_INC 5 -#define PK_BUSB_KEY 14 -#define PK_BUSB_INC 5 - -// -------------------------------------------------------- Destination Keys (arrival layer) - -#define DK_APP 100 // application codes, go to -> main -#define DK_PINGREQ 101 // ping request -#define DK_PINGRES 102 // ping reply -#define DK_RREQ 111 // read request -#define DK_RRES 112 // read response -#define DK_WREQ 113 // write request -#define DK_WRES 114 // write response - -// -------------------------------------------------------- Application Keys - -#define AK_MOCODE 101 - -// -------------------------------------------------------- MVC Endpoints - -#define EP_ERRKEY 150 -#define EP_ERRKEY_QUERYDOWN 151 -#define EP_ERRKEY_EMPTY 153 -#define EP_ERRKEY_UNCLEAR 154 - -#define EP_NAME 171 -#define EP_DESCRIPTION 172 - -#define EP_NUMVPORTS 181 -#define EP_VPORT 182 -#define EP_PORTTYPEKEY 183 -#define EP_MAXSEGLENGTH 184 -#define EP_PORTSTATUS 185 -#define EP_PORTBUFSPACE 186 -#define EP_PORTBUFSIZE 187 - -#define EP_NUMVMODULES 201 -#define EP_VMODULE 202 - -#define EP_NUMINPUTS 211 -#define EP_INPUT 212 - -#define EP_NUMOUTPUTS 221 -#define EP_OUTPUT 222 - -#define EP_TYPE 231 -#define EP_VALUE 232 -#define EP_STATUS 233 - -#define EP_NUMROUES 243 -#define EP_ROUTE 235 - -// ... etc, later - // -------------------------------------------------------- Reading and Writing void ts_writeBoolean(boolean val, unsigned char *buf, uint16_t *ptr); void ts_readUint16(uint16_t *val, uint8_t *buf, uint16_t *ptr); - void ts_writeUint16(uint16_t val, unsigned char *buf, uint16_t *ptr); void ts_writeUint32(uint32_t val, unsigned char *buf, uint16_t *ptr); +void ts_readInt32(int32_t *val, unsigned char *buf, uint16_t *ptr); +void ts_writeInt32(int32_t val, unsigned char *buf, uint16_t *ptr); + void ts_writeString(String val, unsigned char *buf, uint16_t *ptr); diff --git a/controller/embedded/src/main.cpp b/controller/embedded/src/main.cpp index 956c2d3e62f75793f8928ec916be79bada7c8759..ef55ea16c7de6edb7f59f51da8b66f3a1d96613a 100644 --- a/controller/embedded/src/main.cpp +++ b/controller/embedded/src/main.cpp @@ -5,13 +5,12 @@ #include "comm/cobs.h" #include "comm/cobsserial.h" -/* // loadcell / stepper #include "lib/HX711.h" #include "lib/AccelStepper.h" -#define LOADCELL_DOUT_PIN 5 -#define LOADCELL_SCK_PIN 6 +#define LOADCELL_DOUT_PIN 12 +#define LOADCELL_SCK_PIN 11 #define LOADCELL_OFFSET 50682624 #define LOADCELL_DIVIDER 5895655 @@ -22,6 +21,11 @@ HX711 loadcell; AccelStepper stepper(AccelStepper::DRIVER, 9, 10); +// using A4988 step driver, +// want to limit to 1A current for thermal safety, +// vref = 8 * I_max * 0.050 // is 8 * 1A * 50mOhm +// so, target vref at 0.4v + void enableStepDriver(void){ digitalWrite(STEPPER_ENABLE_PIN, LOW); } @@ -30,8 +34,6 @@ void disableStepDriver(void){ digitalWrite(STEPPER_ENABLE_PIN, HIGH); } -*/ - COBSSerial* cobsSerial = new COBSSerial(); void setup() { @@ -39,31 +41,86 @@ void setup() { ERRLIGHT_SETUP; CLKLIGHT_SETUP; cobsSerial->init(); - /* // do application setup, // EN pin on step driver pinMode(5, OUTPUT); disableStepDriver(); // stepper basics hello - stepper.setMaxSpeed(100); - stepper.setAcceleration(20); + stepper.setMaxSpeed(2500); + stepper.setAcceleration(5000); // loacell loadcell.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); loadcell.set_scale(LOADCELL_DIVIDER); loadcell.set_offset(LOADCELL_OFFSET); loadcell.tare(); - */ } uint8_t* pck; uint16_t pl = 0; +uint8_t res[1024]; +uint16_t rl = 0; + +#define DEXKEY_LOADCELLREADING 12 +#define DEXKEY_LOADCELLTARE 14 +#define DEXKEY_STEPS 15 +#define DEXKEY_MOTORENABLE 16 + +uint16_t one = 1; +int32_t stepsToTake = 0; +int32_t position = 0; + void loop() { + stepper.run(); cobsSerial->loop(); if(cobsSerial->hasPacket()){ CLKLIGHT_TOGGLE; cobsSerial->getPacket(&pck, &pl); - cobsSerial->sendPacket(pck, pl); + switch(pck[0]){ + case DEXKEY_LOADCELLREADING: + if(loadcell.is_ready()){ + int32_t reading = loadcell.get_value(10); + rl = 0; + res[rl ++] = DEXKEY_LOADCELLREADING; + ts_writeInt32(reading, res, &rl); + debugmsg("reads: " + String(reading)); + } else { + debugmsg("loadcell not ready"); + } + break; + case DEXKEY_LOADCELLTARE: + loadcell.tare(); + rl = 0; + res[rl ++] = DEXKEY_LOADCELLTARE; + break; + case DEXKEY_STEPS: + stepsToTake = 0; + one = 1; + ts_readInt32(&stepsToTake, pck, &one); + debugmsg("to take steps: " + String(stepsToTake)); + stepper.runToNewPosition(position + stepsToTake); + position += stepsToTake; + rl = 0; + res[rl ++] = DEXKEY_STEPS; + ts_writeInt32(stepsToTake, res, &rl); + break; + case DEXKEY_MOTORENABLE: + rl = 0; + res[rl ++] = DEXKEY_MOTORENABLE; + if(pck[1] > 0){ + enableStepDriver(); + res[rl ++] = 1; + } else { + disableStepDriver(); + res[rl ++] = 0; + } + default: + // no-op + break; + } + if(rl > 0){ + cobsSerial->sendPacket(res, rl); + } cobsSerial->clearPacket(); } } // end loop diff --git a/controller/js/client/dex-client.js b/controller/js/client/dex-client.js index 757e667e4463b885fce13a9f8e530621d497460d..07bf7cd91ebfe06d01bdfa1d3f05dd56bc2d72f6 100644 --- a/controller/js/client/dex-client.js +++ b/controller/js/client/dex-client.js @@ -16,7 +16,7 @@ no warranty is provided, and users accept all liability. import GRIDSQUID from './drawing/gridsquid.js' -import { TS, PK, DK, AK, EP } from '../core/ts.js' +import { TS } from '../core/ts.js' import * as dat from './libs/dat.gui.module.js' @@ -27,7 +27,7 @@ console.log("hello-dex-tools") document.addEventListener('keydown', (evt) => { console.log(`keycode ${evt.keyCode}`) switch (evt.keyCode) { - case 69: // 'e' + case 80: // 'p' if(wscPort.send){ console.log('pinging...') wscPort.send(Uint8Array.from([0,1,2])) @@ -36,20 +36,164 @@ document.addEventListener('keydown', (evt) => { } break; case 82: // 'r' + // read loadcell, + console.log('reading cell...') + dex.readLoadcell().then((count) => { + console.log('resolves count', count) + }).catch((err) => { + console.error(err) + }) break; - case 80: // 'p' + case 69: // 'e' + dex.setMotorEnable(true).then((val) => { + console.log('motor is ', val) + }).catch((err) => { + console.error(err) + }) + break; + case 68: // 'd' + dex.setMotorEnable(false).then((val) => { + console.log('motor is ', val) + }).catch((err) => { + console.error(err) + }) break; case 83: // 's' + dex.step(-10000).then((increment) => { + console.log('motor stepped', increment) + }).catch((err) => { + console.error(err) + }) break; } }) +// -------------------------------------------------------- DEX Virtual Machine + +let TIMEOUT_MS = 5000 +let DEXKEY_DEBUGMSG = 11 +let DEXKEY_LOADCELLREADING = 12 +let DEXKEY_LOADCELLTARE = 14 +let DEXKEY_STEPS = 15 +let DEXKEY_MOTORENABLE = 16 + +let dex = {} + +dex.readLoadcell = () => { + return new Promise((resolve, reject) => { + if(!wscPort.send){ + reject('port to dex is closed') + return + } + wscPort.send((Uint8Array.from([DEXKEY_LOADCELLREADING]))) + let rejected = false + dex.recv = (data) => { + if(rejected) return // if already timed out... don't get confused + if(data[0] != DEXKEY_LOADCELLREADING){ + reject('oddball key after loadcell request') + } else { + let counts = TS.read('int32', data, 1, true) + resolve(counts) + } + } + setTimeout(() => { + rejected = true + reject('timeout') + }, TIMEOUT_MS) + }) +} + +dex.tareLoadcell = () => { + return new Promise((resolve, reject) => { + if(!wscPort.send){ + reject('port to dex is closed') + return + } + wscPort.send(Uint8Array.from([DEXKEY_LOADCELLTARE])) + let rejected = false + dex.recv = (data) => { + if(rejected) return + if(data[0] != DEXKEY_LOADCELLTARE){ + reject('oddball key after loadcell tare request') + } else { + resolve() + } + } + setTimeout(() => { + rejected = true + reject('timeout') + }, TIMEOUT_MS) + }) +} + +dex.setMotorEnable = (val) => { + return new Promise((resolve, reject) => { + if(!wscPort.send){ + reject('port to dex is closed') + return + } + let ob = 0 + if(val) ob = 1 + wscPort.send(Uint8Array.from([DEXKEY_MOTORENABLE, ob])) + let rejected = false + dex.recv = (data) => { + if(rejected) return + if(data[0] != DEXKEY_MOTORENABLE){ + reject('oddball key after motor enable request') + } else { + if(data[1] > 0){ + resolve('enabled') + } else { + resolve('disabled') + } + } + } // end recv + setTimeout(() => { + rejected = true + reject('timeout') + }, TIMEOUT_MS) + }) +} + +dex.step = (count) => { + return new Promise((resolve, reject) => { + if(!wscPort.send){ + reject('port to dex is closed') + return + } + let req = new Uint8Array(5) + req[0] = DEXKEY_STEPS + TS.write('int32', count, req, 1, true) + wscPort.send(req) + let rejected = false + dex.recv = (data) => { + if(rejected) return + if(data[0] != DEXKEY_STEPS){ + reject('oddball key after step request') + } else { + let increment = TS.read('int32', data, 1, true) + resolve(increment) + } + } // end recv + setTimeout(() => { + rejected = true + reject('timeout') + }, TIMEOUT_MS) + }) +} + // -------------------------------------------------------- SPAWNING TEST SUBS let wscPort = {} wscPort.send = null wscPort.onReceive = (data) => { - console.warn('recv', data) + // put ll-msg catch flag here, + if(data[0] == DEXKEY_DEBUGMSG){ + let msg = TS.read('string', data, 1, true) + console.warn('DEX DEBUGMSG: ', msg.value) + } else { + dex.recv(data) + } } let LOGPHY = false diff --git a/controller/js/core/ts.js b/controller/js/core/ts.js index 93e42cf4b302cf8ce259986c4e89d6b0700881d7..e0e84c1a47ca8387f8d2847ea42ea6e2001a7603 100644 --- a/controller/js/core/ts.js +++ b/controller/js/core/ts.js @@ -1,7 +1,7 @@ /* ts.js // typeset -serialization, keys for OSAP +serialization, keys for DEX Jake Read at the Center for Bits and Atoms (c) Massachusetts Institute of Technology 2020 @@ -12,157 +12,12 @@ Copyright is retained and must be preserved. The work is provided as is; no warranty is provided, and users accept all liability. */ -// 13: \r -// 10: \n -// these should be clearly delimited as L1 / L2 possible keys... - -// packet keys, for l0 of packets, -// PKEYS all need to be on the same byte order, since they're -// walked -// TRANSPORT LAYER -let PK = { - PPACK: 77, // this and following two bytes are rcrxb size - PTR: 88, // packet pointer (next byte is instruction) - DEST: 99, // have arrived, (next bytes are 16b checksum) - LLERR: 44, - PORTF: { - KEY: 11, // actual instruction key, - INC: 3 // number of bytes in instruction argument + 1 for the key - }, - BUSF: { - KEY: 12, - INC: 5 - }, - BUSB: { - KEY: 14, - INC: 5, - } -} - -// ARRIVAL LAYER (what do to once received packet / passed checksum) - -// destination keys -let DK = { - APP: 100, // next bytes are for your application, - PINGREQ: 101, // next byte is ping-id | eop - PINGRES: 102, // next byte is ping-id | eop - RREQ: 111, // read request, next byte is request-id, then ENDPOINTS, - RRES: 112, // response, next byte is request-id, then ENDPOINTS - WREQ: 113, // write request, - WRES: 114, // write response, -} - -// application keys -let AK = { - MOCODE: 101 -} - -// could do like -// ITEMS.key / .serialize / .deserialize -// the mess is down here -// the idea is that any unique endpoint has one routine to -// serialize / deserialze. these are for the mvc layer, -// typed objects will get a similar set -// perhaps, i.e, some of these should be like 'numinputs' -// or 'numports', etc ... unclear to me how to query down-tree -let EP = { - ERR: { - KEY: 150, - KEYS: { - QUERYDOWN: 151, // selected chunk too large for single segment, go finer grain - MSG: 152, // generic error message - EMPTY: 153, // resource queried for not here - UNCLEAR: 154, // bad request / query - NOREAD: 155, // no reading supported for this, - NOWRITE: 156, // rejected write request: writing not available here - WRITEREJECT: 157, // writing OK here, but not with this value ? - } - }, - // anything can include: - NAME: { - KEY: 171, - }, - DESCRIPTION: { - KEY: 172, - }, - // number of vPorts, at node, - NUMVPORTS: { - KEY: 181, // count of vPorts at node - }, - // this vPort (always succeeded by indice) - VPORT: { - KEY: 182, - ISDIVE: true, - }, - // vPort-unique keys, - PORTTYPEKEY:{ - KEY: 183, - }, - MAXSEGLENGTH: { - KEY: 184, // uint32 num-bytes-per-fwded-pck allowed on this phy - }, - PORTSTATUS: { - KEY: 185, - CLOSED: 0, - OPEN: 1, - CLOSING: 2, - OPENING: 3 - }, - PORTBUFSPACE: { - KEY: 186, // num empty frames in port - }, - PORTBUFSIZE: { - KEY: 187, - }, - // I currently have *no idea* how will handle bus drops: - // perhaps they are mostly like outputs, in the vPort... typed, have value, ok - // number of vModules, at node, - NUMVMODULES:{ - KEY: 201, - }, - // this vmodule, (always succeeded by indice) - VMODULE: { - KEY: 202, - ISDIVE: true, - }, - // vPorts, vModules can both have inputs, outputs, - NUMINPUTS: { - KEY: 211, - }, - INPUT: { // this input (always succeeded by indice) - KEY: 212, - ISDIVE: true, - }, - NUMOUTPUTS: { - KEY: 221, - }, - OUTPUT: { // this output (always succeeded by indice) - KEY: 222, - ISDIVE: true, - }, - // inputs / outputs can have: (in addnt to name, description) - TYPE: { // not the same as port-type, key or key(s) for compound types - KEY: 231, - }, - VALUE: { // data bytes currently occupying the output / input - KEY: 232, - }, - STATUS: { - KEY: 233, // boolean open / closed, occupied / unoccupied, etc - }, - // outputs have: - NUMROUTES: { - KEY: 234, - }, - ROUTE: { - KEY: 235, // within output, - } -} - let TS = {} let decoder = new TextDecoder() +let tempDataView = {} + TS.read = (type, buffer, start, keyless) => { if (!keyless) { throw new Error('need code here for key checking') @@ -175,6 +30,9 @@ TS.read = (type, buffer, start, keyless) => { return (buffer[start] & 255) | (buffer[start + 1] << 8) case 'uint32': return (buffer[start] & 255) | (buffer[start + 1] << 8) | (buffer[start + 2] << 16) | (buffer[start + 3] << 24) + case 'int32': + tempDataView = new DataView(buffer.buffer) // the actual buffer underlying the uint8array... + return tempDataView.getInt32(start, true) case 'boolean': if (buffer[start] > 0) { return true @@ -219,6 +77,11 @@ TS.write = (type, value, buffer, start, keyless) => { buffer[start + 2] = (value >> 16) & 255 buffer[start + 3] = (value >> 24) & 255 return 4 + case 'int32': + tempArr = Int32Array.from([value]) + tempBytes = new Uint8Array(tempArr.buffer) + buffer.set(tempBytes, start) + return 4 case 'float32': tempArr = Float32Array.from([value]) tempBytes = new Uint8Array(tempArr.buffer) @@ -251,15 +114,6 @@ TS.write = (type, value, buffer, start, keyless) => { } } -// strings, eventually... -TS.writeAppErr = (msg) => { - let reply = new Uint8Array(msg.length + 6) - reply[0] = AK.ERR - reply[1] = AK.E.MSG - TS.write('string', msg, reply, 2, true) - return reply -} - TS.logPacket = (buffer) => { // log a pretty buffer // buffers should all be Uint8Array views, @@ -270,14 +124,6 @@ TS.logPacket = (buffer) => { console.log(pert) } -TS.portf = (num) => { - return [PK.PORTF.KEY, num & 255, (num >> 8) & 255] -} - export { - PK, - DK, - AK, - EP, TS } diff --git a/controller/js/local/dex-usb-bridge.js b/controller/js/local/dex-usb-bridge.js index 713514ad85e50bf422818dccfe17813721edcb6b..dcf3e07e297917c8badc4a32a3829ee025cbcf80 100644 --- a/controller/js/local/dex-usb-bridge.js +++ b/controller/js/local/dex-usb-bridge.js @@ -25,7 +25,7 @@ import COBS from './common/cobs.js' import { PerformanceObserver, performance } from 'perf_hooks' -import { TS, EP } from '../core/ts.js' +import { TS } from '../core/ts.js' let LOGPHY = true