@ -40,6 +40,7 @@ std::atomic<bool> ignition(false);
ExitHandler do_exit ;
ExitHandler do_exit ;
std : : string get_time_str ( const struct tm & time ) {
std : : string get_time_str ( const struct tm & time ) {
char s [ 30 ] = { ' \0 ' } ;
char s [ 30 ] = { ' \0 ' } ;
std : : strftime ( s , std : : size ( s ) , " %Y-%m-%d %H:%M:%S " , & time ) ;
std : : strftime ( s , std : : size ( s ) , " %Y-%m-%d %H:%M:%S " , & time ) ;
@ -143,7 +144,7 @@ Panda *usb_connect() {
// power on charging, only the first time. Panda can also change mode and it causes a brief disconneciton
// power on charging, only the first time. Panda can also change mode and it causes a brief disconneciton
# ifndef __x86_64__
# ifndef __x86_64__
static std : : once_flag connected_once ;
static std : : once_flag connected_once ;
std : : call_once ( connected_once , & Panda : : set_usb_power_mode , panda , cereal : : Panda State : : UsbPowerMode : : CDP ) ;
std : : call_once ( connected_once , & Panda : : set_usb_power_mode , panda , cereal : : Peripheral State : : UsbPowerMode : : CDP ) ;
# endif
# endif
if ( panda - > has_rtc ) {
if ( panda - > has_rtc ) {
@ -234,6 +235,13 @@ void can_recv_thread(Panda *panda) {
}
}
}
}
void send_empty_peripheral_state ( PubMaster * pm ) {
MessageBuilder msg ;
auto peripheralState = msg . initEvent ( ) . initPeripheralState ( ) ;
peripheralState . setPandaType ( cereal : : PandaState : : PandaType : : UNKNOWN ) ;
pm - > send ( " peripheralState " , msg ) ;
}
void send_empty_panda_state ( PubMaster * pm ) {
void send_empty_panda_state ( PubMaster * pm ) {
MessageBuilder msg ;
MessageBuilder msg ;
auto pandaState = msg . initEvent ( ) . initPandaState ( ) ;
auto pandaState = msg . initEvent ( ) . initPandaState ( ) ;
@ -241,44 +249,113 @@ void send_empty_panda_state(PubMaster *pm) {
pm - > send ( " pandaState " , msg ) ;
pm - > send ( " pandaState " , msg ) ;
}
}
void panda_state_thread ( PubMaster * pm , Panda * panda , bool spoofing_started ) {
bool send_panda_state ( PubMaster * pm , Panda * panda , bool spoofing_started ) {
LOGD ( " start panda state thread " ) ;
health_t pandaState = panda - > get_state ( ) ;
uint32_t no_ignition_cnt = 0 ;
bool ignition_last = false ;
Params params = Params ( ) ;
// run at 2hz
if ( spoofing_started ) {
while ( ! do_exit & & panda - > connected ) {
pandaState . ignition_line = 1 ;
health_t pandaState = panda - > get_state ( ) ;
}
if ( spoofing_started ) {
// Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node
pandaState . ignition_line = 1 ;
if ( pandaState . safety_model = = ( uint8_t ) ( cereal : : CarParams : : SafetyModel : : SILENT ) ) {
}
panda - > set_safety_model ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ;
}
// Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node
bool ignition = ( ( pandaState . ignition_line ! = 0 ) | | ( pandaState . ignition_can ! = 0 ) ) ;
if ( pandaState . safety_model = = ( uint8_t ) ( cereal : : CarParams : : SafetyModel : : SILENT ) ) {
panda - > set_safety_model ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ;
}
ignition = ( ( pandaState . ignition_line ! = 0 ) | | ( pandaState . ignition_can ! = 0 ) ) ;
# ifndef __x86_64__
bool power_save_desired = ! ignition ;
if ( pandaState . power_save_enabled ! = power_save_desired ) {
panda - > set_power_saving ( power_save_desired ) ;
}
if ( ignition ) {
// set safety mode to NO_OUTPUT when car is off. ELM327 is an alternative if we want to leverage athenad/connect
no_ignition_cnt = 0 ;
if ( ! ignition & & ( pandaState . safety_model ! = ( uint8_t ) ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ) ) {
} else {
panda - > set_safety_model ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ;
no_ignition_cnt + = 1 ;
}
}
# endif
# ifndef __x86_64__
// build msg
bool power_save_desired = ! ignition ;
MessageBuilder msg ;
if ( pandaState . power_save_enabled ! = power_save_desired ) {
auto evt = msg . initEvent ( ) ;
panda - > set_power_saving ( power_save_desired ) ;
evt . setValid ( panda - > comms_healthy ) ;
auto ps = evt . initPandaState ( ) ;
ps . setUptime ( pandaState . uptime ) ;
ps . setIgnitionLine ( pandaState . ignition_line ) ;
ps . setIgnitionCan ( pandaState . ignition_can ) ;
ps . setControlsAllowed ( pandaState . controls_allowed ) ;
ps . setGasInterceptorDetected ( pandaState . gas_interceptor_detected ) ;
ps . setCanRxErrs ( pandaState . can_rx_errs ) ;
ps . setCanSendErrs ( pandaState . can_send_errs ) ;
ps . setCanFwdErrs ( pandaState . can_fwd_errs ) ;
ps . setGmlanSendErrs ( pandaState . gmlan_send_errs ) ;
ps . setPandaType ( panda - > hw_type ) ;
ps . setSafetyModel ( cereal : : CarParams : : SafetyModel ( pandaState . safety_model ) ) ;
ps . setSafetyParam ( pandaState . safety_param ) ;
ps . setFaultStatus ( cereal : : PandaState : : FaultStatus ( pandaState . fault_status ) ) ;
ps . setPowerSaveEnabled ( ( bool ) ( pandaState . power_save_enabled ) ) ;
ps . setHeartbeatLost ( ( bool ) ( pandaState . heartbeat_lost ) ) ;
ps . setHarnessStatus ( cereal : : PandaState : : HarnessStatus ( pandaState . car_harness_status ) ) ;
// Convert faults bitset to capnp list
std : : bitset < sizeof ( pandaState . faults ) * 8 > fault_bits ( pandaState . faults ) ;
auto faults = ps . initFaults ( fault_bits . count ( ) ) ;
size_t i = 0 ;
for ( size_t f = size_t ( cereal : : PandaState : : FaultType : : RELAY_MALFUNCTION ) ;
f < = size_t ( cereal : : PandaState : : FaultType : : INTERRUPT_RATE_TICK ) ; f + + ) {
if ( fault_bits . test ( f ) ) {
faults . set ( i , cereal : : PandaState : : FaultType ( f ) ) ;
i + + ;
}
}
}
pm - > send ( " pandaState " , msg ) ;
return ignition ;
}
// set safety mode to NO_OUTPUT when car is off. ELM327 is an alternative if we want to leverage athenad/connect
void send_peripheral_state ( PubMaster * pm , Panda * panda ) {
if ( ! ignition & & ( pandaState . safety_model ! = ( uint8_t ) ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ) ) {
health_t pandaState = panda - > get_state ( ) ;
panda - > set_safety_model ( cereal : : CarParams : : SafetyModel : : NO_OUTPUT ) ;
// build msg
MessageBuilder msg ;
auto evt = msg . initEvent ( ) ;
evt . setValid ( panda - > comms_healthy ) ;
auto ps = evt . initPeripheralState ( ) ;
ps . setPandaType ( panda - > hw_type ) ;
if ( Hardware : : TICI ( ) ) {
double read_time = millis_since_boot ( ) ;
ps . setVoltage ( std : : atoi ( util : : read_file ( " /sys/class/hwmon/hwmon1/in1_input " ) . c_str ( ) ) ) ;
ps . setCurrent ( std : : atoi ( util : : read_file ( " /sys/class/hwmon/hwmon1/curr1_input " ) . c_str ( ) ) ) ;
read_time = millis_since_boot ( ) - read_time ;
if ( read_time > 50 ) {
LOGW ( " reading hwmon took %lfms " , read_time ) ;
}
}
# endif
} else {
ps . setVoltage ( pandaState . voltage ) ;
ps . setCurrent ( pandaState . current ) ;
}
uint16_t fan_speed_rpm = panda - > get_fan_speed ( ) ;
ps . setUsbPowerMode ( cereal : : PeripheralState : : UsbPowerMode ( pandaState . usb_power_mode ) ) ;
ps . setFanSpeedRpm ( fan_speed_rpm ) ;
pm - > send ( " peripheralState " , msg ) ;
}
void panda_state_thread ( PubMaster * pm , Panda * peripheral_panda , Panda * panda , bool spoofing_started ) {
Params params ;
bool ignition_last = false ;
LOGD ( " start panda state thread " ) ;
// run at 2hz
while ( ! do_exit & & panda - > connected ) {
send_peripheral_state ( pm , peripheral_panda ) ;
ignition = send_panda_state ( pm , panda , spoofing_started ) ;
// clear VIN, CarParams, and set new safety on car start
// clear VIN, CarParams, and set new safety on car start
if ( ignition & & ! ignition_last ) {
if ( ignition & & ! ignition_last ) {
@ -294,87 +371,16 @@ void panda_state_thread(PubMaster *pm, Panda *panda, bool spoofing_started) {
params . clearAll ( CLEAR_ON_IGNITION_OFF ) ;
params . clearAll ( CLEAR_ON_IGNITION_OFF ) ;
}
}
// Write to rtc once per minute when no ignition present
if ( ( panda - > has_rtc ) & & ! ignition & & ( no_ignition_cnt % 120 = = 1 ) ) {
// Write time to RTC if it looks reasonable
setenv ( " TZ " , " UTC " , 1 ) ;
struct tm sys_time = util : : get_time ( ) ;
if ( util : : time_valid ( sys_time ) ) {
struct tm rtc_time = panda - > get_rtc ( ) ;
double seconds = difftime ( mktime ( & rtc_time ) , mktime ( & sys_time ) ) ;
if ( std : : abs ( seconds ) > 1.1 ) {
panda - > set_rtc ( sys_time ) ;
LOGW ( " Updating panda RTC. dt = %.2f System: %s RTC: %s " ,
seconds , get_time_str ( sys_time ) . c_str ( ) , get_time_str ( rtc_time ) . c_str ( ) ) ;
}
}
}
ignition_last = ignition ;
ignition_last = ignition ;
uint16_t fan_speed_rpm = panda - > get_fan_speed ( ) ;
// build msg
MessageBuilder msg ;
auto evt = msg . initEvent ( ) ;
evt . setValid ( panda - > comms_healthy ) ;
auto ps = evt . initPandaState ( ) ;
ps . setUptime ( pandaState . uptime ) ;
if ( Hardware : : TICI ( ) ) {
double read_time = millis_since_boot ( ) ;
ps . setVoltage ( std : : atoi ( util : : read_file ( " /sys/class/hwmon/hwmon1/in1_input " ) . c_str ( ) ) ) ;
ps . setCurrent ( std : : atoi ( util : : read_file ( " /sys/class/hwmon/hwmon1/curr1_input " ) . c_str ( ) ) ) ;
read_time = millis_since_boot ( ) - read_time ;
if ( read_time > 50 ) {
LOGW ( " reading hwmon took %lfms " , read_time ) ;
}
} else {
ps . setVoltage ( pandaState . voltage ) ;
ps . setCurrent ( pandaState . current ) ;
}
ps . setIgnitionLine ( pandaState . ignition_line ) ;
ps . setIgnitionCan ( pandaState . ignition_can ) ;
ps . setControlsAllowed ( pandaState . controls_allowed ) ;
ps . setGasInterceptorDetected ( pandaState . gas_interceptor_detected ) ;
ps . setHasGps ( true ) ;
ps . setCanRxErrs ( pandaState . can_rx_errs ) ;
ps . setCanSendErrs ( pandaState . can_send_errs ) ;
ps . setCanFwdErrs ( pandaState . can_fwd_errs ) ;
ps . setGmlanSendErrs ( pandaState . gmlan_send_errs ) ;
ps . setPandaType ( panda - > hw_type ) ;
ps . setUsbPowerMode ( cereal : : PandaState : : UsbPowerMode ( pandaState . usb_power_mode ) ) ;
ps . setSafetyModel ( cereal : : CarParams : : SafetyModel ( pandaState . safety_model ) ) ;
ps . setSafetyParam ( pandaState . safety_param ) ;
ps . setFanSpeedRpm ( fan_speed_rpm ) ;
ps . setFaultStatus ( cereal : : PandaState : : FaultStatus ( pandaState . fault_status ) ) ;
ps . setPowerSaveEnabled ( ( bool ) ( pandaState . power_save_enabled ) ) ;
ps . setHeartbeatLost ( ( bool ) ( pandaState . heartbeat_lost ) ) ;
ps . setHarnessStatus ( cereal : : PandaState : : HarnessStatus ( pandaState . car_harness_status ) ) ;
// Convert faults bitset to capnp list
std : : bitset < sizeof ( pandaState . faults ) * 8 > fault_bits ( pandaState . faults ) ;
auto faults = ps . initFaults ( fault_bits . count ( ) ) ;
size_t i = 0 ;
for ( size_t f = size_t ( cereal : : PandaState : : FaultType : : RELAY_MALFUNCTION ) ;
f < = size_t ( cereal : : PandaState : : FaultType : : INTERRUPT_RATE_TICK ) ; f + + ) {
if ( fault_bits . test ( f ) ) {
faults . set ( i , cereal : : PandaState : : FaultType ( f ) ) ;
i + + ;
}
}
pm - > send ( " pandaState " , msg ) ;
panda - > send_heartbeat ( ) ;
panda - > send_heartbeat ( ) ;
util : : sleep_for ( 500 ) ;
util : : sleep_for ( 500 ) ;
}
}
}
}
void hardware_control_thread ( Panda * panda ) {
LOGD ( " start hardware control thread " ) ;
void peripheral_control_thread ( Panda * panda ) {
LOGD ( " start peripheral control thread " ) ;
SubMaster sm ( { " deviceState " , " driverCameraState " } ) ;
SubMaster sm ( { " deviceState " , " driverCameraState " } ) ;
uint64_t last_front_frame_t = 0 ;
uint64_t last_front_frame_t = 0 ;
@ -395,10 +401,10 @@ void hardware_control_thread(Panda *panda) {
bool charging_disabled = sm [ " deviceState " ] . getDeviceState ( ) . getChargingDisabled ( ) ;
bool charging_disabled = sm [ " deviceState " ] . getDeviceState ( ) . getChargingDisabled ( ) ;
if ( charging_disabled ! = prev_charging_disabled ) {
if ( charging_disabled ! = prev_charging_disabled ) {
if ( charging_disabled ) {
if ( charging_disabled ) {
panda - > set_usb_power_mode ( cereal : : Panda State : : UsbPowerMode : : CLIENT ) ;
panda - > set_usb_power_mode ( cereal : : Peripheral State : : UsbPowerMode : : CLIENT ) ;
LOGW ( " TURN OFF CHARGING! \n " ) ;
LOGW ( " TURN OFF CHARGING! \n " ) ;
} else {
} else {
panda - > set_usb_power_mode ( cereal : : Panda State : : UsbPowerMode : : CDP ) ;
panda - > set_usb_power_mode ( cereal : : Peripheral State : : UsbPowerMode : : CDP ) ;
LOGW ( " TURN ON CHARGING! \n " ) ;
LOGW ( " TURN ON CHARGING! \n " ) ;
}
}
prev_charging_disabled = charging_disabled ;
prev_charging_disabled = charging_disabled ;
@ -444,6 +450,23 @@ void hardware_control_thread(Panda *panda) {
prev_ir_pwr = ir_pwr ;
prev_ir_pwr = ir_pwr ;
}
}
// Write to rtc once per minute when no ignition present
if ( ( panda - > has_rtc ) & & ! ignition & & ( cnt % 120 = = 1 ) ) {
// Write time to RTC if it looks reasonable
setenv ( " TZ " , " UTC " , 1 ) ;
struct tm sys_time = util : : get_time ( ) ;
if ( util : : time_valid ( sys_time ) ) {
struct tm rtc_time = panda - > get_rtc ( ) ;
double seconds = difftime ( mktime ( & rtc_time ) , mktime ( & sys_time ) ) ;
if ( std : : abs ( seconds ) > 1.1 ) {
panda - > set_rtc ( sys_time ) ;
LOGW ( " Updating panda RTC. dt = %.2f System: %s RTC: %s " ,
seconds , get_time_str ( sys_time ) . c_str ( ) , get_time_str ( rtc_time ) . c_str ( ) ) ;
}
}
}
}
}
}
}
@ -538,14 +561,16 @@ int main() {
LOG ( " set affinity returns %d " , err ) ;
LOG ( " set affinity returns %d " , err ) ;
LOGW ( " attempting to connect " ) ;
LOGW ( " attempting to connect " ) ;
PubMaster pm ( { " pandaState " } ) ;
PubMaster pm ( { " pandaState " , " peripheralState " } ) ;
while ( ! do_exit ) {
while ( ! do_exit ) {
Panda * panda = usb_connect ( ) ;
Panda * panda = usb_connect ( ) ;
Panda * peripheral_panda = panda ;
// Send empty pandaState and try again
// Send empty pandaState & peripheralState and try again
if ( panda = = nullptr ) {
if ( panda = = nullptr | | peripheral_panda = = nullptr ) {
send_empty_panda_state ( & pm ) ;
send_empty_panda_state ( & pm ) ;
send_empty_peripheral_state ( & pm ) ;
util : : sleep_for ( 500 ) ;
util : : sleep_for ( 500 ) ;
continue ;
continue ;
}
}
@ -553,11 +578,12 @@ int main() {
LOGW ( " connected to board " ) ;
LOGW ( " connected to board " ) ;
std : : vector < std : : thread > threads ;
std : : vector < std : : thread > threads ;
threads . emplace_back ( panda_state_thread , & pm , panda , getenv ( " STARTED " ) ! = nullptr ) ;
threads . emplace_back ( panda_state_thread , & pm , peripheral_panda , panda , getenv ( " STARTED " ) ! = nullptr ) ;
threads . emplace_back ( peripheral_control_thread , peripheral_panda ) ;
threads . emplace_back ( pigeon_thread , peripheral_panda ) ;
threads . emplace_back ( can_send_thread , panda , getenv ( " FAKESEND " ) ! = nullptr ) ;
threads . emplace_back ( can_send_thread , panda , getenv ( " FAKESEND " ) ! = nullptr ) ;
threads . emplace_back ( can_recv_thread , panda ) ;
threads . emplace_back ( can_recv_thread , panda ) ;
threads . emplace_back ( hardware_control_thread , panda ) ;
threads . emplace_back ( pigeon_thread , panda ) ;
for ( auto & t : threads ) t . join ( ) ;
for ( auto & t : threads ) t . join ( ) ;