Power Gauge Pro
A digital dash for aftermarket automotive: custom hardware, embedded C++, online marketplace.
From Concept to Product
Founded PGP from market analysis through product development. Identified gaps in existing aftermarket digital dashes, developed detailed product specifications, found all technology suppliers, hired and managed the engineering team. Full product lifecycle ownership as a solo founder - from the first competitive analysis through hardware bringup, embedded software, and marketplace development.
Every layer designed, built, or integrated in-house.
The Software
C++/Qt6/QML application on Yocto Linux - CAN Bus frame decoding, datalogging, odometer calculation, OTA updates via Mender, and a formula engine with 2,348 unit test assertions.
#include "backend/formulas/Formula.h" #include "backend/core/logging/LoggingCategories.h" #include "backend/formulas/BaseOperation.h" Formula::Formula(QObject *parent) : QObject{parent} , m_value{Value{QVariant{}}} {} void Formula::onExpressionValueChanged() { // Get the cached value from expression Value newValue{m_expressionRoot ? m_expressionRoot->value() : Value{QVariant{}}}; if (newValue != m_value) { m_value = newValue; emit valueChanged(name(), newValue); } } QString Formula::name() const { return m_name; } void Formula::setName(const QString &newName) { m_name = newName; } Value Formula::value() { if (m_expressionRoot.isNull()) return Value{QVariant{}}; return m_expressionRoot->value(); } void Formula::parseFormula(const QJsonObject &description) { m_expressionRoot = BaseOperation::parseExpression(description, this); if (!m_expressionRoot) { QString errorMsg{"Failed to parse formula expression"}; m_currentErrors.clear(); m_currentErrors.append(FormulaError{errorMsg, FormulaError::Error}); qCWarning(pgpFormulasFormula) << errorMsg; emit errorsChanged(); return; } // Clear any previous errors if (!m_currentErrors.isEmpty()) { m_currentErrors.clear(); emit errorsChanged(); } // Connect error propagation from expression tree connect( m_expressionRoot.data(), &Expression::errorsChanged, this, &Formula::onExpressionErrorsChanged); // Connect value changes connect( m_expressionRoot.data(), &Expression::valueChanged, this, &Formula::onExpressionValueChanged); // Initialize value by evaluating the expression if (m_expressionRoot) { m_value = m_expressionRoot->evaluate(); m_currentErrors = m_expressionRoot->currentErrors(); if (!m_currentErrors.isEmpty()) { emit errorsChanged(); } } }
The Hardware
12.3" capacitive touch display powered by NXP iMX8MP on a custom carrier board. Managed PCB Design, board bringup with logic analyzers and oscilloscopes, and supplier coordination across multiple vendors. Serial protocols include SPI, I2C, and UART for peripheral communication.
The Marketplace
Built a PHP/Laravel/React/TypeScript platform where users create and sell custom dash designs. OAuth and Device Code authentication, REST API for device communication, Filament admin interface. Content is delivered to devices via libgit2 - each design is a git repository enabling efficient incremental updates.
Sensor Modules
ESP32-based accessory modules with custom PCBs for analog-to-digital sensor conversion. PlatformIO firmware, Flutter/Dart configuration app over Bluetooth. These modules extend the PGP dash to read analog sensors like boost pressure, exhaust gas temperature, and fuel level that are not available on the vehicle CAN bus.
Gallery