2 Copyright (C) 2012 Intel Corporation
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "obd2source.h"
22 #include <boost/assert.hpp>
23 #include <boost/lexical_cast.hpp>
26 #include <json-glib/json-glib.h>
27 #include <listplusplus.h>
29 #include "bluetooth.hpp"
32 #define __SMALLFILE__ std::string(__FILE__).substr(std::string(__FILE__).rfind("/")+1)
33 AbstractRoutingEngine *m_re;
35 int calledPersecond = 0;
37 bool sendElmCommand(obdLib *obd,std::string command)
39 std::vector<unsigned char> replyVector;
41 obd->sendObdRequestString(command.append("\r").c_str(),command.length()+1,&replyVector,10,3);
42 for (unsigned int i=0;i<replyVector.size();i++)
44 reply += replyVector[i];
46 if (reply.find("OK") == -1)
57 void threadLoop(gpointer data)
59 GAsyncQueue *privCommandQueue = g_async_queue_ref(((OBD2Source*)data)->commandQueue);
60 GAsyncQueue *privResponseQueue = g_async_queue_ref(((OBD2Source*)data)->responseQueue);
61 GAsyncQueue *privSingleShotQueue = g_async_queue_ref(((OBD2Source*)data)->singleShotQueue);
62 GAsyncQueue *privSubscriptionAddQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionAddQueue);
63 GAsyncQueue *privSubscriptionRemoveQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionRemoveQueue);
64 obdLib *obd = new obdLib();
66 obd->setCommsCallback([](const char* mssg, void* data) { DebugOut(6)<<mssg<<endl; },NULL);
67 obd->setDebugCallback([](const char* mssg, void* data, obdLib::DebugLevel debugLevel) { DebugOut(debugLevel)<<mssg<<endl; },NULL);
69 std::list<std::string> reqList;
70 std::list<std::string> repeatReqList;
71 std::map<std::string,std::string> commandMap;
72 std::vector<unsigned char> replyVector;
76 //gpointer query = g_async_queue_pop(privCommandQueue);
79 gpointer query = g_async_queue_try_pop(privSingleShotQueue);
82 //printf("Got request!\n");
83 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got single shot request!"<<endl;
84 ObdRequest *req = (ObdRequest*)query;
85 repeatReqList.push_back(req->req);
88 query = g_async_queue_try_pop(privSubscriptionAddQueue);
92 ObdRequest *req = (ObdRequest*)query;
93 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got subscription request for "<<req->req<<endl;
94 reqList.push_back(req->req);
97 query = g_async_queue_try_pop(privCommandQueue);
100 ObdRequest *req = (ObdRequest*)query;
101 //commandMap[req->req] = req->arg;
102 //printf("Command: %s\n",req->req.c_str());
103 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Command:" << req->req << endl;
104 if (req->req == "connect")
106 //printf("First: %s\nSecond: %s\n",req->arg.substr(0,req->arg.find(':')).c_str(),req->arg.substr(req->arg.find(':')+1).c_str());
107 std::string port = req->arg.substr(0,req->arg.find(':'));
108 int baud = boost::lexical_cast<int>(req->arg.substr(req->arg.find(':')+1));
109 obd->openPort(port.c_str(),baud);
111 obd->sendObdRequestString("ATZ\r",4,&replyVector,500,3);
112 for (unsigned int i=0;i<replyVector.size();i++)
114 reply += replyVector[i];
116 if (reply.find("ELM") == -1)
119 //printf("Error!\n");
120 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error resetting ELM\n";
124 //printf("Reply to reset: %s\n",reply.c_str());
126 if (!sendElmCommand(obd,"ATSP0"))
128 //printf("Error sending echo\n");
129 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error setting auto protocol"<<endl;
131 if (!sendElmCommand(obd,"ATE0"))
133 //printf("Error sending echo\n");
134 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off echo"<<endl;
136 if (!sendElmCommand(obd,"ATH0"))
138 //printf("Error sending headers off\n");
139 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off headers"<<endl;
141 if (!sendElmCommand(obd,"ATL0"))
143 //printf("Error turning linefeeds off\n");
144 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off linefeeds"<<endl;
149 query = g_async_queue_try_pop(privSubscriptionRemoveQueue);
150 if (query != nullptr)
152 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got unsubscription request\n";
153 ObdRequest *req = (ObdRequest*)query;
154 for (std::list<std::string>::iterator i=reqList.begin();i!= reqList.end();i++)
156 if ((*i) == req->req)
162 //reqList.push_back(req->req);
166 for (std::list<std::string>::iterator i=reqList.begin();i!= reqList.end();i++)
168 repeatReqList.push_back(*i);
170 for (std::list<std::string>::iterator i=repeatReqList.begin();i!= repeatReqList.end();i++)
172 //printf("Req: %s\n",(*i).c_str());
173 if ((*i) == "ATRV\r")
175 //printf("Requesting voltage...\n");
176 if (!obd->sendObdRequestString((*i).c_str(),(*i).length(),&replyVector))
178 //printf("Unable to request voltage!!!\n");
179 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unable to request voltage!\n";
182 std::string replystring = "";
183 for (int j=0;j<replyVector.size();j++)
185 replystring += replyVector[j];
187 //printf("Voltage reply: %s\n",replystring.c_str());
188 replystring.substr(0,replystring.find("V"));
189 ObdReply *rep = new ObdReply();
191 rep->reply = replystring;
192 g_async_queue_push(privResponseQueue,rep);
194 if (!obd->sendObdRequest((*i).c_str(),(*i).length(),&replyVector))
196 //printf("Error sending obd2 request\n");
197 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error sending OBD2 request\n";
200 //printf("Reply: %i %i\n",replyVector[0],replyVector[1]);
201 if (replyVector[0] == 0x41)
203 if (replyVector[1] == 0x0C)
205 double rpm = ((replyVector[2] << 8) + replyVector[3]) / 4.0;
206 ObdReply *rep = new ObdReply();
208 rep->reply = boost::lexical_cast<string>(rpm);
209 g_async_queue_push(privResponseQueue,rep);
210 //printf("RPM: %f\n",rpm);
212 else if (replyVector[1] == 0x0D)
214 int mph = replyVector[2];
215 ObdReply *rep = new ObdReply();
217 rep->reply = boost::lexical_cast<string>(mph);
218 g_async_queue_push(privResponseQueue,rep);
220 else if (replyVector[1] == 0x05)
222 int temp = replyVector[2] - 40;
223 ObdReply *rep = new ObdReply();
225 rep->reply = boost::lexical_cast<string>(temp);
226 g_async_queue_push(privResponseQueue,rep);
228 else if (replyVector[1] == 0x10)
230 double maf = ((replyVector[2] << 8) + replyVector[3]) / 100.0;
231 ObdReply *rep = new ObdReply();
233 rep->reply = boost::lexical_cast<string>(maf);
234 g_async_queue_push(privResponseQueue,rep);
238 //printf("Unknown response type: %i\n",replyVector[1]);
239 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unknown response type" << replyVector[1] << "\n";
242 else if (replyVector[0] == 0x49)
253 for (int j=0;j<replyVector.size();j++)
255 if(replyVector[j] == 0x49 && replyVector[j+1] == 0x02)
257 //We're at a reply header
260 if (replyVector[j] != 0x00)
262 vinstring += (char)replyVector[j];
263 //printf("VIN: %i %c\n",replyVector[j],replyVector[j]);
266 ObdReply *rep = new ObdReply();
268 rep->reply = vinstring;
269 g_async_queue_push(privResponseQueue,rep);
270 //printf("VIN Number: %i %s\n",replyVector.size(),vinstring.c_str());
274 //DebugOut()<<"Reply: "<<replyVector[2]<<" "<<replyVector[3]<<endl;
276 if(!reqList.size()) usleep(10000);
277 repeatReqList.clear();
282 static int updateProperties(/*gpointer retval,*/ gpointer data)
285 OBD2Source* src = (OBD2Source*)data;
287 while(gpointer retval = g_async_queue_try_pop(src->responseQueue))
289 ObdReply *reply = (ObdReply*)retval;
290 if (reply->req == "05")
292 VehicleProperty::EngineCoolantTemperatureType speed(reply->reply);
293 src->updateProperty(VehicleProperty::EngineCoolantTemperature,&speed);
295 else if (reply->req == "0C")
297 VehicleProperty::EngineSpeedType speed(reply->reply);
298 src->updateProperty(VehicleProperty::EngineSpeed,&speed);
300 else if (reply->req == "0D")
302 VehicleProperty::VehicleSpeedType speed(reply->reply);
303 src->updateProperty(VehicleProperty::VehicleSpeed,&speed);
305 else if (reply->req == "10")
307 VehicleProperty::MassAirFlowType mass(reply->reply);
308 src->updateProperty(VehicleProperty::MassAirFlow,&mass);
310 else if (reply->req == "ATRV\r")
312 VehicleProperty::BatteryVoltageType volts(reply->reply);
313 src->updateProperty(VehicleProperty::BatteryVoltage,&volts);
316 else if (reply->req == "0902")
319 VehicleProperty::VINType vin(reply->reply);
320 src->updateProperty(VehicleProperty::VIN,&vin);
321 VehicleProperty::WMIType wmi(reply->reply.substr(0,3));
322 src->updateProperty(VehicleProperty::WMI,&wmi);
324 else if (reply->req == "5C")
326 VehicleProperty::EngineCoolantTemperatureType ect(reply->reply);
327 src->updateProperty(VehicleProperty::EngineCoolantTemperature,&ect);
329 else if (reply->req == "46")
331 VehicleProperty::InteriorTemperatureType temp(reply->reply);
332 src->updateProperty(VehicleProperty::InteriorTemperature,&temp);
334 //5C -- engine oil temp
341 void OBD2Source::updateProperty(VehicleProperty::Property property,AbstractPropertyType* value)
343 //m_re->updateProperty(property,&value);
344 m_re->updateProperty(property,value);
345 if (propertyReplyMap.find(property) != propertyReplyMap.end())
347 propertyReplyMap[property]->value = value;
348 propertyReplyMap[property]->completed(propertyReplyMap[property]);
349 propertyReplyMap.erase(property);
352 void OBD2Source::mafValue(double maf)
354 VehicleProperty::VehicleSpeedType emaf(maf);
355 m_re->updateProperty(VehicleProperty::MassAirFlow,&emaf);
357 void OBD2Source::engineCoolantTemp(int temp)
359 VehicleProperty::VehicleSpeedType etemp(temp);
360 m_re->updateProperty(VehicleProperty::EngineCoolantTemperature,&etemp);
362 void OBD2Source::engineSpeed(double speed)
364 VehicleProperty::VehicleSpeedType espeed(speed);
365 m_re->updateProperty(VehicleProperty::EngineSpeed,&espeed);
367 void OBD2Source::vehicleSpeed(int speed)
369 VehicleProperty::EngineSpeedType vspeed(speed);
370 m_re->updateProperty(VehicleProperty::VehicleSpeed,&vspeed);
372 void OBD2Source::setSupported(PropertyList list)
374 m_supportedProperties = list;
375 m_re->updateSupported(list,PropertyList());
377 /*void OBD2Source::propertySignal(VehicleProperty::Property property,boost::any value)
380 void OBD2Source::checkProperty()
383 void OBD2Source::setConfiguration(map<string, string> config)
385 // //Config has been passed, let's start stuff up.
386 configuration = config;
389 std::string port = "/dev/ttyUSB0";
390 std::string baud = "115200";
391 std::string btadapter = "";
394 //printf("OBD2Source::setConfiguration\n");
395 for (map<string,string>::iterator i=configuration.begin();i!=configuration.end();i++)
397 //printf("Incoming setting: %s:%s\n",(*i).first.c_str(),(*i).second.c_str());
398 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Incoming setting:" << (*i).first << ":" << (*i).second << "\n";
399 if ((*i).first == "device")
403 else if ((*i).first == "baud")
408 else if ((*i).first == "bluetoothAdapter")
410 btadapter = (*i).second;
414 if(port.find(":") != string::npos)
417 DebugOut()<<"bluetooth device?"<<endl;
418 ObdBluetoothDevice bt;
420 std::string tempPort = bt.getDeviceForAddress(port, btadapter);
423 DebugOut(3)<<"Using bluetooth device \""<<port<<"\" bound to: "<<tempPort<<endl;
426 else throw std::runtime_error("Device Error");
429 ObdRequest *requ = new ObdRequest();
430 requ->req = "connect";
431 requ->arg = port + ":" + baud;
432 g_async_queue_push(commandQueue,requ);
435 OBD2Source::OBD2Source(AbstractRoutingEngine *re, map<string, string> config) : AbstractSource(re, config)
437 clientConnected = false;
442 for(auto itr = obd2amb.propertyPidMap.begin(); itr != obd2amb.propertyPidMap.end(); itr++)
444 m_supportedProperties.push_back((*itr).first);
447 re->setSupported(supported(), this);
448 /*if (openPort(std::string("/dev/pts/7"),115200))
450 printf("Error opening OBD2 port\n");
452 commandQueue = g_async_queue_new();
453 subscriptionAddQueue = g_async_queue_new();
454 subscriptionRemoveQueue = g_async_queue_new();
455 responseQueue = g_async_queue_new();
456 singleShotQueue = g_async_queue_new();
457 g_thread_new("mythread",(GThreadFunc)&threadLoop,this);
459 setConfiguration(config);
461 //AsyncQueueWatcher * watcher = new AsyncQueueWatcher(responseQueue, (AsyncQueueWatcherCallback) updateProperties, this);
463 //g_timeout_add(1,updateProperties, this);
464 g_idle_add(updateProperties, this);
465 //g_timeout_add(1000,calcCPS,NULL);
469 PropertyList OBD2Source::supported()
471 return m_supportedProperties;
473 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
475 return new OBD2Source(routingengine, config);
478 string OBD2Source::uuid()
480 return "f77af740-f1f8-11e1-aff1-0800200c9a66";
482 void OBD2Source::subscribeToPropertyChanges(VehicleProperty::Property property)
484 /*//printf("Subscribed to property: %s\n",property.c_str());
485 if (property == VehicleProperty::EngineSpeed)
487 ObdRequest *requ = new ObdRequest();
488 requ->req = "010C1\r";
489 g_async_queue_push(subscriptionAddQueue,requ);
491 else if (property == VehicleProperty::MassAirFlow)
493 ObdRequest *requ = new ObdRequest();
494 requ->req = "01101\r";
495 g_async_queue_push(subscriptionAddQueue,requ);
497 else if (property == VehicleProperty::VehicleSpeed)
499 ObdRequest *requ = new ObdRequest();
500 requ->req = "010D1\r";
501 g_async_queue_push(subscriptionAddQueue,requ);
503 else if (property == VehicleProperty::EngineCoolantTemperature)
505 ObdRequest *requ = new ObdRequest();
506 requ->req = "01051\r";
507 g_async_queue_push(subscriptionAddQueue,requ);
509 else if (property == VehicleProperty::VIN)
511 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!\n";
513 else if (property == VehicleProperty::WMI)
515 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!\n";
517 else if (property == VehicleProperty::EngineOilTemperature)
519 ObdRequest *requ = new ObdRequest();
520 requ->req = "015C1\r";
521 g_async_queue_push(subscriptionAddQueue,requ);
523 else if (property == VehicleProperty::InteriorTemperature)
525 ObdRequest *requ = new ObdRequest();
526 requ->req = "01461\r";
527 g_async_queue_push(subscriptionAddQueue,requ);
529 else if (property == VehicleProperty::BatteryVoltage)
531 ObdRequest *requ = new ObdRequest();
532 requ->req = "ATRV\r";
533 g_async_queue_push(subscriptionAddQueue,requ);
535 /*m_supportedProperties.push_back(VehicleProperty::VIN);
536 m_supportedProperties.push_back(VehicleProperty::WMI);
537 m_supportedProperties.push_back(VehicleProperty::EngineOilTemperature);
538 m_supportedProperties.push_back(VehicleProperty::InteriorTemperature);
539 m_supportedProperties.push_back(VehicleProperty::BatteryVoltage);*/
542 //printf("Unsupported property: %s\n",property.c_str());
543 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unsupported property requested:" << property << "\n";
546 if (property == VehicleProperty::VIN)
548 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!"<<endl;
550 else if (property == VehicleProperty::WMI)
552 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!"<<endl;
556 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
558 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
563 ObdRequest *requ = new ObdRequest();
564 requ->req = obd2amb.propertyPidMap[property];
565 g_async_queue_push(subscriptionAddQueue,requ);
570 void OBD2Source::unsubscribeToPropertyChanges(VehicleProperty::Property property)
573 /*if (property == VehicleProperty::EngineSpeed)
575 ObdRequest *requ = new ObdRequest();
576 requ->req = "010C1\r";
577 g_async_queue_push(subscriptionRemoveQueue,requ);
579 else if (property == VehicleProperty::MassAirFlow)
581 ObdRequest *requ = new ObdRequest();
582 requ->req = "01101\r";
583 g_async_queue_push(subscriptionRemoveQueue,requ);
585 else if (property == VehicleProperty::VehicleSpeed)
587 ObdRequest *requ = new ObdRequest();
588 requ->req = "010D1\r";
589 g_async_queue_push(subscriptionRemoveQueue,requ);
591 else if (property == VehicleProperty::EngineCoolantTemperature)
593 ObdRequest *requ = new ObdRequest();
594 requ->req = "01051\r";
595 g_async_queue_push(subscriptionRemoveQueue,requ);
597 else if (property == VehicleProperty::VIN)
599 ObdRequest *requ = new ObdRequest();
600 requ->req = "0902\r";
601 g_async_queue_push(subscriptionRemoveQueue,requ);
603 else if (property == VehicleProperty::WMI)
605 ObdRequest *requ = new ObdRequest();
606 requ->req = "0902\r";
607 g_async_queue_push(subscriptionRemoveQueue,requ);
609 else if (property == VehicleProperty::EngineOilTemperature)
611 ObdRequest *requ = new ObdRequest();
612 requ->req = "015C1\r";
613 g_async_queue_push(subscriptionRemoveQueue,requ);
615 else if (property == VehicleProperty::InteriorTemperature)
617 ObdRequest *requ = new ObdRequest();
618 requ->req = "01461\r";
619 g_async_queue_push(subscriptionRemoveQueue,requ);
621 else if (property == VehicleProperty::BatteryVoltage)
623 ObdRequest *requ = new ObdRequest();
624 requ->req = "ATRV\r";
625 g_async_queue_push(subscriptionRemoveQueue,requ);
629 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
631 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
636 ObdRequest *requ = new ObdRequest();
637 requ->req = obd2amb.propertyPidMap[property];
638 g_async_queue_push(subscriptionRemoveQueue,requ);
642 void OBD2Source::getPropertyAsync(AsyncPropertyReply *reply)
644 propertyReplyMap[reply->property] = reply;
645 VehicleProperty::Property property = reply->property;
647 //TODO: There is a much better way to do this, but for now it's hardcoded.
648 /*if (property == VehicleProperty::EngineSpeed)
650 ObdRequest *requ = new ObdRequest();
651 requ->req = "010C\r";
652 g_async_queue_push(singleShotQueue,requ);
654 else if (property == VehicleProperty::MassAirFlow)
656 ObdRequest *requ = new ObdRequest();
657 requ->req = "0110\r";
658 g_async_queue_push(singleShotQueue,requ);
660 else if (property == VehicleProperty::VehicleSpeed)
662 ObdRequest *requ = new ObdRequest();
663 requ->req = "010D\r";
664 g_async_queue_push(singleShotQueue,requ);
666 else if (property == VehicleProperty::EngineCoolantTemperature)
668 ObdRequest *requ = new ObdRequest();
669 requ->req = "0105\r";
670 g_async_queue_push(singleShotQueue,requ);
672 else if (property == VehicleProperty::VIN)
674 ObdRequest *requ = new ObdRequest();
675 requ->req = "0902\r";
676 g_async_queue_push(singleShotQueue,requ);
678 else if (property == VehicleProperty::WMI)
680 ObdRequest *requ = new ObdRequest();
681 requ->req = "0902\r";
682 g_async_queue_push(singleShotQueue,requ);
684 else if (property == VehicleProperty::EngineOilTemperature)
686 ObdRequest *requ = new ObdRequest();
687 requ->req = "015C\r";
688 g_async_queue_push(singleShotQueue,requ);
690 else if (property == VehicleProperty::InteriorTemperature)
692 ObdRequest *requ = new ObdRequest();
693 requ->req = "0146\r";
694 g_async_queue_push(singleShotQueue,requ);
696 else if (property == VehicleProperty::BatteryVoltage)
698 ObdRequest *requ = new ObdRequest();
699 requ->req = "ATRV\r";
700 g_async_queue_push(singleShotQueue,requ);
704 ///Here's a better way:
706 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
708 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
713 ObdRequest *requ = new ObdRequest();
714 requ->req = obd2amb.propertyPidMap[property];
715 g_async_queue_push(singleShotQueue,requ);
718 AsyncPropertyReply *OBD2Source::setProperty(AsyncSetPropertyRequest request )
720 AsyncPropertyReply* reply = new AsyncPropertyReply (request);
721 reply->success = false;
724 reply->completed(reply);