 }
 
 
+// force calibration (one motor at a time only!)
+void oscSetPosition(OscMessage &m) {
+  if (m.getInt(0) == MOTOR_ID) {
+    encoder0Pos = (long)m.getFloat(1);
+  }
+}
+
+
+// preferences: try to remember encoder value through a crash
+void oscRememberPosition(OscMessage &m) {
+  if (m.size()==1 || (m.size()==2 && m.getInt(0)==MOTOR_ID)) {
+    int p = m.getInt(m.size()-1);
+    if (p) {
+      EEPROM.write(EEPROM_REMEMBER_POSITION, 1);
+    }
+    else {
+      EEPROM.write(EEPROM_REMEMBER_POSITION, 0);
+    }
+  }  
+}
+
 
 // WATCHDOG TEST
 void oscCrash(OscMessage &m) {

// NETWORK SETUP  -------

// get motor id (0 for NW motor, 1 for NE, 2 for SE, 3 for SW) from EEPROM address 0
int MOTOR_ID = EEPROM.read(0);

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, MOTOR_ID };
IPAddress listeningIP(192,168,LOCALNET,42+MOTOR_ID); // you need to set this

  myPID.SetOutputLimits(-ms, ms);
}

// setupEncoder will check if encoder position is retained in RAM after a crash
// and return true if so
bool setupEncoder() {
  pinMode(encoder0PinA, INPUT); 
  pinMode(encoder0PinB, INPUT); 
  pinMode(encoder0PinZ, INPUT);
  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, RISING);
  
  if (EEPROM.read(EEPROM_REMEMBER_POSITION) == 1) {
    // check if encoder value can be recovered after crash
    if (encoder0Pos ^ encoder0ChecksumKey == encoder0Checksum) {
      // yes! let's claim we're homed
      return true;
    }
  }

  return false;
}

void setupMotorDriver(bool poweron) {
  //pinMode(PULSEPIN, OUTPUT); pwm() will do this
  pinMode(DIRPIN, OUTPUT);
  pinMode(ENAPIN, OUTPUT);
  
  digitalWrite(ENAPIN, poweron); // disable motor driver
  
  // set up interrupts for motor speed control
  Timer1.initialize(1000);
