|  |  |  | @ -2,18 +2,15 @@ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #include <algorithm> | 
			
		
	
		
			
				
					|  |  |  |  | #include <array> | 
			
		
	
		
			
				
					|  |  |  |  | #include <atomic> | 
			
		
	
		
			
				
					|  |  |  |  | #include <bitset> | 
			
		
	
		
			
				
					|  |  |  |  | #include <cassert> | 
			
		
	
		
			
				
					|  |  |  |  | #include <cerrno> | 
			
		
	
		
			
				
					|  |  |  |  | #include <chrono> | 
			
		
	
		
			
				
					|  |  |  |  | #include <future> | 
			
		
	
		
			
				
					|  |  |  |  | #include <memory> | 
			
		
	
		
			
				
					|  |  |  |  | #include <thread> | 
			
		
	
		
			
				
					|  |  |  |  | #include <utility> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #include "cereal/gen/cpp/car.capnp.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "cereal/messaging/messaging.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/params.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/ratekeeper.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/swaglog.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/timing.h" | 
			
		
	
	
		
			
				
					|  |  |  | @ -43,9 +40,6 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #define MIN_IR_POWER 0.0f | 
			
		
	
		
			
				
					|  |  |  |  | #define CUTOFF_IL 400 | 
			
		
	
		
			
				
					|  |  |  |  | #define SATURATE_IL 1000 | 
			
		
	
		
			
				
					|  |  |  |  | using namespace std::chrono_literals; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | std::atomic<bool> ignition(false); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ExitHandler do_exit; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -59,88 +53,6 @@ bool check_all_connected(const std::vector<Panda *> &pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |   return true; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | bool safety_setter_thread(std::vector<Panda *> pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |   LOGD("Starting safety setter thread"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   Params p; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // there should be at least one panda connected
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (pandas.size() == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     return false; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // initialize to ELM327 without OBD multiplexing for fingerprinting
 | 
			
		
	
		
			
				
					|  |  |  |  |   bool obd_multiplexing_enabled = false; | 
			
		
	
		
			
				
					|  |  |  |  |   for (int i = 0; i < pandas.size(); i++) { | 
			
		
	
		
			
				
					|  |  |  |  |     pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // openpilot can switch between multiplexing modes for different FW queries
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (true) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (do_exit || !check_all_connected(pandas) || !ignition) { | 
			
		
	
		
			
				
					|  |  |  |  |       return false; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     bool obd_multiplexing_requested = p.getBool("ObdMultiplexingEnabled"); | 
			
		
	
		
			
				
					|  |  |  |  |     if (obd_multiplexing_requested != obd_multiplexing_enabled) { | 
			
		
	
		
			
				
					|  |  |  |  |       for (int i = 0; i < pandas.size(); i++) { | 
			
		
	
		
			
				
					|  |  |  |  |         const uint16_t safety_param = (i > 0 || !obd_multiplexing_requested) ? 1U : 0U; | 
			
		
	
		
			
				
					|  |  |  |  |         pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |       obd_multiplexing_enabled = obd_multiplexing_requested; | 
			
		
	
		
			
				
					|  |  |  |  |       p.putBool("ObdMultiplexingChanged", true); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (p.getBool("FirmwareQueryDone")) { | 
			
		
	
		
			
				
					|  |  |  |  |       LOGW("finished FW query"); | 
			
		
	
		
			
				
					|  |  |  |  |       break; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     util::sleep_for(20); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   std::string params; | 
			
		
	
		
			
				
					|  |  |  |  |   LOGW("waiting for params to set safety model"); | 
			
		
	
		
			
				
					|  |  |  |  |   while (true) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (do_exit || !check_all_connected(pandas) || !ignition) { | 
			
		
	
		
			
				
					|  |  |  |  |       return false; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (p.getBool("ControlsReady")) { | 
			
		
	
		
			
				
					|  |  |  |  |       params = p.get("CarParams"); | 
			
		
	
		
			
				
					|  |  |  |  |       if (params.size() > 0) break; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     util::sleep_for(100); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   LOGW("got %lu bytes CarParams", params.size()); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   AlignedBuffer aligned_buf; | 
			
		
	
		
			
				
					|  |  |  |  |   capnp::FlatArrayMessageReader cmsg(aligned_buf.align(params.data(), params.size())); | 
			
		
	
		
			
				
					|  |  |  |  |   cereal::CarParams::Reader car_params = cmsg.getRoot<cereal::CarParams>(); | 
			
		
	
		
			
				
					|  |  |  |  |   cereal::CarParams::SafetyModel safety_model; | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t safety_param; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto safety_configs = car_params.getSafetyConfigs(); | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t alternative_experience = car_params.getAlternativeExperience(); | 
			
		
	
		
			
				
					|  |  |  |  |   for (uint32_t i = 0; i < pandas.size(); i++) { | 
			
		
	
		
			
				
					|  |  |  |  |     auto panda = pandas[i]; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (safety_configs.size() > i) { | 
			
		
	
		
			
				
					|  |  |  |  |       safety_model = safety_configs[i].getSafetyModel(); | 
			
		
	
		
			
				
					|  |  |  |  |       safety_param = safety_configs[i].getSafetyParam(); | 
			
		
	
		
			
				
					|  |  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |  |       // If no safety mode is specified, default to silent
 | 
			
		
	
		
			
				
					|  |  |  |  |       safety_model = cereal::CarParams::SafetyModel::SILENT; | 
			
		
	
		
			
				
					|  |  |  |  |       safety_param = 0U; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     LOGW("panda %d: setting safety model: %d, param: %d, alternative experience: %d", i, (int)safety_model, safety_param, alternative_experience); | 
			
		
	
		
			
				
					|  |  |  |  |     panda->set_alternative_experience(alternative_experience); | 
			
		
	
		
			
				
					|  |  |  |  |     panda->set_safety_model(safety_model, safety_param); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   return true; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Panda *connect(std::string serial="", uint32_t index=0) { | 
			
		
	
		
			
				
					|  |  |  |  |   std::unique_ptr<Panda> panda; | 
			
		
	
		
			
				
					|  |  |  |  |   try { | 
			
		
	
	
		
			
				
					|  |  |  | @ -197,16 +109,9 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) { | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void can_recv_thread(std::vector<Panda *> pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |   util::set_thread_name("pandad_can_recv"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   PubMaster pm({"can"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // run at 100Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |   RateKeeper rk("pandad_can_recv", 100); | 
			
		
	
		
			
				
					|  |  |  |  |   std::vector<can_frame> raw_can_data; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (!do_exit && check_all_connected(pandas)) { | 
			
		
	
		
			
				
					|  |  |  |  | void can_recv(std::vector<Panda *> &pandas, PubMaster *pm) { | 
			
		
	
		
			
				
					|  |  |  |  |   static std::vector<can_frame> raw_can_data; | 
			
		
	
		
			
				
					|  |  |  |  |   { | 
			
		
	
		
			
				
					|  |  |  |  |     bool comms_healthy = true; | 
			
		
	
		
			
				
					|  |  |  |  |     raw_can_data.clear(); | 
			
		
	
		
			
				
					|  |  |  |  |     for (const auto& panda : pandas) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -217,14 +122,12 @@ void can_recv_thread(std::vector<Panda *> pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |     auto evt = msg.initEvent(); | 
			
		
	
		
			
				
					|  |  |  |  |     evt.setValid(comms_healthy); | 
			
		
	
		
			
				
					|  |  |  |  |     auto canData = evt.initCan(raw_can_data.size()); | 
			
		
	
		
			
				
					|  |  |  |  |     for (uint i = 0; i<raw_can_data.size(); i++) { | 
			
		
	
		
			
				
					|  |  |  |  |     for (size_t i = 0; i < raw_can_data.size(); ++i) { | 
			
		
	
		
			
				
					|  |  |  |  |       canData[i].setAddress(raw_can_data[i].address); | 
			
		
	
		
			
				
					|  |  |  |  |       canData[i].setDat(kj::arrayPtr((uint8_t*)raw_can_data[i].dat.data(), raw_can_data[i].dat.size())); | 
			
		
	
		
			
				
					|  |  |  |  |       canData[i].setSrc(raw_can_data[i].src); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     pm.send("can", msg); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     rk.keepTime(); | 
			
		
	
		
			
				
					|  |  |  |  |     pm->send("can", msg); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -386,7 +289,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> | 
			
		
	
		
			
				
					|  |  |  |  |   return ignition_local; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void send_peripheral_state(PubMaster *pm, Panda *panda) { | 
			
		
	
		
			
				
					|  |  |  |  | void send_peripheral_state(Panda *panda, PubMaster *pm) { | 
			
		
	
		
			
				
					|  |  |  |  |   // build msg
 | 
			
		
	
		
			
				
					|  |  |  |  |   MessageBuilder msg; | 
			
		
	
		
			
				
					|  |  |  |  |   auto evt = msg.initEvent(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -409,46 +312,23 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) { | 
			
		
	
		
			
				
					|  |  |  |  |   pm->send("peripheralState", msg); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) { | 
			
		
	
		
			
				
					|  |  |  |  |   util::set_thread_name("pandad_panda_state"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   Params params; | 
			
		
	
		
			
				
					|  |  |  |  |   SubMaster sm({"controlsState"}); | 
			
		
	
		
			
				
					|  |  |  |  |   PubMaster pm({"pandaStates", "peripheralState"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   Panda *peripheral_panda = pandas[0]; | 
			
		
	
		
			
				
					|  |  |  |  |   bool is_onroad = false; | 
			
		
	
		
			
				
					|  |  |  |  |   bool is_onroad_last = false; | 
			
		
	
		
			
				
					|  |  |  |  |   std::future<bool> safety_future; | 
			
		
	
		
			
				
					|  |  |  |  | void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoofing_started) { | 
			
		
	
		
			
				
					|  |  |  |  |   static SubMaster sm({"controlsState"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   std::vector<std::string> connected_serials; | 
			
		
	
		
			
				
					|  |  |  |  |   for (Panda *p : pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |     connected_serials.push_back(p->hw_serial()); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   LOGD("start panda state thread"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // run at 10hz
 | 
			
		
	
		
			
				
					|  |  |  |  |   RateKeeper rk("panda_state_thread", 10); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (!do_exit && check_all_connected(pandas)) { | 
			
		
	
		
			
				
					|  |  |  |  |     // send out peripheralState at 2Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (sm.frame % 5 == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       send_peripheral_state(&pm, peripheral_panda); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   { | 
			
		
	
		
			
				
					|  |  |  |  |     auto ignition_opt = send_panda_states(pm, pandas, spoofing_started); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!ignition_opt) { | 
			
		
	
		
			
				
					|  |  |  |  |       LOGE("Failed to get ignition_opt"); | 
			
		
	
		
			
				
					|  |  |  |  |       rk.keepTime(); | 
			
		
	
		
			
				
					|  |  |  |  |       continue; | 
			
		
	
		
			
				
					|  |  |  |  |       return; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     ignition = *ignition_opt; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // check if we should have pandad reconnect
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (!ignition) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (!ignition_opt.value()) { | 
			
		
	
		
			
				
					|  |  |  |  |       bool comms_healthy = true; | 
			
		
	
		
			
				
					|  |  |  |  |       for (const auto &panda : pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |         comms_healthy &= panda->comms_healthy(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -468,52 +348,28 @@ void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) { | 
			
		
	
		
			
				
					|  |  |  |  |           } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       if (do_exit) { | 
			
		
	
		
			
				
					|  |  |  |  |         break; | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     is_onroad = params.getBool("IsOnroad"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // set new safety on onroad transition, after params are cleared
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (is_onroad && !is_onroad_last) { | 
			
		
	
		
			
				
					|  |  |  |  |       if (!safety_future.valid() || safety_future.wait_for(0ms) == std::future_status::ready) { | 
			
		
	
		
			
				
					|  |  |  |  |         safety_future = std::async(std::launch::async, safety_setter_thread, pandas); | 
			
		
	
		
			
				
					|  |  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |  |         LOGW("Safety setter thread already running"); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     is_onroad_last = is_onroad; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     sm.update(0); | 
			
		
	
		
			
				
					|  |  |  |  |     const bool engaged = sm.allAliveAndValid({"controlsState"}) && sm["controlsState"].getControlsState().getEnabled(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     for (const auto &panda : pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |       panda->send_heartbeat(engaged); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     rk.keepTime(); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control) { | 
			
		
	
		
			
				
					|  |  |  |  |   static SubMaster sm({"deviceState", "driverCameraState"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void peripheral_control_thread(Panda *panda, bool no_fan_control) { | 
			
		
	
		
			
				
					|  |  |  |  |   util::set_thread_name("pandad_peripheral_control"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   SubMaster sm({"deviceState", "driverCameraState"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   uint64_t last_driver_camera_t = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t prev_fan_speed = 999; | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t ir_pwr = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t prev_ir_pwr = 999; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   FirstOrderFilter integ_lines_filter(0, 30.0, 0.05); | 
			
		
	
		
			
				
					|  |  |  |  |   static uint64_t last_driver_camera_t = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   static uint16_t prev_fan_speed = 999; | 
			
		
	
		
			
				
					|  |  |  |  |   static uint16_t ir_pwr = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   static uint16_t prev_ir_pwr = 999; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (!do_exit && panda->connected()) { | 
			
		
	
		
			
				
					|  |  |  |  |     sm.update(1000); | 
			
		
	
		
			
				
					|  |  |  |  |   static FirstOrderFilter integ_lines_filter(0, 30.0, 0.05); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   { | 
			
		
	
		
			
				
					|  |  |  |  |     sm.update(0); | 
			
		
	
		
			
				
					|  |  |  |  |     if (sm.updated("deviceState") && !no_fan_control) { | 
			
		
	
		
			
				
					|  |  |  |  |       // Fan speed
 | 
			
		
	
		
			
				
					|  |  |  |  |       uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -551,9 +407,46 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) { | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void pandad_main_thread(std::vector<std::string> serials) { | 
			
		
	
		
			
				
					|  |  |  |  |   LOGW("launching pandad"); | 
			
		
	
		
			
				
					|  |  |  |  | void pandad_run(std::vector<Panda *> &pandas) { | 
			
		
	
		
			
				
					|  |  |  |  |   const bool no_fan_control = getenv("NO_FAN_CONTROL") != nullptr; | 
			
		
	
		
			
				
					|  |  |  |  |   const bool spoofing_started = getenv("STARTED") != nullptr; | 
			
		
	
		
			
				
					|  |  |  |  |   const bool fake_send = getenv("FAKESEND") != nullptr; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // Start the CAN send thread
 | 
			
		
	
		
			
				
					|  |  |  |  |   std::thread send_thread(can_send_thread, pandas, fake_send); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   RateKeeper rk("pandad", 100); | 
			
		
	
		
			
				
					|  |  |  |  |   PubMaster pm({"can", "pandaStates", "peripheralState"}); | 
			
		
	
		
			
				
					|  |  |  |  |   PandaSafety panda_safety(pandas); | 
			
		
	
		
			
				
					|  |  |  |  |   Panda *peripheral_panda = pandas[0]; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // Main loop: receive CAN data and process states
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (!do_exit && check_all_connected(pandas)) { | 
			
		
	
		
			
				
					|  |  |  |  |     can_recv(pandas, &pm); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Process peripheral state at 20 Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (rk.frame() % 5 == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       process_peripheral_state(peripheral_panda, &pm, no_fan_control); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Process panda state at 10 Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (rk.frame() % 10 == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       process_panda_state(pandas, &pm, spoofing_started); | 
			
		
	
		
			
				
					|  |  |  |  |       panda_safety.configureSafetyMode(); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Send out peripheralState at 2Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (rk.frame() % 50 == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       send_peripheral_state(peripheral_panda, &pm); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     rk.keepTime(); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   send_thread.join(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void pandad_main_thread(std::vector<std::string> serials) { | 
			
		
	
		
			
				
					|  |  |  |  |   if (serials.size() == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     serials = Panda::list(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -585,16 +478,7 @@ void pandad_main_thread(std::vector<std::string> serials) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (!do_exit) { | 
			
		
	
		
			
				
					|  |  |  |  |     LOGW("connected to all pandas"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     std::vector<std::thread> threads; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     threads.emplace_back(panda_state_thread, pandas, getenv("STARTED") != nullptr); | 
			
		
	
		
			
				
					|  |  |  |  |     threads.emplace_back(peripheral_control_thread, pandas[0], getenv("NO_FAN_CONTROL") != nullptr); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     threads.emplace_back(can_send_thread, pandas, getenv("FAKESEND") != nullptr); | 
			
		
	
		
			
				
					|  |  |  |  |     threads.emplace_back(can_recv_thread, pandas); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     for (auto &t : threads) t.join(); | 
			
		
	
		
			
				
					|  |  |  |  |     pandad_run(pandas); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   for (Panda *panda : pandas) { | 
			
		
	
	
		
			
				
					|  |  |  | 
 |