diff --git a/firmware/fab-step/src/drivers/step_a4950.h b/firmware/fab-step/src/drivers/step_a4950.h
index 63ec2304a0e1c22238dcc08a2421fd036f51c69d..2e5747583ec74a56b24b76c274c1039e58e9273c 100644
--- a/firmware/fab-step/src/drivers/step_a4950.h
+++ b/firmware/fab-step/src/drivers/step_a4950.h
@@ -15,7 +15,7 @@ is; no warranty is provided, and users accept all liability.
 #ifndef STEP_A4950_H_
 #define STEP_A4950_H_
 
-#include <arduino.h> 
+#include <Arduino.h> 
 
 // AIN1 PA14
 // AIN2 PA15 
diff --git a/firmware/fab-step/src/homing.cpp b/firmware/fab-step/src/homing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7896d5025c3745c31e25950541637e2770b1af3d
--- /dev/null
+++ b/firmware/fab-step/src/homing.cpp
@@ -0,0 +1,92 @@
+/*
+homing.cpp
+
+tiny homing routine for fab-step 
+
+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.
+*/
+
+#include "homing.h"
+#include "drivers/step_a4950.h"
+
+endpoint_t* _homeStateEP;
+uint8_t homeState = HOMESTATE_NONE;
+uint32_t homeBackoffStepsTaken = 0;
+
+// home settings 
+boolean homeDir = true;
+unsigned long lastHomeOperation = 0;
+unsigned long homeOperationPeriod = 1000; // in us 
+uint32_t homeBackoffDistance = 100;
+
+void homeSetup(endpoint_t* homeStateEP){
+    // stash this 
+    _homeStateEP = homeStateEP;
+    // make an input 
+    PORT->Group[LIMIT_PORT].DIRCLR.reg = (1 << LIMIT_PIN);
+    PORT->Group[LIMIT_PORT].PINCFG[LIMIT_PIN].bit.INEN = 1;
+    // pullup 
+    PORT->Group[LIMIT_PORT].OUTSET.reg = (1 << LIMIT_PIN);
+}
+
+// return true if limit switch is hit 
+boolean limitHit(void){
+    return (PORT->Group[LIMIT_PORT].IN.reg & (1 << LIMIT_PIN));
+}
+
+void writeHomeSettings(boolean dir, uint32_t stepsPerSecond, uint32_t offset){
+    homeDir = dir;
+    homeOperationPeriod = 1000000 / stepsPerSecond;
+    homeBackoffDistance = offset;
+}
+
+uint8_t getHomeState(void){
+    return homeState;
+}
+
+void startHomingRoutine(void){
+    homeState = HOMESTATE_APPROACH;
+    endpointWrite(_homeStateEP, &homeState, 1);
+}
+
+void runHomingRoutine(void){
+  // run this at a rate... 
+  if(lastHomeOperation + homeOperationPeriod > micros()) return;
+  lastHomeOperation = micros();
+  // state switch; 
+  switch(homeState){
+    case HOMESTATE_NONE:
+      break;
+    case HOMESTATE_APPROACH: 
+      // check for contact, 
+      if(limitHit()){
+        homeState = HOMESTATE_BACKOFF;
+        endpointWrite(_homeStateEP, &homeState, 1);
+        homeBackoffStepsTaken = 0;
+      } else {
+        step_a4950_dir(homeDir);
+        step_a4950_step();
+      }
+      break;
+    case HOMESTATE_BACKOFF:
+      step_a4950_dir(!homeDir);
+      step_a4950_step();
+      homeBackoffStepsTaken ++;
+      if(homeBackoffStepsTaken > homeBackoffDistance){
+        homeState = HOMESTATE_NONE;
+        endpointWrite(_homeStateEP, &homeState, 1);
+      }
+      break;
+    default:
+      // broken, 
+      homeState = HOMESTATE_NONE;
+      endpointWrite(_homeStateEP, &homeState, 1);
+      break;
+  }
+}
\ No newline at end of file
diff --git a/firmware/fab-step/src/homing.h b/firmware/fab-step/src/homing.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b56e633c4f06d27c4179fb8d8bb6e42d7f49dc7
--- /dev/null
+++ b/firmware/fab-step/src/homing.h
@@ -0,0 +1,37 @@
+/*
+homing.h
+
+tiny homing routine for fab-step 
+
+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 HOMING_H_ 
+#define HOMING_H_ 
+
+#include <Arduino.h>
+#include "osape-d21/osape/osap/endpoint.h"
+
+// limit 
+#define LIMIT_PORT 0 
+#define LIMIT_PIN 3 
+
+// home states
+#define HOMESTATE_NONE 0
+#define HOMESTATE_APPROACH 1
+#define HOMESTATE_BACKOFF 2
+
+void homeSetup(endpoint_t* homeStateEP);
+boolean limitHit(void);
+uint8_t getHomeState(void);
+void writeHomeSettings(boolean dir, uint32_t stepsPerSecond, uint32_t offset);
+void startHomingRoutine(void);
+void runHomingRoutine(void);
+
+#endif 
\ No newline at end of file
diff --git a/firmware/fab-step/src/main.cpp b/firmware/fab-step/src/main.cpp
index d15ba6a0f07f85dfc84cc1ca5b1240fc4afeef4a..a2ab555edd0764c649a7dcaccaab49b0f73397dc 100644
--- a/firmware/fab-step/src/main.cpp
+++ b/firmware/fab-step/src/main.cpp
@@ -3,6 +3,7 @@
 #include "config.h"
 
 #include "drivers/step_a4950.h"
+#include "homing.h"
 
 #include "osape-d21/osape/osap/osap.h"
 #include "osape-d21/vertices/vt_usbSerial.h"
@@ -70,6 +71,20 @@ endpoint_t* cScaleEp = osapBuildEndpoint("CScale", onCScaleData);
 // -------------------------------------------------------- HOME ROUTINE
 
 EP_ONDATA_RESPONSES onHomeData(uint8_t* data, uint16_t len){
+  chunk_int32 rate = { .bytes = { data[0], data[1], data[2], data[3] } };
+  chunk_uint32 offset = { .bytes = { data[4], data[5], data[6], data[7] } };
+  // sign of rate is dir, 
+  boolean hdir;
+  uint32_t hrate;
+  if(rate.i < 0) {
+    hdir = false;
+    hrate = -1 * rate.i;
+  } else {
+    hdir = true;
+    hrate = rate.i;
+  }
+  writeHomeSettings(hdir, hrate, offset.u);
+  startHomingRoutine();
   return EP_ONDATA_ACCEPT;
 }
 
@@ -86,7 +101,9 @@ endpoint_t* homeStateEp = osapBuildEndpoint("HomeState");
 void setup() {
   CLKLIGHT_SETUP;
   BUSLIGHT_SETUP;
-  DEBUG1PIN_SETUP;
+  // debug1pin is the limit pin: we can have one or the other
+  //DEBUG1PIN_SETUP;
+  homeSetup(homeStateEp);
   // osap setup...
   osapSetup("fab-step");
   vt_usbSerial_setup();
@@ -119,6 +136,9 @@ unsigned long last_tick = 0;
 void loop() {
   // do osap things, 
   osapLoop(); 
+  limitHit() ? BUSLIGHT_ON : BUSLIGHT_OFF;
+  // do homing things, if need be:
+  if(getHomeState() != HOMESTATE_NONE) runHomingRoutine();
   // blink 
   if(millis() > last_tick + CLK_TICK){
     // step_a4950_step();
@@ -141,6 +161,8 @@ void ucBusDrop_onPacketARx(uint8_t* inBufferA, volatile uint16_t len){
 void ucBusDrop_onRxISR(void){
   // if we dont' have valid steps, bail 
   if(!stepValid) return;
+  // if we're currently homing the motor, bail 
+  if(getHomeState() != HOMESTATE_NONE) return;
   // extract our step mask
   stepMask = 0b00000011 & (stepCache[cachePtr] >> (axisPick * 2));
   // mask -> step api:
diff --git a/log/fab-step-log.md b/log/fab-step-log.md
index 9e8e8ec038da5eb9060dc930f024fb641ab00ce2..df4fc87af2a7ea0b0fb3e2ed3c296490e1433606 100644
--- a/log/fab-step-log.md
+++ b/log/fab-step-log.md
@@ -205,6 +205,8 @@ I think I need a config / setup on the js side.
 
 OK this is almost jogging - I need some motor config, then I think I actually have some driver level debug to do, not unlikely with the VREF stuff. 
 
+### H-Bridge : Code Alignment 
+
 It has come to my attention that I changed the pins on all of these things, and hadn't re-written to use the timer-counter rc filters, whoops. 
 
 Looking a lot better now that I sorted that haha - looks like I still need to scope the pwm'd lines, I am maybe flipping a phase improperly. 
@@ -219,12 +221,108 @@ Now I'm just not seeing it hold while it's microstepping...
 
 OK it's all up, and jogs around w/ the motion system, rad. 
 
-- check that 'microstep' config does what it claims 
-- microstep image
-- bring motor config back to JS side 
-- we need a homing routine still, write this little statemachine pls ! 
+### Homing Routine 
+
+Next thing I need is a little homing routine... I think I will do `homingRate` in steps / second... rather than keep any other info in the motor. 
+
+This is still kind of a pain.
+
+Great, homing works. It's stateful, that's why it's such a pain - and has to work all asynchronously;
+
+```cpp
+#include "homing.h"
+#include "drivers/step_a4950.h"
+
+endpoint_t* _homeStateEP;
+uint8_t homeState = HOMESTATE_NONE;
+uint32_t homeBackoffStepsTaken = 0;
+
+// home settings 
+boolean homeDir = true;
+unsigned long lastHomeOperation = 0;
+unsigned long homeOperationPeriod = 1000; // in us 
+uint32_t homeBackoffDistance = 100;
+
+void homeSetup(endpoint_t* homeStateEP){
+    // stash this 
+    _homeStateEP = homeStateEP;
+    // make an input 
+    PORT->Group[LIMIT_PORT].DIRCLR.reg = (1 << LIMIT_PIN);
+    PORT->Group[LIMIT_PORT].PINCFG[LIMIT_PIN].bit.INEN = 1;
+    // pullup 
+    PORT->Group[LIMIT_PORT].OUTSET.reg = (1 << LIMIT_PIN);
+}
+
+// return true if limit switch is hit 
+boolean limitHit(void){
+    return (PORT->Group[LIMIT_PORT].IN.reg & (1 << LIMIT_PIN));
+}
+
+void writeHomeSettings(boolean dir, uint32_t stepsPerSecond, uint32_t offset){
+    homeDir = dir;
+    homeOperationPeriod = 1000000 / stepsPerSecond;
+    homeBackoffDistance = offset;
+}
+
+uint8_t getHomeState(void){
+    return homeState;
+}
+
+void startHomingRoutine(void){
+    homeState = HOMESTATE_APPROACH;
+    endpointWrite(_homeStateEP, &homeState, 1);
+}
+
+void runHomingRoutine(void){
+  // run this at a rate... 
+  if(lastHomeOperation + homeOperationPeriod > micros()) return;
+  lastHomeOperation = micros();
+  // state switch; 
+  switch(homeState){
+    case HOMESTATE_NONE:
+      break;
+    case HOMESTATE_APPROACH: 
+      // check for contact, 
+      if(limitHit()){
+        homeState = HOMESTATE_BACKOFF;
+        endpointWrite(_homeStateEP, &homeState, 1);
+        homeBackoffStepsTaken = 0;
+      } else {
+        step_a4950_dir(homeDir);
+        step_a4950_step();
+      }
+      break;
+    case HOMESTATE_BACKOFF:
+      step_a4950_dir(!homeDir);
+      step_a4950_step();
+      homeBackoffStepsTaken ++;
+      if(homeBackoffStepsTaken > homeBackoffDistance){
+        homeState = HOMESTATE_NONE;
+        endpointWrite(_homeStateEP, &homeState, 1);
+      }
+      break;
+    default:
+      // broken, 
+      homeState = HOMESTATE_NONE;
+      endpointWrite(_homeStateEP, &homeState, 1);
+      break;
+  }
+}
+```
+
+Now I just want to touch up the motor APIs in JS, and add the steps-per-unit adjust stuff in the motion-head, etc... I should be pretty close now. 
+
+### Power State API 
+
+- do need this, and it needs to be part of the 'setup' routine 
+
+### New Motor API and JS Config 
+
+- check that 'microstep' config does what it claims (?) 
 - now ... we need to reconsider how / whomst makes SPU / dir configs, etc 
     - inversion is per-motor 
     - so is microstepping 
     - so is the axis pick 
-    - spu is per-axis in the head 
\ No newline at end of file
+    - spu is per-axis in the head 
+
+A final treat would be reconfiguring the ucbus-stepper firmware to match this. Another treat would be a tiny demo video for Donny. 
\ No newline at end of file