Commit 0c223c5e authored by Jake Read's avatar Jake Read

low level controller

parent 9b629821
......@@ -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 {
......
......@@ -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;
......
......@@ -6,6 +6,8 @@
#include "cobs.h"
#include "ts.h"
void sysError(String msg);
#define DEXKEY_DEBUGMSG 11
void debugmsg(String msg);
#endif
......@@ -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;
......
......@@ -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);
......@@ -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
......@@ -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
......
/*
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
}
......@@ -25,7 +25,7 @@ import COBS from './common/cobs.js'
import { PerformanceObserver, performance } from 'perf_hooks'