From 85bba57642d3b1ab3f6a1771b2340453133762cf Mon Sep 17 00:00:00 2001
From: Jake <jake.read@cba.mit.edu>
Date: Mon, 26 Sep 2022 22:31:20 -0400
Subject: [PATCH] spun up new esc-box firmware

---
 .gitmodules                                   |  12 +
 firmware/esc-drop/.gitignore                  |   5 +
 firmware/esc-drop/.vscode/extensions.json     |  10 +
 firmware/esc-drop/include/README              |  39 ++++
 firmware/esc-drop/lib/README                  |  46 ++++
 firmware/esc-drop/platformio.ini              |  14 ++
 .../esc-drop/src/drivers/peripheral_nums.h    |  18 ++
 firmware/esc-drop/src/drivers/servo_pwm.cpp   | 103 +++++++++
 firmware/esc-drop/src/drivers/servo_pwm.h     |  32 +++
 firmware/esc-drop/src/indicators.h            |  34 +++
 firmware/esc-drop/src/main.cpp                |  69 ++++++
 firmware/esc-drop/src/osap_config.h           |  34 +++
 firmware/esc-drop/src/osape                   |   1 +
 firmware/esc-drop/src/osape_arduino           |   1 +
 firmware/esc-drop/src/osape_ucbus             |   1 +
 firmware/esc-drop/src/ucbus_config.h          |  29 +++
 firmware/esc-drop/src/utils_samd51            |   1 +
 firmware/esc-drop/test/README                 |  11 +
 log/esc-pid-log.md                            | 212 ++++++++++++++++++
 19 files changed, 672 insertions(+)
 create mode 100644 .gitmodules
 create mode 100644 firmware/esc-drop/.gitignore
 create mode 100644 firmware/esc-drop/.vscode/extensions.json
 create mode 100644 firmware/esc-drop/include/README
 create mode 100644 firmware/esc-drop/lib/README
 create mode 100644 firmware/esc-drop/platformio.ini
 create mode 100644 firmware/esc-drop/src/drivers/peripheral_nums.h
 create mode 100644 firmware/esc-drop/src/drivers/servo_pwm.cpp
 create mode 100644 firmware/esc-drop/src/drivers/servo_pwm.h
 create mode 100644 firmware/esc-drop/src/indicators.h
 create mode 100644 firmware/esc-drop/src/main.cpp
 create mode 100644 firmware/esc-drop/src/osap_config.h
 create mode 160000 firmware/esc-drop/src/osape
 create mode 160000 firmware/esc-drop/src/osape_arduino
 create mode 160000 firmware/esc-drop/src/osape_ucbus
 create mode 100644 firmware/esc-drop/src/ucbus_config.h
 create mode 160000 firmware/esc-drop/src/utils_samd51
 create mode 100644 firmware/esc-drop/test/README
 create mode 100644 log/esc-pid-log.md

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..d57842b
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,12 @@
+[submodule "firmware/esc-drop/src/osape"]
+	path = firmware/esc-drop/src/osape
+	url = https://github.com/jakeread/osape.git
+[submodule "firmware/esc-drop/src/osape_ucbus"]
+	path = firmware/esc-drop/src/osape_ucbus
+	url = https://github.com/jakeread/osape_ucbus.git
+[submodule "firmware/esc-drop/src/osape_arduino"]
+	path = firmware/esc-drop/src/osape_arduino
+	url = https://github.com/jakeread/osape_arduino.git
+[submodule "firmware/esc-drop/src/utils_samd51"]
+	path = firmware/esc-drop/src/utils_samd51
+	url = https://github.com/jakeread/utils_samd51.git
diff --git a/firmware/esc-drop/.gitignore b/firmware/esc-drop/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/firmware/esc-drop/.gitignore
@@ -0,0 +1,5 @@
+.pio
+.vscode/.browse.c_cpp.db*
+.vscode/c_cpp_properties.json
+.vscode/launch.json
+.vscode/ipch
diff --git a/firmware/esc-drop/.vscode/extensions.json b/firmware/esc-drop/.vscode/extensions.json
new file mode 100644
index 0000000..080e70d
--- /dev/null
+++ b/firmware/esc-drop/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/firmware/esc-drop/include/README b/firmware/esc-drop/include/README
new file mode 100644
index 0000000..194dcd4
--- /dev/null
+++ b/firmware/esc-drop/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/firmware/esc-drop/lib/README b/firmware/esc-drop/lib/README
new file mode 100644
index 0000000..6debab1
--- /dev/null
+++ b/firmware/esc-drop/lib/README
@@ -0,0 +1,46 @@
+
+This directory is intended for project specific (private) libraries.
+PlatformIO will compile them to static libraries and link into executable file.
+
+The source code of each library should be placed in a an own separate directory
+("lib/your_library_name/[here are source files]").
+
+For example, see a structure of the following two libraries `Foo` and `Bar`:
+
+|--lib
+|  |
+|  |--Bar
+|  |  |--docs
+|  |  |--examples
+|  |  |--src
+|  |     |- Bar.c
+|  |     |- Bar.h
+|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
+|  |
+|  |--Foo
+|  |  |- Foo.c
+|  |  |- Foo.h
+|  |
+|  |- README --> THIS FILE
+|
+|- platformio.ini
+|--src
+   |- main.c
+
+and a contents of `src/main.c`:
+```
+#include <Foo.h>
+#include <Bar.h>
+
+int main (void)
+{
+  ...
+}
+
+```
+
+PlatformIO Library Dependency Finder will find automatically dependent
+libraries scanning project source files.
+
+More information about PlatformIO Library Dependency Finder
+- https://docs.platformio.org/page/librarymanager/ldf.html
diff --git a/firmware/esc-drop/platformio.ini b/firmware/esc-drop/platformio.ini
new file mode 100644
index 0000000..50208f0
--- /dev/null
+++ b/firmware/esc-drop/platformio.ini
@@ -0,0 +1,14 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:adafruit_feather_m4]
+platform = atmelsam
+board = adafruit_feather_m4
+framework = arduino
diff --git a/firmware/esc-drop/src/drivers/peripheral_nums.h b/firmware/esc-drop/src/drivers/peripheral_nums.h
new file mode 100644
index 0000000..eed9f18
--- /dev/null
+++ b/firmware/esc-drop/src/drivers/peripheral_nums.h
@@ -0,0 +1,18 @@
+#ifndef PERIPHERAL_NUMS_H_
+#define PERIPHERAL_NUMS_H_
+
+#define PERIPHERAL_A 0
+#define PERIPHERAL_B 1
+#define PERIPHERAL_C 2
+#define PERIPHERAL_D 3
+#define PERIPHERAL_E 4
+#define PERIPHERAL_F 5
+#define PERIPHERAL_G 6
+#define PERIPHERAL_H 7
+#define PERIPHERAL_I 8
+#define PERIPHERAL_K 9
+#define PERIPHERAL_L 10
+#define PERIPHERAL_M 11
+#define PERIPHERAL_N 12
+
+#endif 
\ No newline at end of file
diff --git a/firmware/esc-drop/src/drivers/servo_pwm.cpp b/firmware/esc-drop/src/drivers/servo_pwm.cpp
new file mode 100644
index 0000000..93856de
--- /dev/null
+++ b/firmware/esc-drop/src/drivers/servo_pwm.cpp
@@ -0,0 +1,103 @@
+/*
+drivers/servo_pwm.cpp
+
+output servo-type (20ms period, 1-2ms duty cycle) with TC on D51
+
+Jake Read at the Center for Bits and Atoms
+(c) Massachusetts Institute of Technology 2020
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and ponyo
+projects. Copyright is retained and must be preserved. The work is provided as
+is; no warranty is provided, and users accept all liability.
+*/
+
+#include "servo_pwm.h"
+#include "../utils_samd51/clock_utils.h"
+#include "indicators.h"
+#include "peripheral_nums.h"
+
+Servo_PWM* Servo_PWM::instance = 0;
+
+Servo_PWM* Servo_PWM::getInstance(void){
+    if(instance == 0){
+        instance = new Servo_PWM();
+    }
+    return instance;
+}
+
+Servo_PWM* servo_pwm = Servo_PWM::getInstance();
+
+Servo_PWM::Servo_PWM(){}
+
+#define PWM_PORT PORT->Group[0]
+#define PWM_PIN 22 
+#define PWM_PIN_BM (uint32_t)(1 << PWM_PIN)
+#define PWM_PIN_HI PWM_PORT.OUTSET.reg = PWM_PIN_BM
+#define PWM_PIN_LO PWM_PORT.OUTCLR.reg = PWM_PIN_BM
+
+// PWM for servos is 20ms period, 1-2ms duty.
+// do 2MHz input clock, wrap at 40000 ticks 
+// 1ms is 2000 ticks, 2ms is 4000 ticks 
+
+void Servo_PWM::init(void){
+    // set the pin as output: this is toggled on interrupt, not hardware TC 
+    PWM_PORT.DIRSET.reg = PWM_PIN_BM;
+    // setup xtal for timer clk 
+    d51ClockUtils->setup_16mhz_xtal();
+    // disable to setup 
+    TC3->COUNT16.CTRLA.bit.ENABLE = 0;
+    // unmask clock 
+    MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC3;
+    // send clk to ch 
+    GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_CHEN
+        | GCLK_PCHCTRL_GEN(d51ClockUtils->mhz_xtal_gclk_num);
+    // setup timer 
+    TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16
+        | TC_CTRLA_PRESCSYNC_PRESC
+        | TC_CTRLA_PRESCALER_DIV4;
+    // want match pwm (MPWM) mode, should have a TOP register for period, and CC[x] for the swap 
+    TC3->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MPWM;
+    TC3->COUNT16.CC[0].reg = 5000; // CC0 is 'top' in match pwm: 40k here for 20ms period, 5k for 2.5ms / 400hz
+    TC3->COUNT16.CC[1].reg = 250; // this should put the first tick at 1ms 
+    // want interrupts
+    TC3->COUNT16.INTENSET.bit.MC0 = 1;
+    TC3->COUNT16.INTENSET.bit.MC1 = 1;
+    // the rest 
+    // ...
+    // now enable 
+    while(TC3->COUNT16.SYNCBUSY.bit.ENABLE);
+    TC3->COUNT16.CTRLA.bit.ENABLE = 1;
+    // enable the IRQ
+    NVIC_EnableIRQ(TC3_IRQn);
+}
+
+// need to finish setup above, to do... something, I think compare at duty cycle and wrap at 20ms
+// then try write(0) and write(1) at some intervals in loop,
+// configure your debugs (currently triggered on pulse_counter events) and watch scope 
+// then roll PID... tune... and do bus action 
+// maybe tune after bus action, have pid-config there, not too hard 
+
+void TC3_Handler(void){
+    if(TC3->COUNT16.INTFLAG.bit.MC0){
+        TC3->COUNT16.INTFLAG.bit.MC0 = 1;
+        PWM_PIN_HI;
+        DEBUG1PIN_ON;
+    }
+    if(TC3->COUNT16.INTFLAG.bit.MC1){
+        TC3->COUNT16.INTFLAG.bit.MC1 = 1;
+        PWM_PIN_LO;
+        DEBUG1PIN_OFF;
+    }
+}
+
+void Servo_PWM::setDuty(float duty){
+    // 0-1 -> 2000-4000
+    if(duty > 1.0F){
+        duty = 1.0F;
+    } else if (duty < 0.0F){
+        duty = 0.0F;
+    }
+    uint16_t ticks = duty * 250;
+    TC3->COUNT16.CCBUF[1].reg = 250 + ticks; //2000 + ticks; 
+}
\ No newline at end of file
diff --git a/firmware/esc-drop/src/drivers/servo_pwm.h b/firmware/esc-drop/src/drivers/servo_pwm.h
new file mode 100644
index 0000000..8811dba
--- /dev/null
+++ b/firmware/esc-drop/src/drivers/servo_pwm.h
@@ -0,0 +1,32 @@
+/*
+drivers/servo_pwm.h
+
+output servo-type (20ms period, 1-2ms duty cycle) with TC on D51
+
+Jake Read at the Center for Bits and Atoms
+(c) Massachusetts Institute of Technology 2020
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and ponyo
+projects. Copyright is retained and must be preserved. The work is provided as
+is; no warranty is provided, and users accept all liability.
+*/
+
+#ifndef SERVO_PWM_H_
+#define SERVO_PWM_H_
+
+#include <Arduino.h>
+
+class Servo_PWM {
+    private:
+        static Servo_PWM* instance;
+    public:
+        Servo_PWM();
+        static Servo_PWM* getInstance(void);
+        void init(void);
+        void setDuty(float duty); // 0-1 
+};
+
+extern Servo_PWM* servo_pwm;
+
+#endif 
\ No newline at end of file
diff --git a/firmware/esc-drop/src/indicators.h b/firmware/esc-drop/src/indicators.h
new file mode 100644
index 0000000..260d458
--- /dev/null
+++ b/firmware/esc-drop/src/indicators.h
@@ -0,0 +1,34 @@
+// for the new one! with the DIP switch! 
+#define CLKLIGHT_PIN 27
+#define CLKLIGHT_PORT PORT->Group[0]
+#define ERRLIGHT_PIN 8
+#define ERRLIGHT_PORT PORT->Group[1]
+
+#define DEBUG1PIN_PIN 20
+#define DEBUG1PIN_PORT PORT->Group[0]
+#define DEBUG2PIN_PIN 6
+#define DEBUG2PIN_PORT PORT->Group[1]
+
+#define CLKLIGHT_BM (uint32_t)(1 << CLKLIGHT_PIN)
+#define CLKLIGHT_ON CLKLIGHT_PORT.OUTCLR.reg = CLKLIGHT_BM
+#define CLKLIGHT_OFF CLKLIGHT_PORT.OUTSET.reg = CLKLIGHT_BM
+#define CLKLIGHT_TOGGLE CLKLIGHT_PORT.OUTTGL.reg = CLKLIGHT_BM
+#define CLKLIGHT_SETUP CLKLIGHT_PORT.DIRSET.reg = CLKLIGHT_BM; CLKLIGHT_OFF
+
+#define ERRLIGHT_BM (uint32_t)(1 << ERRLIGHT_PIN)
+#define ERRLIGHT_ON ERRLIGHT_PORT.OUTCLR.reg = ERRLIGHT_BM
+#define ERRLIGHT_OFF ERRLIGHT_PORT.OUTSET.reg = ERRLIGHT_BM
+#define ERRLIGHT_TOGGLE ERRLIGHT_PORT.OUTTGL.reg = ERRLIGHT_BM
+#define ERRLIGHT_SETUP ERRLIGHT_PORT.DIRSET.reg = ERRLIGHT_BM; ERRLIGHT_OFF
+
+#define DEBUG1PIN_BM (uint32_t)(1 << DEBUG1PIN_PIN)
+#define DEBUG1PIN_ON DEBUG1PIN_PORT.OUTSET.reg = DEBUG1PIN_BM
+#define DEBUG1PIN_OFF DEBUG1PIN_PORT.OUTCLR.reg = DEBUG1PIN_BM
+#define DEBUG1PIN_TOGGLE DEBUG1PIN_PORT.OUTTGL.reg = DEBUG1PIN_BM
+#define DEBUG1PIN_SETUP DEBUG1PIN_PORT.DIRSET.reg = DEBUG1PIN_BM; DEBUG1PIN_OFF
+
+#define DEBUG2PIN_BM (uint32_t)(1 << DEBUG2PIN_PIN)
+#define DEBUG2PIN_ON DEBUG2PIN_PORT.OUTSET.reg = DEBUG2PIN_BM
+#define DEBUG2PIN_OFF DEBUG2PIN_PORT.OUTCLR.reg = DEBUG2PIN_BM
+#define DEBUG2PIN_TOGGLE DEBUG2PIN_PORT.OUTTGL.reg = DEBUG2PIN_BM
+#define DEBUG2PIN_SETUP DEBUG2PIN_PORT.DIRSET.reg = DEBUG2PIN_BM; DEBUG2PIN_OFF
\ No newline at end of file
diff --git a/firmware/esc-drop/src/main.cpp b/firmware/esc-drop/src/main.cpp
new file mode 100644
index 0000000..fc01852
--- /dev/null
+++ b/firmware/esc-drop/src/main.cpp
@@ -0,0 +1,69 @@
+#include <Arduino.h>
+
+#include "indicators.h"
+#include "drivers/servo_pwm.h"
+
+#include "osape/core/osap.h"
+#include "osape/vertices/endpoint.h"
+#include "osape_arduino/vp_arduinoSerial.h"
+#include "osape_ucbus/vb_ucBusDrop.h"
+
+OSAP osap("esc-drop");
+
+// -------------------------------------------------------- 0: USB Serial 
+
+VPort_ArduinoSerial vpUSBSerial(&osap, "arduinoUSBSerial", &Serial);
+
+// -------------------------------------------------------- 1: Bus Drop 
+
+VBus_UCBusDrop vbUCBusDrop(&osap, "ucBusDrop"); 
+
+// -------------------------------------------------------- 2: Spindle Duty (0-1 FP)
+
+EP_ONDATA_RESPONSES onDutyData(uint8_t* data, uint16_t len){
+  uint16_t rptr = 0;
+  float duty = ts_readFloat32(data, &rptr);
+  servo_pwm->setDuty(duty);
+  // get a float, set a float
+  return EP_ONDATA_ACCEPT;
+}
+
+Endpoint dutyEP(&osap, "spindleDuty", onDutyData);
+
+// -------------------------------------------------------- Arduino Setup 
+
+void setup() {
+  ERRLIGHT_SETUP;
+  CLKLIGHT_SETUP;
+  // DEBUG1PIN_SETUP;
+  // DEBUG2PIN_SETUP;
+  // port begin 
+  vpUSBSerial.begin();
+  vbUCBusDrop.begin();
+  // pwm-out, 
+  servo_pwm->init();
+  servo_pwm->setDuty(0.0F);
+  // ERRLIGHT_ON;
+  // CLKLIGHT_ON;
+}
+
+// 0.1 -> nok RPM
+// 0.15 -> 2.5k RPM ~ unstable stops 
+// 0.2 -> 6k RPM 
+// 0.3 -> 12k RPM 6k comfortable, low harmonic 
+// 0.4 -> 18k RPM 6k p loud 
+// 0.5 -> 23k RPM 5k loud AF 
+// 0.6 -> don't do this
+
+// -------------------------------------------------------- Das Loop 
+
+void loop() {
+  osap.loop();
+  // CLKLIGHT_TOGGLE;
+} // end loop 
+
+void ucBusDrop_onRxISR(void){
+  // DEBUG1PIN_ON;
+  // axl_integrator();
+  // DEBUG1PIN_OFF;
+}
\ No newline at end of file
diff --git a/firmware/esc-drop/src/osap_config.h b/firmware/esc-drop/src/osap_config.h
new file mode 100644
index 0000000..f94ddc1
--- /dev/null
+++ b/firmware/esc-drop/src/osap_config.h
@@ -0,0 +1,34 @@
+/*
+osap_config.h
+
+config options for an osap-embedded build 
+
+Jake Read at the Center for Bits and Atoms
+(c) Massachusetts Institute of Technology 2022
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the osap project.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+#ifndef OSAP_CONFIG_H_
+#define OSAP_CONFIG_H_
+
+// size of vertex stacks, lenght, then count,
+#define VT_SLOTSIZE 256
+#define VT_STACKSIZE 3  // must be >= 2 for ringbuffer operation 
+#define VT_MAXCHILDREN 16
+#define VT_MAXITEMSPERTURN 8
+
+// max # of endpoints that could be spawned here,
+#define MAX_CONTEXT_ENDPOINTS 64
+
+// count of routes each endpoint can have, 
+#define ENDPOINT_MAX_ROUTES 4
+#define ENDPOINT_ROUTE_MAX_LEN 64 
+
+// count of broadcast channels width, 
+#define VBUS_MAX_BROADCAST_CHANNELS 64 
+
+#endif 
\ No newline at end of file
diff --git a/firmware/esc-drop/src/osape b/firmware/esc-drop/src/osape
new file mode 160000
index 0000000..e656c96
--- /dev/null
+++ b/firmware/esc-drop/src/osape
@@ -0,0 +1 @@
+Subproject commit e656c969af1ca5785108d0007294441ffafbb732
diff --git a/firmware/esc-drop/src/osape_arduino b/firmware/esc-drop/src/osape_arduino
new file mode 160000
index 0000000..ede9f0b
--- /dev/null
+++ b/firmware/esc-drop/src/osape_arduino
@@ -0,0 +1 @@
+Subproject commit ede9f0b52f1db0e686088441c0e9ab863eb38aa7
diff --git a/firmware/esc-drop/src/osape_ucbus b/firmware/esc-drop/src/osape_ucbus
new file mode 160000
index 0000000..9779836
--- /dev/null
+++ b/firmware/esc-drop/src/osape_ucbus
@@ -0,0 +1 @@
+Subproject commit 9779836fd38f965e2be5fb0ff11f933dac7f7a04
diff --git a/firmware/esc-drop/src/ucbus_config.h b/firmware/esc-drop/src/ucbus_config.h
new file mode 100644
index 0000000..eee24b0
--- /dev/null
+++ b/firmware/esc-drop/src/ucbus_config.h
@@ -0,0 +1,29 @@
+/*
+ucbus_confi.h
+
+config options for an ucbus instance 
+
+Jake Read at the Center for Bits and Atoms
+(c) Massachusetts Institute of Technology 2022
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the osap project.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+#ifndef UCBUS_CONFIG_H_
+#define UCBUS_CONFIG_H_
+
+#define UCBUS_MAX_DROPS 32 
+#define UCBUS_IS_DROP 
+//#define UCBUS_IS_HEAD 
+
+#define UCBUS_BAUD 2 
+
+#define UCBUS_IS_D51
+// #define UCBUS_IS_D21
+
+#define UCBUS_ON_OSAP 
+
+#endif 
\ No newline at end of file
diff --git a/firmware/esc-drop/src/utils_samd51 b/firmware/esc-drop/src/utils_samd51
new file mode 160000
index 0000000..d77c3a9
--- /dev/null
+++ b/firmware/esc-drop/src/utils_samd51
@@ -0,0 +1 @@
+Subproject commit d77c3a9993a908c52ef53efd69d407be3c288e7e
diff --git a/firmware/esc-drop/test/README b/firmware/esc-drop/test/README
new file mode 100644
index 0000000..b94d089
--- /dev/null
+++ b/firmware/esc-drop/test/README
@@ -0,0 +1,11 @@
+
+This directory is intended for PlatformIO Unit Testing and project tests.
+
+Unit Testing is a software testing method by which individual units of
+source code, sets of one or more MCU program modules together with associated
+control data, usage procedures, and operating procedures, are tested to
+determine whether they are fit for use. Unit testing finds problems early
+in the development cycle.
+
+More information about PlatformIO Unit Testing:
+- https://docs.platformio.org/page/plus/unit-testing.html
diff --git a/log/esc-pid-log.md b/log/esc-pid-log.md
new file mode 100644
index 0000000..d319b4e
--- /dev/null
+++ b/log/esc-pid-log.md
@@ -0,0 +1,212 @@
+## 2020 09 14 
+
+Notes for attempt to speed control 'esc' type controller, using PWM output and hall reading input. 
+
+OK, have verified that the hall readout is as expected, so I will be able to count pulses. 14 per revolution. So I can solder up another test-esc-board, and get into trying to read that & control it. 
+
+This is set up now, so I have to configure a timer on PB14, and get setup to debug / etc... 
+
+First observation is that: hot damn: at 60kRPM, with 14 ticks per rev, this signal is going to be 50 mHz? Seems wild... op, nevermind, that was /60 not *60, so that'd be 14kHz, god bless. I do need a pretty high resolution timer here though, I should want a lower bound of measurement at about 500RPM, upper bound 60kRPM. TC outputs 0 or 1 can both be input / output for the counter, I think, that's good. I'm not sure I have timer setup code, except for internal interrupt generators. 
+
+My tack is to do this: setup the timer, capture channel edges, and put counts in a ringbuffer of ~ 16 values. When I want to read that, I'll average it and pull an RPM value, then do PID over that. I'll want to generate an internal clock to run that PID loop on as well. 
+
+So, about timer resolution. I have 16 bits. The slowest speed I'd like to measure is 500rpm, which would generate ~ 100Hz signal, so I should aim for my 16 bit timer to wrap at 100Hz - so that's 6553600 ticks / s for 100hz wrap, about 6MHz clock - so I can do the 16MHz source / 4, to get well underneath. At 60kRPM, I'll still have ~ 285 counts in the timer, enough resolution. 
+
+I think the way to do this is to setup two interrupts: one on the capture (defaults to the rising edge capture), where I would store the count and then reset it, and one on the overflow, where I would store the full width (lowest speed reading) and reset it. 
+
+OK, I think I just need to setup some debug pins, to make sure I am capturing the overflow and capture interrupts properly. 
+
+OK, I think this is set, and I'm seeing the overflow interrupt OK, but not the capture channel. Op, just had a pin misconfigured... 14, not 16. 
+
+So, next I'll read captures into a ringbuffer, then try printing out speeds... do with modular div/ count, yar? 
+
+### Counting Ticks 
+
+Here's the setup code, to track:
+
+```cpp
+    // setup PB14 to this peripheral 
+    PULSE_PORT.DIRCLR.reg = PULSE_PIN_BM;   // not-output 
+    PULSE_PORT.PINCFG[PULSE_PIN].bit.PMUXEN = 1; // allow peripheral mux 
+    if(PULSE_PIN % 2){
+        PULSE_PORT.PMUX[PULSE_PIN >> 1].reg |= PORT_PMUX_PMUXO(PERIPHERAL_E);
+    } else {
+        PULSE_PORT.PMUX[PULSE_PIN >> 1].reg |= PORT_PMUX_PMUXE(PERIPHERAL_E);
+    }
+    // setup the xtal, will use to send a clock to this timer 
+    d51_clock_boss->setup_16mhz_xtal();
+    // reset
+    TC5->COUNT16.CTRLA.bit.ENABLE = 0;
+    // unmask clocks 
+    MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC5;
+    // send a clock to the ch 
+    GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_CHEN 
+        | GCLK_PCHCTRL_GEN(d51_clock_boss->mhz_xtal_gclk_num);
+    // setup timer 
+    TC5->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 // 16 bit timer 
+        | TC_CTRLA_PRESCSYNC_PRESC  // reload / reset on prescaler 
+        | TC_CTRLA_PRESCALER_DIV256   // div the input clock, 
+        | TC_CTRLA_CAPTEN0          // enable for capture option on ch0 
+        | TC_CTRLA_COPEN0;          // capture on pin 
+    // capture event defaults to the rising edge, that's OK. 
+    // we need to get a capture and overflow interrupt, 
+    TC5->COUNT16.INTENSET.bit.MC0 = 1;
+    TC5->COUNT16.INTENSET.bit.OVF = 1;
+    // now enable it, 
+    while(TC5->COUNT16.SYNCBUSY.bit.ENABLE);
+    TC5->COUNT16.CTRLA.bit.ENABLE = 1;
+    // enable the IRQ
+    NVIC_EnableIRQ(TC5_IRQn);
+```
+
+Then interrupts:
+```cpp
+void TC5_Handler(void){
+    if(TC5->COUNT16.INTFLAG.bit.MC0){
+        // clear, stash, and reset 
+        TC5->COUNT16.INTFLAG.bit.MC0 = 1; // clear 
+        DEBUG1PIN_TOGGLE;
+    }
+    if(TC5->COUNT16.INTFLAG.bit.OVF){
+        // clear, stash, and reset 
+        TC5->COUNT16.INTFLAG.bit.OVF = 1;
+        DEBUG2PIN_TOGGLE;
+    }
+}
+```
+
+Next I'll actually do something inside those interrupts, then see about some beginner PID params. 
+
+OK, capture happens on rising edges, so I have 7 of those per rev. 
+
+Got that all running, code is like:
+
+```cpp
+void TC4_Handler(void){
+    if(TC4->COUNT16.INTFLAG.bit.MC0){
+        uint16_t width;
+        pulse_counter->_lastWasOVF ? width = 65534 : width = TC4->COUNT16.CC[0].reg;
+        pulse_counter->_lastWasOVF = false;
+        // clear, stash, and reset 
+        TC4->COUNT16.INTFLAG.bit.MC0 = 1; // clear 
+        pulse_counter->addPulse(width); // stash 
+        TC4->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // restart (?) 
+        DEBUG1PIN_TOGGLE;
+    }
+    if(TC4->COUNT16.INTFLAG.bit.OVF){
+        // clear, stash, reset is already happening 
+        pulse_counter->_lastWasOVF = true;
+        TC4->COUNT16.INTFLAG.bit.OVF = 1;
+        pulse_counter->addPulse(65534); // the long nap 
+        DEBUG2PIN_TOGGLE;
+    }
+}
+
+void Pulse_Counter::addPulse(uint16_t width){
+    _widths[_wh] = width;
+    _wh ++; 
+    if(_wh >= PULSE_WIDTHS_COUNT){
+        _wh = 0;
+    }
+}
+
+float Pulse_Counter::getAverageWidth(void){
+    float sum = 0;
+    NVIC_DisableIRQ(TC4_IRQn);
+    for(uint8_t i = 0; i < PULSE_WIDTHS_COUNT; i ++){
+        sum += _widths[i];
+    }
+    NVIC_EnableIRQ(TC4_IRQn);
+    return sum / (float)PULSE_WIDTHS_COUNT;
+}
+```
+
+### PWM Output
+
+Next up I need to generate the PWM signal. I had previously done this on a big banged loop... don't want that, I should properly scope the thing out and run it with a timer I think. I should write a little servo PWM class then... 
+
+*Unfortunately* I put the PWM signal on this board on PA22, where it's TC4 out, same as the hall I'm currenty reading... so I could modify the CAD and put the hall back on TC5-1, or I could carry on and use an interrupt to flip the pin... that's a bit of a hack, but makes muxing easy in software and servo PWM is slow AF anyways, 20ms cycle with 1-2ms pulse widths, so an interrupt is cool here. 
+
+OK, have that set, no problems. Since I am anticipating wanting to do this again...
+
+```cpp
+#define PWM_PORT PORT->Group[0]
+#define PWM_PIN 22 
+#define PWM_PIN_BM (uint32_t)(1 << PWM_PIN)
+#define PWM_PIN_HI PWM_PORT.OUTSET.reg = PWM_PIN_BM
+#define PWM_PIN_LO PWM_PORT.OUTCLR.reg = PWM_PIN_BM
+
+// PWM for servos is 20ms period, 1-2ms duty.
+// do 2MHz input clock, wrap at 40000 ticks 
+// 1ms is 2000 ticks, 2ms is 4000 ticks 
+
+void Servo_PWM::init(void){
+    // set the pin as output: this is toggled on interrupt, not hardware TC 
+    PWM_PORT.DIRSET.reg = PWM_PIN_BM;
+    // setup xtal for timer clk 
+    d51_clock_boss->setup_16mhz_xtal();
+    // disable to setup 
+    TC3->COUNT16.CTRLA.bit.ENABLE = 0;
+    // unmask clock 
+    MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC3;
+    // send clk to ch 
+    GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_CHEN
+        | GCLK_PCHCTRL_GEN(d51_clock_boss->mhz_xtal_gclk_num);
+    // setup timer 
+    TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16
+        | TC_CTRLA_PRESCSYNC_PRESC
+        | TC_CTRLA_PRESCALER_DIV8;
+    // want match pwm (MPWM) mode, should have a TOP register for period, and CC[x] for the swap 
+    TC3->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MPWM;
+    TC3->COUNT16.CC[0].reg = 40000; // CC0 is 'top' in match pwm 
+    TC3->COUNT16.CC[1].reg = 2000; // this should put the first tick at 1ms 
+    // want interrupts
+    TC3->COUNT16.INTENSET.bit.MC0 = 1;
+    TC3->COUNT16.INTENSET.bit.MC1 = 1;
+    // the rest 
+    // ...
+    // now enable 
+    while(TC3->COUNT16.SYNCBUSY.bit.ENABLE);
+    TC3->COUNT16.CTRLA.bit.ENABLE = 1;
+    // enable the IRQ
+    NVIC_EnableIRQ(TC3_IRQn);
+}
+
+// need to finish setup above, to do... something, I think compare at duty cycle and wrap at 20ms
+// then try write(0) and write(1) at some intervals in loop,
+// configure your debugs (currently triggered on pulse_counter events) and watch scope 
+// then roll PID... tune... and do bus action 
+// maybe tune after bus action, have pid-config there, not too hard 
+
+void TC3_Handler(void){
+    if(TC3->COUNT16.INTFLAG.bit.MC0){
+        TC3->COUNT16.INTFLAG.bit.MC0 = 1;
+        PWM_PIN_HI;
+        DEBUG1PIN_ON;
+    }
+    if(TC3->COUNT16.INTFLAG.bit.MC1){
+        TC3->COUNT16.INTFLAG.bit.MC1 = 1;
+        PWM_PIN_LO;
+        DEBUG1PIN_OFF;
+        DEBUG2PIN_TOGGLE;
+    }
+}
+```
+
+So now I want to roll a little PID loop. 
+
+OK, I'm into 'tuning' and it's a pain. Trying to go with just the P term, obv not enough. I'm also routinely tripping my 3A fancy PSU, should hit it with the big one. And even with that, some strange micro resets are ongoing. I'll see about deleting the gnd line from micro-to-esc. 
+
+I suspect (?) the ESC is just throwing noise into the system, causing resets. I also think it supports higher refresh rates, so I'll push that to improve my PID timing... 50hz probably not good enough. Also, spinde inertia will probably change this thing entirely, so I should mount it up before I continue. From there, I might just need to setup an interface to tune PIDs, so that I can do it without this code rewrite thing... but I do feel like getting it into the ballpark is a good move. 
+
+Scaling error to 0-1 domain before running PID helps a tonne, but I am now doubting my rpm reading. Might be the case that motor outputs are flipping the HALL sensor, wouldn't be shocked by this. 
+
+Yep, that's absolutely what's going on. So this project is shot, hall sensors do need to live inside the housing and, broadly, we want encoders and better BLDC control. I'll do the remainder with a linear scaling between 0 and 55k rpm for commands to write direct to the pwm output, no feedback. Makes wiring simpler, etc, and I won't have to chase down this bug that's causing things to reset (or maybe I will anyways!). 
+
+Might still spin the bulid up with the grommets, to see if that makes things less-loud. 
+
+## 2020 09 18
+
+OK, totally sewered yesterday with a parts-lost-in-the-mail issue, meaning I have re-designed the whole spindle assembly. Moving on, I'm getting this set up to listen to remote requests for spindle speed. I think I'm going to do the mapping in JS, then just push 0-1 PWM values to the remote thing. 
+
+OK, mapping in JS: good. This works. 
\ No newline at end of file
-- 
GitLab