2023-06-18 00:12:10 +00:00
//TODO:
/*
- reset Trip to 0 by button press or something . function : resetTrip ( )
*/
2021-02-26 18:37:12 +00:00
# include <Arduino.h>
2023-05-19 14:45:56 +00:00
2023-03-02 20:18:48 +00:00
# include "definitions.h"
2023-04-15 22:13:02 +00:00
//#include "structs.h"
2023-05-23 19:48:40 +00:00
2023-03-02 20:18:48 +00:00
# include <TimeLib.h> //for teensy rtc
2023-05-23 19:48:40 +00:00
# include "helpfunctions.h"
2023-04-15 22:13:02 +00:00
# include "hoverboard-esc-serial-comm.h"
2023-05-23 19:48:40 +00:00
# include "led.h"
2023-06-13 21:45:17 +00:00
# include "temperature.h"
2023-05-23 19:48:40 +00:00
2024-07-13 17:19:10 +00:00
2023-06-02 17:18:21 +00:00
String getLogFilename ( ) ;
2024-07-13 17:19:10 +00:00
bool getDatalogging ( ) ;
2023-03-02 21:05:48 +00:00
# include "display.h"
2023-04-15 22:13:02 +00:00
# include "logging.h"
2023-06-02 17:18:21 +00:00
2023-02-28 23:31:46 +00:00
# include "ADS1X15.h"
2023-04-15 22:13:02 +00:00
2023-05-19 14:45:56 +00:00
2023-06-13 21:45:17 +00:00
2024-06-26 18:41:11 +00:00
ESCSerialComm escFront ( Serial7 ) ;
ESCSerialComm escRear ( Serial2 ) ;
2023-04-15 22:13:02 +00:00
2024-06-26 18:41:11 +00:00
ADS1115 ADS ( 0x48 , & Wire ) ; //Pins: https://www.pjrc.com/teensy/td_libs_Wire.html
2021-10-04 16:41:16 +00:00
2021-03-09 23:59:54 +00:00
/*
2024-06-26 18:41:11 +00:00
Serial Hoverboard Colors
RX ( Green ) connect to TX on Teensy
TX ( Blue ) connect to RX on Teensy
2021-03-09 23:59:54 +00:00
*/
2021-03-31 19:16:48 +00:00
2021-03-02 22:48:20 +00:00
2023-02-28 23:31:46 +00:00
void readADS ( ) ;
2021-03-31 19:16:48 +00:00
void readADC ( ) ;
void failChecks ( ) ;
2023-04-15 22:13:02 +00:00
//void sendCMD();
2023-06-02 19:10:45 +00:00
void calculateSetSpeed ( unsigned long timediff ) ;
2024-07-14 08:54:49 +00:00
2023-03-02 20:18:48 +00:00
2021-03-31 19:58:23 +00:00
void leds ( ) ;
2021-03-20 20:20:17 +00:00
2021-05-15 16:56:42 +00:00
void readButtons ( ) ;
2024-03-02 20:55:40 +00:00
void readADSButtons ( ) ;
2021-05-15 16:56:42 +00:00
2023-08-27 16:16:51 +00:00
uint16_t linearizeThrottle ( uint16_t v , const uint16_t * pthrottleCurvePerMM , int arraysize , bool sorteddescending ) ;
2021-08-06 14:20:48 +00:00
2021-02-26 18:37:12 +00:00
2023-03-02 20:18:48 +00:00
time_t getTeensy3Time ( ) ;
2021-03-02 22:48:20 +00:00
2021-02-26 18:37:12 +00:00
// ########################## SETUP ##########################
void setup ( )
{
2021-03-20 20:20:17 +00:00
Serial . begin ( SERIAL_BAUD ) ; //Debug and Program
2021-02-26 18:37:12 +00:00
2021-03-20 20:20:17 +00:00
Serial1 . begin ( SERIAL_LOG_BAUD ) ; //TX1=1, RX1=0
2024-06-26 18:41:11 +00:00
pinMode ( PIN_PWRBUTTON , INPUT_PULLUP ) ; //Pressed=High
2024-07-13 16:18:58 +00:00
if ( ! digitalRead ( PIN_PWRBUTTON ) ) { //button is not pressed during startup means teensy is powered externally (usb)
datalogging = false ; //disable logging when connected via usb to not clutter up sd card
Serial . println ( " PWRBUTTON not pressed. Logging disabled! " ) ;
}
2021-03-20 15:13:16 +00:00
2021-03-25 17:37:33 +00:00
pinMode ( PIN_LED_START , OUTPUT ) ; //Active High
2023-06-03 10:50:29 +00:00
pinMode ( PIN_FAN , OUTPUT ) ;
digitalWrite ( PIN_FAN , HIGH ) ; //Turn fan on during startup for debugging purposes
2021-03-31 19:58:23 +00:00
2023-04-15 22:13:02 +00:00
2021-02-26 18:37:12 +00:00
2021-03-20 15:13:16 +00:00
pinMode ( PIN_LATCH_ENABLE , OUTPUT ) ;
digitalWrite ( PIN_LATCH_ENABLE , HIGH ) ; //latch on
2023-05-19 14:45:56 +00:00
init_led ( ) ;
2023-05-23 19:46:34 +00:00
led_testLEDSBlocking ( ) ;
2023-03-02 21:05:48 +00:00
2023-04-15 22:13:02 +00:00
delay ( 2000 ) ;
Serial . println ( " Init Functions " ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 0 , 1 ) ;
2023-05-19 14:45:56 +00:00
2023-05-23 19:46:34 +00:00
bool initResult = false ;
initResult = display_init ( ) ;
2024-07-13 19:16:57 +00:00
if ( ! initResult ) {
writeLogComment ( ( unsigned long ) millis ( ) , " SSD1306 allocation failed " ) ;
}
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 1 , initResult ) ;
2021-02-26 18:37:12 +00:00
2023-05-23 19:46:34 +00:00
initResult = initLogging ( ) ;
2024-07-13 16:18:58 +00:00
led_simpeProgress ( 2 , ( initResult ? ( datalogging ? 1 : 2 ) : 0 ) ) ; //0=sd card fail, 1=sd ok and logging, 2(warn)=sd ok and logging off
2023-06-18 00:12:10 +00:00
2023-04-15 22:13:02 +00:00
escFront . init ( ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 3 , true ) ;
2023-04-15 22:13:02 +00:00
escRear . init ( ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 4 , true ) ;
2023-04-15 22:13:02 +00:00
2023-02-28 23:31:46 +00:00
delay ( 2000 ) ;
Serial . println ( " Wait finished. Booting.. " ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 5 , true ) ;
2023-02-28 23:31:46 +00:00
//init ADS1115
2023-06-13 21:45:17 +00:00
2023-02-28 23:31:46 +00:00
if ( ! ADS . begin ( ) ) {
Serial . println ( " Error: " ) ; delay ( 2000 ) ; Serial . println ( " ADS1115 Init Error! " ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 6 , false ) ;
2023-04-15 22:13:02 +00:00
writeLogComment ( ( unsigned long ) millis ( ) , " Error ADS1115 Init " ) ;
2023-05-23 19:46:34 +00:00
} else {
ADS . setGain ( 0 ) ;
ADS . setDataRate ( 7 ) ; // Read Interval: 7-> 2ms, 6-> 3-4ms , 5-> 5-6ms, 4-> 9ms, 0-> 124ms
// also set ADSREADPERIOD to at least the read interval
ADS . requestADC ( 0 ) ; //Start requesting a channel
led_simpeProgress ( 6 , true ) ;
2023-02-28 23:31:46 +00:00
}
delay ( 10 ) ;
2023-06-03 10:50:29 +00:00
for ( uint8_t i = 0 ; i < 4 ; i + + ) { //read all channels once to have adc readings ready in first loop (to prevent premature failsafe)
readADS ( ) ;
delay ( 10 ) ;
}
2023-05-23 19:46:34 +00:00
2023-02-28 23:31:46 +00:00
2021-10-04 16:41:16 +00:00
setSyncProvider ( getTeensy3Time ) ; //See https://www.pjrc.com/teensy/td_libs_Time.html#teensy3
if ( timeStatus ( ) ! = timeSet ) {
Serial . println ( " Unable to sync with the RTC " ) ;
2024-07-13 18:58:27 +00:00
writeLogComment ( ( unsigned long ) millis ( ) , " Unable to sync with the RTC " ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 7 , false ) ;
2021-10-04 16:41:16 +00:00
} else {
Serial . println ( " RTC has set the system time " ) ;
2023-05-23 19:46:34 +00:00
led_simpeProgress ( 7 , true ) ;
2021-10-04 16:41:16 +00:00
}
2023-06-13 21:45:17 +00:00
2023-06-18 00:12:10 +00:00
2024-07-13 16:18:58 +00:00
if ( sdcard_available ) { //sd init was successful
2023-06-18 00:12:10 +00:00
initResult = loadTripSD ( ) ;
} else {
initResult = false ;
}
led_simpeProgress ( 8 , initResult ) ;
2023-06-13 21:45:17 +00:00
initTemperature ( ) ;
2023-06-18 00:12:10 +00:00
led_simpeProgress ( 9 , true ) ;
2023-05-23 19:46:34 +00:00
2021-10-04 16:41:16 +00:00
2023-04-15 22:13:02 +00:00
writeLogComment ( millis ( ) , " Setup Finished " ) ;
2021-10-04 16:41:16 +00:00
2023-05-23 19:46:34 +00:00
led_simpleProgressWait ( ) ; //wait longer if any errors were displayed with led_simpeProgress()
2024-07-12 23:30:20 +00:00
Serial . println ( " Ready " ) ;
2021-10-04 16:41:16 +00:00
}
2021-03-02 22:48:20 +00:00
2023-06-02 17:18:21 +00:00
2023-04-15 22:13:02 +00:00
2021-02-26 18:37:12 +00:00
// ########################## LOOP ##########################
void loop ( ) {
2023-02-28 23:31:46 +00:00
//Serial.print("Loopduration="); Serial.println(); //loopduration is at max 11ms
2021-02-26 18:37:12 +00:00
loopmillis = millis ( ) ; //read millis for this cycle
2024-06-26 19:42:16 +00:00
2023-06-13 21:45:17 +00:00
2023-02-28 23:31:46 +00:00
2021-03-20 15:13:16 +00:00
2023-06-13 21:45:17 +00:00
if ( ADS . isConnected ( ) & & ( loopmillis - last_adsread > ADSREADPERIOD ) ) { //read teensy adc and filter
2023-02-28 23:31:46 +00:00
last_adsread = loopmillis ;
if ( ADS . isBusy ( ) = = false ) //reads a register on ads
{
readADS ( ) ;
} else {
Serial . println ( " Unnecessary ADS poll. Increase ADSREADPERIOD " ) ;
}
}
static unsigned long last_adcread = 0 ;
2021-03-31 19:16:48 +00:00
if ( loopmillis - last_adcread > ADCREADPERIOD ) { //read teensy adc and filter
2021-03-20 15:13:16 +00:00
last_adcread = loopmillis ;
2023-02-28 23:31:46 +00:00
2021-03-31 19:16:48 +00:00
readADC ( ) ;
2023-02-28 23:31:46 +00:00
}
2021-05-15 16:56:42 +00:00
2023-02-28 23:31:46 +00:00
static unsigned long last_buttonread = 0 ;
if ( loopmillis - last_buttonread > BUTTONREADPERIOD ) { //read digital input states
last_buttonread = loopmillis ;
2021-05-15 16:56:42 +00:00
readButtons ( ) ;
2024-03-02 20:55:40 +00:00
readADSButtons ( ) ;
2021-03-20 15:13:16 +00:00
}
2021-03-20 20:20:17 +00:00
2024-06-26 19:42:16 +00:00
2021-03-31 19:50:30 +00:00
failChecks ( ) ;
2021-02-26 18:37:12 +00:00
2023-04-15 22:13:02 +00:00
static unsigned long last_calculateSetSpeed = 0 ;
if ( loopmillis - last_calculateSetSpeed > SENDPERIOD ) {
2023-06-02 19:10:45 +00:00
unsigned long _timediff = loopmillis - last_calculateSetSpeed ;
last_calculateSetSpeed = loopmillis ;
calculateSetSpeed ( _timediff ) ;
2023-06-03 10:21:40 +00:00
//Update Statistics
max_filtered_currentAll = max ( max_filtered_currentAll , filtered_currentAll ) ;
min_filtered_currentAll = min ( min_filtered_currentAll , filtered_currentAll ) ;
2023-07-10 13:43:01 +00:00
max_filtered_wattAll = max ( max_filtered_wattAll , filtered_currentAll * ( escFront . getFeedback_batVoltage ( ) + escRear . getFeedback_batVoltage ( ) ) / 2.0 ) ;
min_filtered_wattAll = min ( min_filtered_wattAll , filtered_currentAll * ( escFront . getFeedback_batVoltage ( ) + escRear . getFeedback_batVoltage ( ) ) / 2.0 ) ;
2023-06-03 10:21:40 +00:00
max_meanSpeed = max ( max_meanSpeed , ( escFront . getMeanSpeed ( ) + escRear . getMeanSpeed ( ) ) / 2 ) ;
if ( ! armed ) { //reset statistics if disarmed
max_filtered_currentAll = 0 ;
min_filtered_currentAll = 0 ;
2023-07-10 13:43:01 +00:00
max_filtered_wattAll = 0 ;
min_filtered_wattAll = 0 ;
2023-06-03 10:21:40 +00:00
max_meanSpeed = 0 ;
}
2023-04-15 22:13:02 +00:00
}
2024-06-26 19:42:16 +00:00
2023-04-15 22:13:02 +00:00
escFront . update ( loopmillis ) ;
escRear . update ( loopmillis ) ;
2023-06-18 00:12:10 +00:00
static unsigned long last_statsupdate = 0 ;
# define STATSUPDATEINTERVAL 100
if ( loopmillis - last_statsupdate > STATSUPDATEINTERVAL ) {
minSpeedms = min ( escFront . getWheelspeed_L ( ) , min ( escFront . getWheelspeed_R ( ) , min ( escRear . getWheelspeed_L ( ) , escRear . getWheelspeed_R ( ) ) ) ) ; //take speed of slowest wheel
float _tripincrease = abs ( minSpeedms ) * ( ( loopmillis - last_statsupdate ) / 1000.0 ) ;
trip + = _tripincrease ;
overallTrip + = _tripincrease ;
float _currentIncrease = ( escFront . getFiltered_curL ( ) + escFront . getFiltered_curR ( ) + escRear . getFiltered_curL ( ) + escRear . getFiltered_curR ( ) ) * ( ( loopmillis - last_statsupdate ) / 1000.0 ) / 3600.0 ; //amp hours
2023-07-10 13:43:01 +00:00
float _watthoursIncrease = ( ( escFront . getFiltered_curL ( ) + escFront . getFiltered_curR ( ) ) * escFront . getFeedback_batVoltage ( ) + ( escRear . getFiltered_curL ( ) + escRear . getFiltered_curR ( ) ) * escRear . getFeedback_batVoltage ( ) ) * ( ( loopmillis - last_statsupdate ) / 1000.0 ) / 3600.0 ; //amp hours
2023-06-18 00:12:10 +00:00
currentConsumed + = _currentIncrease ;
overallCurrentConsumed + = _currentIncrease ;
2023-07-10 13:43:01 +00:00
watthoursConsumed + = _watthoursIncrease ;
overallWatthoursConsumed + = _watthoursIncrease ;
2023-06-18 00:12:10 +00:00
last_statsupdate = loopmillis ;
}
2024-07-14 08:54:49 +00:00
2021-03-20 20:20:17 +00:00
2023-04-15 22:13:02 +00:00
loggingLoop ( loopmillis , escFront , escRear ) ;
2023-06-18 00:12:10 +00:00
if ( ! armed & & ! statswritten ) { //write stats only once when disarmed
statswritten = true ;
writeTrip ( loopmillis , escFront , escRear ) ;
}
if ( statswritten & & armed ) {
statswritten = false ;
}
2021-03-31 19:50:30 +00:00
2021-03-31 19:58:23 +00:00
leds ( ) ;
2023-05-23 19:46:34 +00:00
led_update ( loopmillis , escFront , escRear ) ; //ws2812 led ring
2021-03-31 19:58:23 +00:00
2023-03-02 21:05:48 +00:00
static unsigned long last_display_update = 0 ;
if ( loopmillis - last_display_update > DISPLAYUPDATEPERIOD ) {
last_display_update = loopmillis ;
2023-05-23 20:01:07 +00:00
display_update ( escFront , escRear ) ;
2023-03-02 21:05:48 +00:00
}
2023-06-03 10:50:29 +00:00
2024-06-26 19:42:16 +00:00
2023-06-13 21:45:17 +00:00
//Temperature
2024-06-26 19:42:16 +00:00
if ( ! temperatureLoop ( loopmillis ) ) {
writeLogComment ( loopmillis , " Request Temperatures Timeout! " ) ;
}
2023-06-13 21:45:17 +00:00
2024-06-26 19:42:16 +00:00
2023-06-03 10:50:29 +00:00
//Fan
static unsigned long last_fan_update = 0 ;
# define FANUPDATEPERIOD 5000
float fan_turn_on_temp = 45 ;
float fan_turn_off_temp = 32 ;
if ( loopmillis - last_fan_update > FANUPDATEPERIOD ) {
last_fan_update = loopmillis ;
boolean fanstatus = digitalRead ( PIN_FAN ) ;
2023-06-13 21:45:17 +00:00
//float temp=max(escFront.getFeedback_boardTemp(),escRear.getFeedback_boardTemp());
2024-07-14 08:54:49 +00:00
float temp = max ( temp_Front , temp_Rear ) ;
if ( temp_Front = = DEVICE_DISCONNECTED_C | | temp_Rear = = DEVICE_DISCONNECTED_C ) { //temperature error
2023-06-03 10:50:29 +00:00
digitalWrite ( PIN_FAN , HIGH ) ; //force fan on
2023-06-18 00:12:10 +00:00
2024-07-14 08:54:49 +00:00
} else { //normal temperature control_currentIncrease
2023-06-18 00:12:10 +00:00
2023-06-03 10:50:29 +00:00
if ( ! fanstatus ) { //fan is off
if ( temp > = fan_turn_on_temp ) {
digitalWrite ( PIN_FAN , HIGH ) ;
}
} else { //fan is on
if ( temp < = fan_turn_off_temp ) {
digitalWrite ( PIN_FAN , LOW ) ;
}
}
}
2023-06-18 00:12:10 +00:00
2023-06-03 10:50:29 +00:00
}
2024-07-13 17:19:10 +00:00
serialCommandLoop ( loopmillis , escFront , escRear ) ;
2024-07-12 23:30:20 +00:00
2024-07-13 17:19:10 +00:00
looptime_duration_min = min ( looptime_duration_min , millis ( ) - loopmillis ) ;
looptime_duration_max = max ( looptime_duration_max , millis ( ) - loopmillis ) ;
2023-06-17 20:28:08 +00:00
2021-03-09 21:21:21 +00:00
}
2021-03-31 19:50:30 +00:00
2021-10-04 16:41:16 +00:00
time_t getTeensy3Time ( )
{
return Teensy3Clock . get ( ) ;
}
2021-03-31 19:50:30 +00:00
2021-03-09 21:21:21 +00:00
2021-03-31 19:16:48 +00:00
2023-02-28 23:31:46 +00:00
void readADS ( ) { //sequentially read ads and write to variable
/*static unsigned long _lastReadADS=0;
Serial . print ( " readADS Interval= " ) ; Serial . println ( millis ( ) - _lastReadADS ) ;
_lastReadADS = millis ( ) ; */
static uint8_t ads_input_switch = 0 ;
int16_t ads_val = ADS . getValue ( ) ; //get value from last selected channel
switch ( ads_input_switch ) {
case 0 : //Throttle Sensor A
ads_throttle_A_raw = ads_val ;
break ;
case 1 : //Throttle Sensor B
ads_throttle_B_raw = ads_val ;
break ;
case 2 : //Brake
ads_brake_raw = ads_val ;
break ;
2024-03-02 20:55:40 +00:00
case 3 : //Buttons
2023-02-28 23:31:46 +00:00
ads_control_raw = ads_val ;
break ;
}
ads_input_switch + + ;
ads_input_switch % = 4 ; //max 4 channels
ADS . requestADC ( ads_input_switch ) ; // request a new one
}
2021-03-31 19:16:48 +00:00
2021-03-31 19:50:30 +00:00
// #### LOOPFUNCTIONS
2021-03-31 19:16:48 +00:00
void readADC ( ) {
2023-08-27 16:16:51 +00:00
//Serial.print(ads_throttle_A_raw); Serial.print('\t');
//Serial.print(ads_throttle_B_raw); Serial.print('\t');
//Serial.print(ads_brake_raw); Serial.print('\t');
//Serial.print(ads_control_raw); Serial.println();
//throttle_raw = (ads_throttle_A_raw+ads_throttle_B_raw)/2.0*THROTTLE_ADC_FILTER + throttle_raw*(1-THROTTLE_ADC_FILTER); //apply filter
throttle_rawA = ads_throttle_A_raw ;
throttle_rawB = ads_throttle_B_raw ;
2022-08-28 13:42:10 +00:00
2023-02-28 23:31:46 +00:00
//maps throttle curve to be linear
2023-08-27 16:16:51 +00:00
//throttle_pos=max(0,min(1000,linearizeThrottle(throttle_raw))); //map and constrain
throttle_posA = max ( 0 , min ( 1000 , linearizeThrottle ( ads_throttle_A_raw , throttleCurvePerMM_A , sizeof ( throttleCurvePerMM_A ) / sizeof ( throttleCurvePerMM_A [ 0 ] ) , throttleCurvePerMM_A_Descending ) ) ) ; //map and constrain
throttle_posB = max ( 0 , min ( 1000 , linearizeThrottle ( ads_throttle_B_raw , throttleCurvePerMM_B , sizeof ( throttleCurvePerMM_B ) / sizeof ( throttleCurvePerMM_B [ 0 ] ) , throttleCurvePerMM_B_Descending ) ) ) ; //map and constrain
//Serial.print(throttle_posA); Serial.print('\t');
//Serial.print(throttle_posB); Serial.print('\t');
int16_t throttle_posMean = ( throttle_posA + throttle_posB ) / 2.0 ;
throttle_pos = throttle_posMean * THROTTLE_ADC_FILTER + throttle_pos * ( 1 - THROTTLE_ADC_FILTER ) ; //apply filter
2023-02-28 23:31:46 +00:00
brake_raw = ads_brake_raw ;
2021-03-31 19:50:30 +00:00
brake_pos = max ( 0 , min ( 1000 , map ( brake_raw , calib_brake_min , calib_brake_max , 0 , 1000 ) ) ) ; //map and constrain
2021-05-29 14:36:39 +00:00
//brake_pos = (int16_t)(pow((brake_pos/1000.0),2)*1000);
2021-03-31 19:50:30 +00:00
2023-02-28 23:31:46 +00:00
2023-06-02 17:18:21 +00:00
if ( throttle_pos > 0 | | ( ( escFront . getMeanSpeed ( ) + escRear . getMeanSpeed ( ) ) / 2.0 ) > 0.5 | | ( ! reverse_enabled & & brake_pos > 0 ) ) { //reset idle time on these conditions (disables reverse driving)
2021-05-29 22:07:43 +00:00
last_notidle = loopmillis ;
reverse_enabled = false ;
}
2021-08-06 14:25:43 +00:00
2021-05-29 22:07:43 +00:00
if ( loopmillis - last_notidle > REVERSE_ENABLE_TIME ) {
reverse_enabled = true ;
}
2021-03-31 19:50:30 +00:00
int16_t throttlebreak_pos = throttle_pos - brake_pos * 2 ; //reduce throttle_when applying brake
throttle_pos = constrain ( throttlebreak_pos , 0 , 1000 ) ;
brake_pos = constrain ( - throttlebreak_pos / 2 , 0 , 1000 ) ; //rescale brake value from throttlebreak_pos
2021-03-31 19:58:23 +00:00
2021-03-31 19:16:48 +00:00
}
void failChecks ( ) {
2023-09-08 07:10:08 +00:00
static bool laststate_Front_getControllerConnected ;
if ( ! escFront . getControllerConnected ( ) & & laststate_Front_getControllerConnected ) { //controller got disconnected and was connected before
laststate_Front_getControllerConnected = false ;
writeLogComment ( loopmillis , " Controller Front feedback timeout " ) ;
} else if ( escFront . getControllerConnected ( ) & & ! laststate_Front_getControllerConnected ) { //controller was disconnected and is now connected
laststate_Front_getControllerConnected = true ;
writeLogComment ( loopmillis , " Controller Front connected " ) ;
}
static bool laststate_Rear_getControllerConnected ;
if ( ! escRear . getControllerConnected ( ) & & laststate_Rear_getControllerConnected ) { //controller got disconnected and was connected before
laststate_Rear_getControllerConnected = false ;
writeLogComment ( loopmillis , " Controller Rear feedback timeout " ) ;
} else if ( escRear . getControllerConnected ( ) & & ! laststate_Rear_getControllerConnected ) { //controller was disconnected and is now connected
laststate_Rear_getControllerConnected = true ;
writeLogComment ( loopmillis , " Controller Rear connected " ) ;
2021-03-31 19:16:48 +00:00
}
2023-04-15 22:13:02 +00:00
controllers_connected = escFront . getControllerConnected ( ) & escRear . getControllerConnected ( ) ;
2021-03-31 19:16:48 +00:00
2021-03-31 19:50:30 +00:00
//ADC Range Check
2023-08-27 16:16:51 +00:00
static unsigned long throttle_ok_time = 0 ;
if ( ( ads_throttle_A_raw > = failsafe_throttle_min_A ) & ( ads_throttle_A_raw < = failsafe_throttle_max_A ) & ( ads_throttle_B_raw > = failsafe_throttle_min_B ) & ( ads_throttle_B_raw < = failsafe_throttle_max_B ) ) { //inside safe range (to check if wire got disconnected)
2021-03-31 19:50:30 +00:00
throttle_ok_time = loopmillis ;
}
if ( loopmillis > throttle_ok_time + ADC_OUTOFRANGE_TIME ) { //not ok for too long
if ( ! error_throttle_outofrange ) {
error_throttle_outofrange = true ;
2023-08-27 16:16:51 +00:00
writeLogComment ( loopmillis , " Error Throttle ADC Out of Range. A= " + ( String ) ads_throttle_A_raw + " B= " + ( String ) ads_throttle_B_raw ) ;
2021-03-31 19:50:30 +00:00
}
2023-02-28 23:31:46 +00:00
//Serial.print("Error Throttle ADC Out of Range="); Serial.println(throttle_raw);
2021-03-31 19:50:30 +00:00
}
2023-08-27 16:16:51 +00:00
static unsigned long throttlediff_ok_time = 0 ;
if ( abs ( throttle_posA - throttle_posB ) < = failsafe_throttle_maxDiff ) { //inside safe range (to check if wire got disconnected)
throttlediff_ok_time = loopmillis ;
}
if ( loopmillis > throttlediff_ok_time + ADC_DIFFHIGH_TIME ) { //not ok for too long
2023-09-15 22:15:32 +00:00
if ( ! error_throttle_difftoohigh ) {
error_throttle_difftoohigh = true ;
2023-08-27 16:16:51 +00:00
writeLogComment ( loopmillis , " Error Throttle Diff too High. A= " + ( String ) ads_throttle_A_raw + " B= " + ( String ) ads_throttle_B_raw ) ;
}
//Serial.print("Error Throttle ADC Out of Range="); Serial.println(throttle_raw);
}
static unsigned long brake_ok_time = 0 ;
2021-03-31 19:50:30 +00:00
if ( ( brake_raw > = failsafe_brake_min ) & ( brake_raw < = failsafe_brake_max ) ) { //outside safe range. maybe wire got disconnected
brake_ok_time = loopmillis ;
}
if ( loopmillis > brake_ok_time + ADC_OUTOFRANGE_TIME ) { //not ok for too long
if ( ! error_brake_outofrange ) {
error_brake_outofrange = true ;
2023-08-27 16:16:51 +00:00
writeLogComment ( loopmillis , " Error Brake ADC Out of Range. ADC= " + ( String ) brake_raw ) ;
2021-03-31 19:50:30 +00:00
}
2023-02-28 23:31:46 +00:00
//Serial.print("Error Brake ADC Out of Range="); Serial.println(brake_raw);
}
# define ADS_MAX_READ_INTERVAL 100
if ( loopmillis - last_adsread > ADS_MAX_READ_INTERVAL ) {
if ( ! error_ads_max_read_interval ) {
error_ads_max_read_interval = true ;
2023-04-15 22:13:02 +00:00
writeLogComment ( loopmillis , " Error ADS Max read interval " ) ;
2023-02-28 23:31:46 +00:00
}
2023-06-18 00:12:10 +00:00
//Serial.print("Error ADS Max read interval="); Serial.println(loopmillis-last_adsread);
2021-03-31 19:50:30 +00:00
}
2023-07-10 14:25:19 +00:00
boolean logged_error_sdfile_unavailable = false ;
if ( error_sdfile_unavailable & & ! logged_error_sdfile_unavailable ) {
logged_error_sdfile_unavailable = true ;
writeLogComment ( loopmillis , " Error SDFile Unavailable " ) ;
}
2023-09-15 22:15:32 +00:00
if ( ! controllers_connected | | error_brake_outofrange | | error_throttle_outofrange | | error_throttle_difftoohigh | | error_ads_max_read_interval ) { //any errors?
2023-06-18 00:12:10 +00:00
armed = false ; //disarm
2021-03-31 19:16:48 +00:00
throttle_pos = 0 ;
brake_pos = 0 ;
}
}
2023-06-02 19:10:45 +00:00
void calculateSetSpeed ( unsigned long timediff ) {
2023-04-15 22:13:02 +00:00
2024-03-02 20:55:40 +00:00
int16_t adjusted_throttle_pos = constrain ( throttle_pos * ( throttle_max / 1000.0 ) , 0 , throttle_max ) ;
2023-04-15 22:13:02 +00:00
int16_t brake_pos_expo = ( int16_t ) ( pow ( ( brake_pos / 1000.0 ) , 2 ) * 1000 ) ;
float brakepedal_current_multiplier = startbrakecurrent / 1000.0 ; //how much breaking (in Ampere) for unit of brake_pos (0<=brake_pos<=1000)
2023-06-02 19:10:45 +00:00
int16_t cmdreduce_constant = map ( brake_pos_expo , 0 , 1000 , 0 , ( int16_t ) ( brake_cmdreduce_proportional * timediff / 1000 ) ) ; //reduce cmd value every cycle
2023-04-15 22:13:02 +00:00
float freewheel_current = startbrakecurrent_offset - brake_pos_expo * brakepedal_current_multiplier ; //above which driving current cmd send will be reduced more. increase value to decrease breaking. values <0 increases breaking above freewheeling
2024-03-02 20:55:40 +00:00
2023-06-02 19:10:45 +00:00
2023-04-15 22:13:02 +00:00
float filtered_currentFront = max ( escFront . getFiltered_curL ( ) , escFront . getFiltered_curR ( ) ) ;
float filtered_currentRear = max ( escRear . getFiltered_curL ( ) , escRear . getFiltered_curR ( ) ) ;
2023-06-03 10:21:40 +00:00
filtered_currentAll = filtered_currentFront + filtered_currentRear ; //positive value is current Drawn from battery. negative value is braking current
2023-04-15 22:13:02 +00:00
2024-03-02 20:55:40 +00:00
if ( adjusted_throttle_pos < last_cmd_send ) { //freewheeling or braking
2023-04-15 22:13:02 +00:00
if ( filtered_currentAll > freewheel_current ) { //drive current too high
2023-06-02 19:10:45 +00:00
cmd_send - = max ( 0 , ( filtered_currentAll - freewheel_current ) * freewheel_break_factor * ( timediff / 1000.0 ) ) ; //how much current over freewheel current, multiplied by factor. reduces cmd_send value
2023-04-15 22:13:02 +00:00
}
cmd_send - = max ( minimum_constant_cmd_reduce , cmdreduce_constant ) ; //reduce slowly anyways
}
2023-09-15 22:15:32 +00:00
//acceleration
2024-07-14 09:12:08 +00:00
cmd_send + = constrain ( adjusted_throttle_pos - cmd_send , 0 , ( int16_t ) ( max_acceleration_rate * ( timediff / 1000.0 ) ) ) ; //if throttle higher than last applied value, apply throttle directly with rate limit
2023-09-15 22:15:32 +00:00
2024-03-02 20:55:40 +00:00
cmd_send = constrain ( cmd_send , 0 , throttle_max ) ;
2023-04-15 22:13:02 +00:00
last_cmd_send = cmd_send ;
2024-03-02 20:55:40 +00:00
int16_t cmd_send_toMotor = constrain ( cmd_send * ( 1.0 - ( brake_pos * 0.5 / 1000.0 ) ) , 0 , throttle_max ) ; //brake "ducking"
2023-04-15 22:13:02 +00:00
if ( reverse_enabled ) {
2024-03-02 20:55:40 +00:00
cmd_send_toMotor - = brake_pos * reverse_speed ;
2023-04-15 22:13:02 +00:00
}
if ( ! controllers_connected | | ! armed ) { //controllers not connected or not armed
cmd_send = 0 ;
cmd_send_toMotor = 0 ; //safety off
}
escFront . setSpeed ( cmd_send_toMotor , cmd_send_toMotor ) ;
escRear . setSpeed ( cmd_send_toMotor , cmd_send_toMotor ) ;
log_update = true ;
}
2021-03-31 19:50:30 +00:00
2023-03-02 20:18:48 +00:00
2021-03-31 19:58:23 +00:00
void leds ( ) {
2021-05-15 16:56:42 +00:00
//Start LED
if ( ! armed ) { //disarmed
digitalWrite ( PIN_LED_START , ( ( loopmillis / 1000 ) % 2 = = 0 ) ) ; //high is on for LED_START. blink every second. loopmillis 0 - 1000 led is on.
} else { //armed
digitalWrite ( PIN_LED_START , HIGH ) ; //LED On
}
2021-03-31 19:58:23 +00:00
2021-05-15 16:56:42 +00:00
}
void readButtons ( ) {
2023-05-23 19:11:14 +00:00
bool button_start_longpress_flag = false ;
bool button_start_shortpress_flag = false ;
static bool button_start_wait_release_flag = false ;
bool last_button_start_state = button_start_state ;
2024-07-12 19:44:40 +00:00
2021-05-15 16:56:42 +00:00
if ( loopmillis > button_start_lastchange + DEBOUNCE_TIME ) { //wait some time after last change
2024-06-26 18:41:11 +00:00
if ( digitalRead ( PIN_PWRBUTTON ) & & ! button_start_state ) { //start engine button pressed and was not pressed before
2021-05-15 16:56:42 +00:00
button_start_state = true ; //pressed
button_start_lastchange = loopmillis ; //save time for debouncing
2024-06-26 18:41:11 +00:00
} else if ( ! digitalRead ( PIN_PWRBUTTON ) & & button_start_state ) { //released an was pressed before
2023-05-23 20:17:25 +00:00
button_start_state = false ; // not pressed
2021-05-15 16:56:42 +00:00
button_start_lastchange = loopmillis ; //save time for debouncing
}
}
2023-05-23 19:11:14 +00:00
if ( ! button_start_wait_release_flag ) { //action not prohibited currently
if ( button_start_state ) { //button is pressed
if ( ( loopmillis > button_start_lastchange + LONG_PRESS_ARMING_TIME ) ) { //pressed long
button_start_longpress_flag = true ;
button_start_wait_release_flag = true ; //do not trigger again until button released
}
} else if ( ! button_start_state & & last_button_start_state ) { //just released
button_start_shortpress_flag = true ;
}
}
2023-05-23 20:17:25 +00:00
if ( ! button_start_state ) { //release wait flag at end if button released
button_start_wait_release_flag = false ;
}
2023-05-23 19:11:14 +00:00
if ( button_start_shortpress_flag ) {
armed = false ; //disarm
writeLogComment ( loopmillis , " Disarmed by button " ) ;
}
if ( button_start_longpress_flag ) {
2023-06-03 10:21:40 +00:00
if ( escFront . getControllerConnected ( ) & & escRear . getControllerConnected ( ) ) {
armed = true ; //arm if button pressed long enough
writeLogComment ( loopmillis , " Armed by button " ) ;
2024-03-02 20:55:40 +00:00
if ( control_buttonA ) { //button A is held down during start button press
throttle_max = 1000 ;
reverse_speed = 0.25 ;
2024-07-14 09:12:08 +00:00
max_acceleration_rate = NORMAL_MAX_ACCELERATION_RATE ;
2024-03-02 20:55:40 +00:00
} else if ( control_buttonB ) { //button B is held down during start button press
throttle_max = 750 ;
reverse_speed = 0.25 ;
2024-07-14 09:12:08 +00:00
max_acceleration_rate = SLOW_MAX_ACCELERATION_RATE ;
2024-03-02 20:55:40 +00:00
} else { //no control button pressed during start
throttle_max = 250 ;
reverse_speed = 0.15 ;
2024-07-14 09:12:08 +00:00
max_acceleration_rate = SLOW_MAX_ACCELERATION_RATE ;
2024-03-02 20:55:40 +00:00
}
2023-06-03 10:21:40 +00:00
} else {
writeLogComment ( loopmillis , " Unable to arm " ) ;
}
2023-05-23 19:11:14 +00:00
}
2021-08-06 14:20:48 +00:00
}
2024-03-02 20:55:40 +00:00
void readADSButtons ( ) {
bool last_control_buttonA = control_buttonA ;
bool last_control_buttonB = control_buttonB ;
if ( ( ads_control_raw > ( calib_control_buttonA - calib_control_treshold ) ) & & ( ads_control_raw < ( calib_control_buttonA + calib_control_treshold ) ) ) {
control_buttonA = true ;
control_buttonB = false ;
} else if ( ( ads_control_raw > ( calib_control_buttonB - calib_control_treshold ) ) & & ( ads_control_raw < ( calib_control_buttonB + calib_control_treshold ) ) ) {
control_buttonA = false ;
control_buttonB = true ;
} else if ( ( ads_control_raw > ( calib_control_buttonAB - calib_control_treshold ) ) & & ( ads_control_raw < ( calib_control_buttonAB + calib_control_treshold ) ) ) {
control_buttonA = true ;
control_buttonB = true ;
} else if ( ads_control_raw > calib_control_max ) {
control_buttonA = false ;
control_buttonB = false ;
}
if ( control_buttonA & & ! last_control_buttonA ) { //button A was just pressed
writeLogComment ( loopmillis , " Button A Pressed " ) ;
2024-07-13 18:58:27 +00:00
if ( ! armed ) { //standing disarmed display is showing
if ( standingDisplayScreen < NUM_STANDINGDISPLAYSCREEN - 1 ) {
standingDisplayScreen + + ;
}
}
2024-03-02 20:55:40 +00:00
}
if ( control_buttonB & & ! last_control_buttonB ) { //button B was just pressed
writeLogComment ( loopmillis , " Button B Pressed " ) ;
2024-07-13 18:58:27 +00:00
if ( ! armed ) { //standing disarmed display is showing
if ( standingDisplayScreen > 0 ) {
standingDisplayScreen - - ;
}
}
2024-03-02 20:55:40 +00:00
}
}
2023-08-27 16:16:51 +00:00
uint16_t linearizeThrottle ( uint16_t v , const uint16_t * pthrottleCurvePerMM , int arraysize , bool sorteddescending ) {
2021-08-06 14:20:48 +00:00
//input is raw adc value from hall sensor
2023-08-27 16:16:51 +00:00
//uses pthrottleCurvePerMM array to find linear approximation of actual throttle travel
//array has to be sorted ! if sorteddescending=false then sorted ascending, if true then array should be sorted descending
2021-08-06 14:20:48 +00:00
uint8_t _searchpos = 0 ;
2023-08-27 16:16:51 +00:00
//uint8_t arraysize = sizeof(pthrottleCurvePerMM)/sizeof(pthrottleCurvePerMM[0]);
while ( _searchpos < arraysize & & v > pthrottleCurvePerMM [ ( sorteddescending ? ( arraysize - 1 - _searchpos ) : _searchpos ) ] ) { //find arraypos with value above input value
2021-08-06 14:20:48 +00:00
_searchpos + + ; //try next value
}
if ( _searchpos < = 0 ) { //lower limit
2023-08-27 16:16:51 +00:00
return ( sorteddescending ? 1000 : 0 ) ;
2021-08-06 14:20:48 +00:00
}
if ( _searchpos > = arraysize ) { //upper limit
2023-08-27 16:16:51 +00:00
return ( sorteddescending ? 0 : 1000 ) ;
2021-08-06 14:20:48 +00:00
}
2023-08-27 16:16:51 +00:00
uint16_t nextLower = pthrottleCurvePerMM [ ( sorteddescending ? ( arraysize - 1 - _searchpos ) : _searchpos ) - ( sorteddescending ? 0 : 1 ) ] ;
uint16_t nextHigher = pthrottleCurvePerMM [ ( sorteddescending ? ( arraysize - 1 - _searchpos ) : _searchpos ) - ( sorteddescending ? 1 : 0 ) ] ;
2021-08-06 14:20:48 +00:00
float _linearThrottle = _searchpos + map ( v * 1.0 , nextLower , nextHigher , 0.0 , 1.0 ) ;
_linearThrottle / = arraysize ; //scale to 0-1
_linearThrottle * = 1000 ; //scale to 0-1000
2023-08-27 16:16:51 +00:00
if ( sorteddescending ) {
_linearThrottle = 1000 - _linearThrottle ; //invert result
}
2021-08-06 14:20:48 +00:00
return ( uint16_t ) _linearThrottle ;
2021-02-26 18:37:12 +00:00
}