Skip to content

Instantly share code, notes, and snippets.

@pinski1
Last active November 27, 2023 22:14
Show Gist options
  • Save pinski1/6552054d6d738eaf9bb94b8d5970e7e5 to your computer and use it in GitHub Desktop.
Save pinski1/6552054d6d738eaf9bb94b8d5970e7e5 to your computer and use it in GitHub Desktop.
Code for a ZTW Spider Lite 18A v2 brushless ESC into a solenoid driver
/** ZTW Spider Lite v2 ESC to Solenoid Driver
ZTW Spider Lite 18A v2 uses the ATMega8A and
none of the MOSFET enable pins are PWM capable.
Pin out:
https://github.com/bitdump/BLHeli/blob/master/Atmel/ZTW_Spider_Lite_18Av2.inc
To Do:
[ ] Monitor VCC via PIN_MUX_V
[ ] Use soft PWM to put valve into low power mode
[ ] Use varying `durationRC` to set valve duration
**/
#include <elapsedMillis.h>
/** Settings **/
#define MAX_MISSING_RC (22000UL*3) // max time between pulses, μs
#define LOOP_RATE (10000) // loop duration (100Hz, μs
#define THRESHOLD_SOL (1780) // solenoid threshold, μs
#define SOL_DURATION_TOTAL (100000) // how long to open for, μs
#define DEADTIME_LOW (10)
#define DEADTIME_HIGH (8)
#define CHARGE_PUMP_REFRESH (500) // time to let the charge pump refresh
#define VERSION_MAJOR (0)
#define VERSION_MINOR (2)
#define VERSION_PATCH (0)
/** Pin Map **/
#define PIN_RC_IN (2) // PD2, INT0
#define PIN_AP_FET (17) // PC3 (no PWM)
#define PIN_AN_FET (5) // PD5
#define PIN_CP_FET (4) // PD4 (no PWM)
#define PIN_CN_FET (8) // PB0
#define PIN_MUX_V (A2) // PC2, 220kΩ from Vbat, 51kΩ to GND, 10.10V → 1.900V at ADC2
#define PIN_MUX_T (A1) // PC1, 10kΩ NTC to 5V, 820Ω to GND, at ADC1
/** Globals **/
elapsedMicros timerLoop; // timer for loop
elapsedMicros timerSol; // timer for solenoid
elapsedMicros timerRC; // timer for RC signals
volatile long durationRC; // RC pulse length
volatile int lastPinRC; // last RC pin state
enum states {sFailsafe, sIdle, sActive} currentState;
void setup(void) {
delay(10);
// configure pins
pinMode(PIN_RC_IN, INPUT);
pinMode(PIN_AP_FET, OUTPUT);
pinMode(PIN_AN_FET, OUTPUT);
pinMode(PIN_CP_FET, OUTPUT);
pinMode(PIN_CN_FET, OUTPUT);
pinMode(PIN_MUX_V, INPUT);
pinMode(PIN_MUX_T, INPUT);
// set starting pin states
digitalWrite(PIN_AP_FET, LOW);
digitalWrite(PIN_AN_FET, LOW);
digitalWrite(PIN_CP_FET, LOW);
digitalWrite(PIN_CN_FET, LOW);
// set up interrupt for RC signals
attachInterrupt(digitalPinToInterrupt(PIN_RC_IN), handlerRC, CHANGE);
// setup the counters
timerRC = MAX_MISSING_RC + 5; // set to 'safe'
lastPinRC = LOW;
currentState = sFailsafe;
durationRC = 0x00UL;
timerLoop = 0x00UL;
}
void loop(void) {
if (timerLoop >= LOOP_RATE) {
timerLoop -= LOOP_RATE;
if (timerRC >= MAX_MISSING_RC) currentState = sFailsafe;
switch (currentState) {
case sIdle:
if (durationRC > THRESHOLD_SOL) {
setSolenoid(HIGH);
timerSol = 0x00UL;
currentState = sActive;
}
else setSolenoid(LOW);
break;
case sActive:
if (timerSol >= SOL_DURATION_TOTAL) {
setSolenoid(LOW);
}
if (durationRC < THRESHOLD_SOL) {
setSolenoid(LOW);
currentState = sIdle;
}
break;
case sFailsafe:
default:
setSolenoid(LOW);
if (durationRC > 500 && durationRC < 2500) currentState = sIdle;
}
}
}
void handlerRC(void) {
if (digitalRead(PIN_RC_IN) == HIGH && lastPinRC == LOW) {
// rising edge
timerRC = 0x00UL;
}
else if (digitalRead(PIN_RC_IN) == LOW && lastPinRC == HIGH) {
//falling edge
durationRC = timerRC;
}
lastPinRC = digitalRead(PIN_RC_IN);
}
void setSolenoid(int value) {
// channel A will be set to GND permanently
// channel C will be set to GND/VBAT based on `value`
digitalWrite(PIN_AN_FET, HIGH);
if (value == HIGH) {
if (digitalRead(PIN_CP_FET) != HIGH) {
digitalWrite(PIN_CN_FET, LOW);
delayMicroseconds(DEADTIME_HIGH / 2);
digitalWrite(PIN_CP_FET, HIGH);
}
else {
// else already set!
// but need to drop it down to allow the charge pump to refill
digitalWrite(PIN_CP_FET, LOW);
delayMicroseconds(CHARGE_PUMP_REFRESH);
digitalWrite(PIN_CP_FET, HIGH);
}
}
else {
if (digitalRead(PIN_CN_FET) != HIGH) {
digitalWrite(PIN_CP_FET, LOW);
delayMicroseconds(DEADTIME_LOW / 2);
digitalWrite(PIN_CN_FET, HIGH);
}
// else already set!
}
return;
}
unsigned int getVoltage(void) {
const unsigned long lsb2MicroVolt = 25971; // ((5000000μV ÷ 1023)/51kΩ)*(220kΩ + 51kΩ)
unsigned int voltage = analogRead(PIN_MUX_V) * lsb2MicroVolt;
voltage = (voltage + 500) / 1000; // convert to millivolts as accurately as possible
return voltage;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment