diff --git a/.gitignore b/.gitignore index 141c45ba..edc48278 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,8 @@ openauto.ini # Plugins # ########### +!plugins/vehicle/ plugins/vehicle/* !plugins/vehicle/test +!plugins/vehicle/obd2 !plugins/vehicle/README.md diff --git a/include/app/arbiter.hpp b/include/app/arbiter.hpp index 37c933c3..e30a4b67 100644 --- a/include/app/arbiter.hpp +++ b/include/app/arbiter.hpp @@ -39,6 +39,7 @@ class Arbiter : public QObject { void set_action(Action *action, QString key); void send_openauto_button_press(aasdk::proto::enums::ButtonCode::Enum buttonCode, openauto::projection::WheelDirection wheelDirection = openauto::projection::WheelDirection::NONE); void send_openauto_full_screen(bool fullscreen = true); + void send_vehicle_data(QString gauge_id, int value); QMainWindow *window() { return this->window_; } QSettings &settings() { return this->session_.settings_; } @@ -68,4 +69,5 @@ class Arbiter : public QObject { void action_changed(Action *action, QString key); void openauto_button_press(aasdk::proto::enums::ButtonCode::Enum buttonCode, openauto::projection::WheelDirection wheelDirection); void openauto_full_screen(bool fullscreen); + void vehicle_update_data(QString gauge_id, int value); }; diff --git a/include/app/pages/vehicle.hpp b/include/app/pages/vehicle.hpp index b9e912ca..87bf9a1b 100644 --- a/include/app/pages/vehicle.hpp +++ b/include/app/pages/vehicle.hpp @@ -6,16 +6,40 @@ #include "canbus/socketcanbus.hpp" #include "obd/message.hpp" -#include "obd/command.hpp" +#include "obd/conversions.hpp" + #include "app/widgets/selector.hpp" #include "app/widgets/dialog.hpp" - #include "app/pages/page.hpp" class Arbiter; -typedef std::function obd_decoder_t; typedef QPair units_t; +struct font_size_t { + int label; + int value; + int unit; +}; +typedef std::function unit_converter_t; + +struct GaugeConfig { + QString id; + QString description; + units_t units; + font_size_t font_size; + int precision; + unit_converter_t converter; +}; + +// typedef QList Gauges; +struct GaugesConfig { + GaugeConfig LOAD; + GaugeConfig COOLANT_TEMP; + GaugeConfig RPM; + GaugeConfig SPEED; + GaugeConfig INTAKE_TEMP; + GaugeConfig MPG; +}; class Gauge : public QWidget { Q_OBJECT @@ -23,25 +47,22 @@ class Gauge : public QWidget { public: enum Orientation { BOTTOM, RIGHT }; - Gauge(units_t units, QFont value_font, QFont unit_font, Orientation orientation, int rate, - std::vector cmds, int precision, obd_decoder_t decoder, QWidget *parent = nullptr); + Gauge(GaugeConfig cfg, QFont value_font, QFont unit_font, Orientation orientation, QWidget *parent = nullptr); - inline void start() { this->timer->start(this->rate); } - inline void stop() { this->timer->stop(); } - void can_callback(QByteArray payload); + inline QString get_id() { return this->id; }; + void set_value(int value); private: QString format_value(double value); QString null_value(); QLabel *value_label; - obd_decoder_t decoder; - std::vector cmds; + unit_converter_t converter; + QString id; bool si; - int rate; int precision; - QTimer *timer; + units_t units; signals: void toggle_unit(bool si); @@ -82,8 +103,7 @@ class DataTab : public QWidget { QWidget *speedo_tach_widget(); // QWidget *mileage_data_widget(); QWidget *engine_data_widget(); - QWidget *coolant_temp_widget(); - QWidget *engine_load_widget(); + QWidget *vehicle_data_widget(GaugeConfig cfg); std::vector gauges; }; diff --git a/include/obd/command.hpp b/include/obd/command.hpp index b0ed59cc..a0bec09b 100644 --- a/include/obd/command.hpp +++ b/include/obd/command.hpp @@ -2,22 +2,29 @@ #include #include + +#include #include #include "obd/message.hpp" +typedef std::function decoder_t; + struct Command { - std::string description; + QString id; QCanBusFrame frame; - std::function decoder; + decoder_t decoder; + QTimer *timer; + int rate; }; -struct Commands { - Command LOAD; - Command COOLANT_TEMP; - Command RPM; - Command SPEED; - Command INTAKE_TEMP; - Command MAF; -}; +typedef QList Commands; +// struct Commands { +// Command LOAD; +// Command COOLANT_TEMP; +// Command RPM; +// Command SPEED; +// Command INTAKE_TEMP; +// Command MAF; +// }; extern Commands cmds; diff --git a/include/obd/conversions.hpp b/include/obd/conversions.hpp index 82d13f5f..5c95b789 100644 --- a/include/obd/conversions.hpp +++ b/include/obd/conversions.hpp @@ -1,9 +1,14 @@ #pragma once -double kph_to_mph(double val) { return val * 0.621371; } +class Conversion { + public: + static double kph_to_mph(double val) { return val * 0.621371; }; -double c_to_f(double val) { return (val * 9 / 5) + 32; } + static double c_to_f(double val) { return (val * 9 / 5) + 32; }; -double gps_to_gph(double val) { return val * 0.0805; } + static double gps_to_gph(double val) { return val * 0.0805; }; -double gps_to_lph(double val) { return val * 3.6; } + static double gps_to_lph(double val) { return val * 3.6; }; + + static double l100km_to_mpg(double val) { return (val == 0 ? 0 : (235.214583 / val)); }; +}; diff --git a/plugins/vehicle/obd2/README.md b/plugins/vehicle/obd2/README.md new file mode 100644 index 00000000..9953d936 --- /dev/null +++ b/plugins/vehicle/obd2/README.md @@ -0,0 +1,21 @@ +# OBD2 integration + +To test without OBD2, send packets via CANBUS: + +```bash +cansend can0 7e8#03010D1e11100000 # speed 30 km/h +cansend can0 7e8#03010C1e11100000 # rpm to 1.9 +cansend can0 7e8#0301056e00000000 # temp to 70C +cansend can0 7e8#0301046e11100000 # engine load 43% +# mpg - 10l/100km (23mpg) +cansend can0 7e8#030110004f111110 && cansend can0 7e8#03010D1e11100000 +*/ +``` + +Current functionality: + +* Speed +* RPM +* Engine temp +* Engine load +* MPG diff --git a/plugins/vehicle/obd2/obd2.cpp b/plugins/vehicle/obd2/obd2.cpp new file mode 100644 index 00000000..c411bad1 --- /dev/null +++ b/plugins/vehicle/obd2/obd2.cpp @@ -0,0 +1,44 @@ +#include "obd2.hpp" + +bool Obd2::init(ICANBus* canbus){ + if (this->arbiter) { + OBD2_LOG(info)<<"loading plugin, totoal commands: "<registerFrameHandler(cmds.at(i).frame.frameId()+0x9, [this,i](QByteArray payload){this->readObd2(cmds.at(i), payload);}); + cmds[i].timer = new QTimer(this); + connect(cmds.at(i).timer, &QTimer::timeout, [this, canbus, i]() { + canbus->writeFrame(cmds.at(i).frame); + }); + cmds.at(i).timer->start(cmds.at(i).rate); + OBD2_LOG(info)<<"loaded "<< QString((cmds.at(i).id)).toStdString(); + } + OBD2_LOG(info)<<"loaded successfully"; + return true; + } + else{ + OBD2_LOG(error)<<"Failed to get arbiter"; + return false; + } +} + +QList Obd2::widgets() +{ + QList tabs; + return tabs; +} + +void Obd2::readObd2(Command cmd, QByteArray payload){ + Response resp = Response(payload); + if(cmd.frame.payload().at(2) == resp.PID){ + double value = cmd.decoder(resp); + OBD2_LOG(debug)<maf_rate = Conversion::gps_to_lph(value); + } else if (cmd.id == "speed") { + const double mpg = this->maf_rate == 0 ? 0 : value / this->maf_rate; + this->arbiter->vehicle_update_data("mpg", mpg); + } + this->arbiter->vehicle_update_data(cmd.id, value); + } +} diff --git a/plugins/vehicle/obd2/obd2.hpp b/plugins/vehicle/obd2/obd2.hpp new file mode 100644 index 00000000..9a0a9eb9 --- /dev/null +++ b/plugins/vehicle/obd2/obd2.hpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include "plugins/vehicle_plugin.hpp" +#include "obd/command.hpp" +#include "obd/conversions.hpp" + +#include "app/arbiter.hpp" +#include "openauto/Service/InputService.hpp" + +#define OBD2_LOG(severity) BOOST_LOG_TRIVIAL(severity) << "[OBD2Plugin] " + +class Obd2 : public QObject, VehiclePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID VehiclePlugin_iid) + Q_INTERFACES(VehiclePlugin) + + public: + bool init(ICANBus* canbus) override; + + private: + QList widgets() override; + double maf_rate; + void readObd2(Command cmd, QByteArray payload); +}; diff --git a/src/app/arbiter.cpp b/src/app/arbiter.cpp index eaf99cc7..f952fd7d 100644 --- a/src/app/arbiter.cpp +++ b/src/app/arbiter.cpp @@ -199,3 +199,8 @@ void Arbiter::send_openauto_full_screen(bool fullscreen) { emit openauto_full_screen(fullscreen); } + +void Arbiter::send_vehicle_data(QString gauge_id, int value) +{ + emit vehicle_update_data(gauge_id, value); +} diff --git a/src/app/pages/vehicle.cpp b/src/app/pages/vehicle.cpp index cbeddba6..a57795e8 100644 --- a/src/app/pages/vehicle.cpp +++ b/src/app/pages/vehicle.cpp @@ -4,30 +4,43 @@ #include "app/config.hpp" #include "app/pages/vehicle.hpp" #include "app/window.hpp" -#include "obd/conversions.hpp" #include "canbus/elm327.hpp" #include "plugins/vehicle_plugin.hpp" -Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientation orientation, int rate, - std::vector cmds, int precision, obd_decoder_t decoder, QWidget *parent) +GaugesConfig gauges_cfg = +{ + {"load", "Calculated Engine Load", {"%", "%"}, + {10, 16, 12}, 1, [](double x, bool _) { return x; } + }, + {"coolant_temp", "Engine Coolant Temperature", {"°F", "°C"}, + {10, 16, 12}, 1, [](double x, bool si) { return si ? x : Conversion::c_to_f(x); } + }, + {"rpm", "Engine Revolutions Per Minute (RPM)", {"x1000rpm", "x1000rpm"}, + {0, 24, 12}, 1, [](double x, bool _) { return x / 1000.0; } + }, + {"speed", "Vehicle Speed", {"mph", "km/h"}, + {0, 36, 16}, 0, [](double x, bool si) { return si ? x : Conversion::kph_to_mph(x); } + }, + {"intake_temp", "Intake Air Temperature", {"°F", "°C"}, + {10, 16, 12}, 1, [](double x, bool si) { return si ? x : Conversion::c_to_f(x); } + }, + {"mpg", "Petrol consumption", {"mpg", "l/100km"}, + {10, 16, 12}, 1, [](double x, bool si) { return si ? x : Conversion::l100km_to_mpg(x); } + } +}; + +Gauge::Gauge(GaugeConfig cfg, QFont value_font, QFont unit_font, Gauge::Orientation orientation, QWidget *parent) : QWidget(parent) { Config *config = Config::get_instance(); - ICANBus *bus = (config->get_vehicle_can_bus())?((ICANBus *)SocketCANBus::get_instance()):((ICANBus *)elm327::get_instance()); - - using namespace std::placeholders; - std::function callback = std::bind(&Gauge::can_callback, this, std::placeholders::_1); - - bus->registerFrameHandler(cmds[0].frame.frameId()+0x9, callback); - DASH_LOG(info)<<"[Gauges] Registered frame handler for id "<<(cmds[0].frame.frameId()+0x9); + this->id = cfg.id; this->si = config->get_si_units(); - this->rate = rate; - this->precision = precision; + this->precision = cfg.precision; - this->cmds = cmds; - this->decoder = decoder; + this->units = cfg.units; + this->converter = cfg.converter; QBoxLayout *layout; if (orientation == BOTTOM) @@ -39,20 +52,13 @@ Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientatio value_label->setFont(value_font); value_label->setAlignment(Qt::AlignCenter); - QLabel *unit_label = new QLabel(this->si ? units.second : units.first, this); + QLabel *unit_label = new QLabel(this->si ? this->units.second : this->units.first, this); unit_label->setFont(unit_font); unit_label->setAlignment(Qt::AlignCenter); - this->timer = new QTimer(this); - connect(this->timer, &QTimer::timeout, [this, bus, cmds]() { - for (auto cmd : cmds) { - bus->writeFrame(cmd.frame); - } - }); - - connect(config, &Config::si_units_changed, [this, units, unit_label](bool si) { + connect(config, &Config::si_units_changed, [this, unit_label](bool si) { this->si = si; - unit_label->setText(this->si ? units.second : units.first); + unit_label->setText(this->si ? this->units.second : this->units.first); value_label->setText(this->null_value()); }); @@ -63,13 +69,9 @@ Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientatio layout->addStretch(4); } -void Gauge::can_callback(QByteArray payload){ - Response resp = Response(payload); - for(auto cmd : cmds){ - if(cmd.frame.payload().at(2) == resp.PID){ - value_label->setText(this->format_value(this->decoder(cmd.decoder(resp), this->si))); - } - } +void Gauge::set_value(int value){ + DASH_LOG(debug)<<"[Gauges] set_value: "<setText(this->format_value(this->converter(value, this->si))); } QString Gauge::format_value(double value) @@ -249,8 +251,16 @@ DataTab::DataTab(Arbiter &arbiter, QWidget *parent) QSizePolicy sp_right(QSizePolicy::Preferred, QSizePolicy::Preferred); sp_right.setHorizontalStretch(2); engine_data->setSizePolicy(sp_right); - for (auto &gauge : this->gauges) - gauge->start(); + + connect(&this->arbiter, &Arbiter::vehicle_update_data, [this](QString gauge_id, int value){ + // DASH_LOG(info)<<"[Gauges] arbiter update: "<gauges) { + if(gauge->get_id() == gauge_id){ + // DASH_LOG(info)<<"[Gauges] Found: "<get_id(); + gauge->set_value(value); + } + } + }); } QWidget *DataTab::speedo_tach_widget() @@ -260,33 +270,9 @@ QWidget *DataTab::speedo_tach_widget() layout->setContentsMargins(0, 0, 0, 0); layout->addStretch(3); - - QFont speed_value_font(this->arbiter.forge().font(36, true)); - - QFont speed_unit_font(this->arbiter.forge().font(16)); - speed_unit_font.setWeight(QFont::Light); - speed_unit_font.setItalic(true); - - Gauge *speed = new Gauge({"mph", "km/h"}, speed_value_font, speed_unit_font, - Gauge::BOTTOM, 100, {cmds.SPEED}, 0, - [](double x, bool si) { return si ? x : kph_to_mph(x); }, widget); - layout->addWidget(speed); - this->gauges.push_back(speed); - + layout->addWidget(this->vehicle_data_widget(gauges_cfg.SPEED)); layout->addStretch(2); - - QFont tach_value_font(this->arbiter.forge().font(24, true)); - - QFont tach_unit_font(this->arbiter.forge().font(12)); - tach_unit_font.setWeight(QFont::Light); - tach_unit_font.setItalic(true); - - Gauge *rpm = new Gauge({"x1000rpm", "x1000rpm"}, tach_value_font, - tach_unit_font, Gauge::BOTTOM, 100, {cmds.RPM}, 1, - [](double x, bool _) { return x / 1000.0; }, widget); - layout->addWidget(rpm); - this->gauges.push_back(rpm); - + layout->addWidget(this->vehicle_data_widget(gauges_cfg.RPM)); layout->addStretch(1); return widget; @@ -312,7 +298,7 @@ QWidget *DataTab::speedo_tach_widget() // unit_font.setItalic(true); // // Gauge *mileage = new Gauge({"mpg", "km/L"}, value_font, unit_font, -// Gauge::BOTTOM, 100, {cmds.SPEED, cmds.MAF}, 1, +// Gauge::BOTTOM, 100, {gauges_cfg.SPEED, gauges_cfg.MAF}, 1, // [](std::vector x, bool si) { // return (si ? x[0] : kph_to_mph(x[0])) / (si ? gps_to_lph(x[1]) : gps_to_gph(x[1])); // }, @@ -331,71 +317,47 @@ QWidget *DataTab::engine_data_widget() layout->setSpacing(0); layout->addStretch(); - layout->addWidget(this->coolant_temp_widget()); + layout->addWidget(this->vehicle_data_widget(gauges_cfg.COOLANT_TEMP)); layout->addStretch(); layout->addWidget(Session::Forge::br()); layout->addStretch(); - layout->addWidget(this->engine_load_widget()); + layout->addWidget(this->vehicle_data_widget(gauges_cfg.LOAD)); + layout->addStretch(); + layout->addWidget(Session::Forge::br()); + layout->addStretch(); + layout->addWidget(this->vehicle_data_widget(gauges_cfg.MPG)); layout->addStretch(); return widget; } -QWidget *DataTab::coolant_temp_widget() +QWidget *DataTab::vehicle_data_widget(GaugeConfig cfg) { QWidget *widget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - QFont value_font(this->arbiter.forge().font(16, true)); + QFont value_font(this->arbiter.forge().font(cfg.font_size.value, true)); - QFont unit_font(this->arbiter.forge().font(12)); + QFont unit_font(this->arbiter.forge().font(cfg.font_size.unit)); unit_font.setWeight(QFont::Light); unit_font.setItalic(true); - Gauge *coolant_temp = new Gauge( - {"°F", "°C"}, value_font, unit_font, Gauge::RIGHT, 5000, - {cmds.COOLANT_TEMP}, 1, [](double x, bool si) { return si ? x : c_to_f(x); }, widget); - layout->addWidget(coolant_temp); - this->gauges.push_back(coolant_temp); - - QFont label_font(this->arbiter.forge().font(10)); - label_font.setWeight(QFont::Light); - - QLabel *coolant_temp_label = new QLabel("coolant", widget); - coolant_temp_label->setFont(label_font); - coolant_temp_label->setAlignment(Qt::AlignHCenter); - layout->addWidget(coolant_temp_label); + Gauge *gauge = new Gauge(cfg, + value_font, unit_font, Gauge::RIGHT, widget); + layout->addWidget(gauge); + this->gauges.push_back(gauge); - return widget; -} + if (cfg.font_size.label > 0) { + QFont label_font(this->arbiter.forge().font(cfg.font_size.label)); + label_font.setWeight(QFont::Light); -QWidget *DataTab::engine_load_widget() -{ - QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - QFont value_font(this->arbiter.forge().font(16, true)); - - QFont unit_font(this->arbiter.forge().font(12)); - unit_font.setWeight(QFont::Light); - unit_font.setItalic(true); - - Gauge *engine_load = - new Gauge({"%", "%"}, value_font, unit_font, Gauge::RIGHT, - 500, {cmds.LOAD}, 1, [](double x, bool _) { return x; }, widget); - layout->addWidget(engine_load); - this->gauges.push_back(engine_load); - - QFont label_font(this->arbiter.forge().font(10)); - label_font.setWeight(QFont::Light); + QLabel *gauge_label = new QLabel(cfg.description, widget); + gauge_label->setFont(label_font); + gauge_label->setAlignment(Qt::AlignHCenter); + layout->addWidget(gauge_label); + } - QLabel *engine_load_label = new QLabel("load", widget); - engine_load_label->setFont(label_font); - engine_load_label->setAlignment(Qt::AlignHCenter); - layout->addWidget(engine_load_label); return widget; } diff --git a/src/obd/command.cpp b/src/obd/command.cpp index a0788edb..2f10fc2a 100644 --- a/src/obd/command.cpp +++ b/src/obd/command.cpp @@ -1,9 +1,11 @@ #include "obd/command.hpp" #include "obd/decoders.hpp" -Commands cmds = {{"Calculated Engine Load", QCanBusFrame(0x7df, QByteArray::fromHex("0201040000000000")), percentage}, - {"Engine Coolant Temperature", QCanBusFrame(0x7df, QByteArray::fromHex("0201050000000000")), temp}, - {"Engine Revolutions Per Minute (RPM)", QCanBusFrame(0x7df, QByteArray::fromHex("02010C0000000000")), rpm}, - {"Vehicle Speed", QCanBusFrame(0x7df, QByteArray::fromHex("02010D0000000000")), speed}, - {"Intake Air Temperature", QCanBusFrame(0x7df, QByteArray::fromHex("02010F0000000000")), temp}, - {"Mass Air Flow (MAF) Rate", QCanBusFrame(0x7df, QByteArray::fromHex("0201100000000000")), flow}}; +Commands cmds = { + {"load", QCanBusFrame(0x7df, QByteArray::fromHex("0201040000000000")), percentage, NULL, 200}, + {"coolant_temp", QCanBusFrame(0x7df, QByteArray::fromHex("0201050000000000")), temp, NULL, 5000}, + {"rpm", QCanBusFrame(0x7df, QByteArray::fromHex("02010C0000000000")), rpm, NULL, 200}, + {"speed", QCanBusFrame(0x7df, QByteArray::fromHex("02010D0000000000")), speed, NULL, 200}, + {"intake_temp", QCanBusFrame(0x7df, QByteArray::fromHex("02010F0000000000")), temp, NULL, 1000}, + {"maf_rate", QCanBusFrame(0x7df, QByteArray::fromHex("0201100000000000")), flow, NULL, 200} +};