Open-source OBD adapter

Information and discussion of EFI hardware and specifications
Posts: 21
Joined: Thu Sep 21, 2017 3:00 pm

Re: Open-source OBD adapter

Postby 160plus » Wed Dec 06, 2017 9:31 am

antus wrote:Yes, it is capable. I hacked a proof of concept in to it with success but it was messy. In the end I decided I wasnt happy with the code quality. The elm protocol sucks because you have to send 2 bytes for every byte of data. I was able up size the buffers to about 180 chars with no significant changes which allowed for about 80 bytes of actual VPW data after converting it back to binary on the interface before VPW transmission. It was an OK proof of concept, but needs a closer look and a cleaner implementation. Modifying the firmware to support a protocol more similar to the AVT was something I didnt have time to do. The timing requirements were pretty straight forward, though. The firmware was not stable with a buffer larger than about 180 bytes, but it totally would be possible if it was written properly.

How about a link to this"Messy" code?

Site Admin
User avatar
Posts: 4868
Joined: Sat Feb 28, 2009 8:34 pm

Re: Open-source OBD adapter

Postby antus » Wed Dec 06, 2017 11:59 am

Its GPL and I am required by license to provide it on request, therefore here is the diff. I wont support it, nor would I recommend using it. This was just an experimental proof of concept. I make no claims that this the 'right' way. Also, developing on master. Tsk tsk. Ive learnt how to and am now using git/gitflow since I last looked at this in 2016 :lol:

Code: Select all
# git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/adapter/adaptertypes.h
#   modified:   src/adapter/dispatcher.cpp
#   modified:   src/adapter/ecumsg.cpp
#   modified:   src/adapter/obd/j1850.h
#   modified:   src/adapter/obd/obdprofile.cpp
#   modified:   src/adapter/obd/padapter.h
#   modified:   src/adapter/obd/vpw.cpp
#   modified:   src/adapter/obd/vpw.h
#
no changes added to commit (use "git add" and/or "git commit -a")

# git diff
diff --git a/src/adapter/adaptertypes.h b/src/adapter/adaptertypes.h
old mode 100644
new mode 100755
index 19a02ba..07ce2b2
--- a/src/adapter/adaptertypes.h
+++ b/src/adapter/adaptertypes.h
@@ -16,10 +16,10 @@
 using namespace std;
 
 // Config settings
-const int OBD_IN_MSG_DLEN = 7;
+const int OBD_IN_MSG_DLEN = 256;
 const int OBD_IN_MSG_LEN  = OBD_IN_MSG_DLEN + 5; // 7 data + 4 header + 1 reserved
-const int RX_BUFFER_LEN   = 100;
-const int RX_CMD_LEN      = 20;         // The incoming cmd
+const int RX_BUFFER_LEN   = 256 + 5; // This is the max string size of the ELM command. So should be slightly more than packetsize *2
+const int RX_CMD_LEN      = OBD_IN_MSG_LEN * 3 + 1; // The incoming cmd
 const int USER_BUF_LEN    = RX_CMD_LEN; // The previous cmd
 
 //
@@ -60,6 +60,8 @@ enum AT_Requests {
     PAR_WARMSTART,
     PAR_WIRING_TEST,
     PAR_USE_AUTO_SP,
+       PAR_VPW_4X,
+       PAR_RAW_PAYLOAD,
     // int properties
     PAR_CAN_CF = INT_PROPS_START,
     PAR_CAN_CM,
@@ -69,7 +71,7 @@ enum AT_Requests {
     PAR_WAKEUP_VAL,
     // bytes properties
     PAR_HEADER_BYTES = BYTES_PROPS_START,
-    PAR_WM_HEADER
+    PAR_WM_HEADER,
 };
 
 struct ByteArray {
diff --git a/src/adapter/dispatcher.cpp b/src/adapter/dispatcher.cpp
old mode 100644
new mode 100755
index 6c511ea..378db0f
--- a/src/adapter/dispatcher.cpp
+++ b/src/adapter/dispatcher.cpp
@@ -22,7 +22,7 @@ static const char ErrMessage [] = "?";
 static const char OkMessage  [] = "OK";
 static const char Version    [] = "1.05";
 static const char Interface  [] = "ELM327 v1.4";
-static const char Copyright  [] = "Copyright (c) 2009-2016 ObdDiag.Net";
+static const char Copyright  [] = "Copyright (c) 2009-2016 ObdDiag.Net, 2016 PCMHacking.net";
 static const char Copyright2 [] = "This is free software; see the source for copying conditions. There is NO";
 static const char Copyright3 [] = "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";
 
@@ -169,6 +169,7 @@ static void SetDefault()
     config->setBoolProperty(PAR_SPACES, true);
     config->setIntProperty(PAR_TIMEOUT, 0);
     config->setIntProperty(PAR_ISO_INIT_ADDRESS, 0x33);
+    config->setBoolProperty(PAR_VPW_4X, false);
     AdptSendReply(OkMessage);
 }
 
@@ -190,7 +191,7 @@ static void OnSetDefault(const string& cmd, int par)
  */
 static void OnProtocolDescribe(const string& cmd, int par)
 {   
-    OBDProfile::instance()->getProtocolDescription();
+    OBDProfile::instance()->getProtocolDescription();
 }
 
 /**
@@ -350,7 +351,12 @@ static const DispatchType dispatchTbl[] = {
     { "TP",   PAR_TRY_PROTOCOL,      1, 1, OnSetProtocol          },
     { "WM",   PAR_WM_HEADER,         1, 6, OnSetBytes             },
     { "WS",   PAR_WARMSTART,         0, 0, OnReset                },
-    { "Z",    PAR_RESET_CPU,         0, 0, OnReset                }
+    { "Z",    PAR_RESET_CPU,         0, 0, OnReset                },
+       { "V0",   PAR_VPW_4X,            0, 0, OnSetValueFalse        },
+       { "V1",   PAR_VPW_4X,            0, 0, OnSetValueTrue         },
+       { "R0",   PAR_RAW_PAYLOAD,       0, 0, OnSetValueFalse        },
+       { "R1",   PAR_RAW_PAYLOAD,       0, 0, OnSetValueTrue         },
+
 };
 
 static bool ValidateArgLength(const DispatchType& entry, const string& arg)
diff --git a/src/adapter/ecumsg.cpp b/src/adapter/ecumsg.cpp
old mode 100644
new mode 100755
index 69c3695..7a505df
--- a/src/adapter/ecumsg.cpp
+++ b/src/adapter/ecumsg.cpp
@@ -13,6 +13,7 @@ using namespace util;
 
 const int HEADER_SIZE = 3;
 
+
 class EcumsgISO9141 : public Ecumsg {
     friend class Ecumsg;
 public:
@@ -115,8 +116,10 @@ static void IsoAddChecksum(uint8_t* data, uint8_t& length)
  */
 static void StripHeader(uint8_t* data, uint8_t& length)
 {
-    length -= HEADER_SIZE;
-    memmove(&data[0], &data[HEADER_SIZE], length);
+       if (!AdapterConfig::instance()->getBoolProperty(PAR_RAW_PAYLOAD)) {
+               length -= HEADER_SIZE;
+               memmove(&data[0], &data[HEADER_SIZE], length);
+       }
 }
 
 /*
@@ -217,10 +220,12 @@ void Ecumsg::setHeader(const uint8_t* header)
  */
 void EcumsgISO9141::addHeaderAndChecksum()
 {
-    // Shift data on 3 bytes to accommodate the header
-    memmove(&data_[HEADER_SIZE], &data_[0], length_);
-    length_ += HEADER_SIZE;
-    memcpy(&data_[0], header_, HEADER_SIZE);
+       if (AdapterConfig::instance()->getBoolProperty(PAR_RAW_PAYLOAD)) {
+               // Shift data on 3 bytes to accommodate the header
+               memmove(&data_[HEADER_SIZE], &data_[0], length_);
+               length_ += HEADER_SIZE;
+               memcpy(&data_[0], header_, HEADER_SIZE);
+       }
     
     IsoAddChecksum(data_, length_);
 }
@@ -230,14 +235,15 @@ void EcumsgISO9141::addHeaderAndChecksum()
  */
 void EcumsgISO14230::addHeaderAndChecksum()
 {
-    // Shift data on 3 bytes to accommodate the header
-    memmove(&data_[HEADER_SIZE], &data_[0], length_);
-    uint8_t len = length_;
-    length_ += HEADER_SIZE;
-    memcpy(&data_[0], header_, HEADER_SIZE);
-   
-    // The length is in the 1st byte
-    data_[0] = (data_[0] & 0xC0) | len;
+       if (!AdapterConfig::instance()->getBoolProperty(PAR_RAW_PAYLOAD)) {
+               // Shift data on 3 bytes to accommodate the header
+               memmove(&data_[HEADER_SIZE], &data_[0], length_);
+               uint8_t len = length_;
+               length_ += HEADER_SIZE;
+               memcpy(&data_[0], header_, HEADER_SIZE);
+           // The length is in the 1st byte
+           data_[0] = (data_[0] & 0xC0) | len;
+       }
     
     IsoAddChecksum(data_, length_);
 }
@@ -247,11 +253,12 @@ void EcumsgISO14230::addHeaderAndChecksum()
  */
 void EcumsgVPW::addHeaderAndChecksum()
 {
-    // Shift data on 3 bytes to accommodate the header
-    memmove(&data_[HEADER_SIZE], &data_[0], length_);
-    length_ += HEADER_SIZE;
-    memcpy(&data_[0], header_, HEADER_SIZE);
-   
+       if (!AdapterConfig::instance()->getBoolProperty(PAR_RAW_PAYLOAD)) {
+               // Shift data on 3 bytes to accommodate the header
+               memmove(&data_[HEADER_SIZE], &data_[0], length_);
+               length_ += HEADER_SIZE;
+               memcpy(&data_[0], header_, HEADER_SIZE);
+       }
     J1850AddChecksum(data_, length_);
 }
 
@@ -260,10 +267,12 @@ void EcumsgVPW::addHeaderAndChecksum()
  */
 void EcumsgPWM::addHeaderAndChecksum()
 {
-    // Shift data on 3 bytes to accommodate the header
-    memmove(&data_[HEADER_SIZE], &data_[0], length_);
-    length_ += HEADER_SIZE;
-    memcpy(&data_[0], header_, HEADER_SIZE);
+       if (!AdapterConfig::instance()->getBoolProperty(PAR_RAW_PAYLOAD)) {
+               // Shift data on 3 bytes to accommodate the header
+               memmove(&data_[HEADER_SIZE], &data_[0], length_);
+               length_ += HEADER_SIZE;
+               memcpy(&data_[0], header_, HEADER_SIZE);
+       }
     
     J1850AddChecksum(data_, length_);
 }
diff --git a/src/adapter/obd/j1850.h b/src/adapter/obd/j1850.h
old mode 100644
new mode 100755
index c0dbab7..01587de
--- a/src/adapter/obd/j1850.h
+++ b/src/adapter/obd/j1850.h
@@ -14,9 +14,9 @@
 
 enum J1850Limits {
     J1850_BYTES_MIN =  1,
-    J1850_BYTES_MAX = 12,
+    J1850_BYTES_MAX = 256 + 5,
     OBD2_BYTES_MIN  =  5, // 3(header) + 1(data) + 1(checksum)
-    OBD2_BYTES_MAX  = 11  // 3(header) + 7(data) + 1(checksum)
+    OBD2_BYTES_MAX  = 256 + 5  // 3(header) + 7(data) + 1(checksum)
 };
     
 // J1850 Timeouts
@@ -44,7 +44,25 @@ enum VpwTimeouts {
     TV3_RX_MAX  =  239,
     TV5_RX_MIN  =  239,
     TV6_RX_MIN  =  280,
-    VPW_RX_MID  =   96
+    VPW_RX_MID  =   96,
+
+       TV1_4X_TX_NOM  =   64/4,
+       TV2_4X_TX_NOM  =  128/4,
+       TV3_4X_TX_NOM  =  200/4,
+       TV4_4X_TX_MIN  =  261/4,
+       TV6_4X_TX_MIN  =  280/4,
+       TV5_4X_TX_NOM  =  300/4,
+       TV5_4X_TX_MAX  =  5000/4,
+       TV6_4X_TX_NOM  =  300/4,
+       TV1_4X_TX_ADJ  =   64/4,
+       TV2_4X_TX_ADJ  =  128/4,
+       TV1_4X_RX_MIN  =   34/4,
+       TV2_4X_RX_MAX  =  163/4,
+       TV3_4X_RX_MIN  =  163/4,
+       TV3_4X_RX_MAX  =  239/4,
+       TV5_4X_RX_MIN  =  239/4,
+       TV6_4X_RX_MIN  =  280/4,
+       VPW_4X_RX_MID  =   96/4
 };
 
 // PWM Timeouts, in usec
diff --git a/src/adapter/obd/obdprofile.cpp b/src/adapter/obd/obdprofile.cpp
old mode 100644
new mode 100755
index f10a917..861c49c
--- a/src/adapter/obd/obdprofile.cpp
+++ b/src/adapter/obd/obdprofile.cpp
@@ -78,6 +78,7 @@ int OBDProfile::setProtocol(int num, bool refreshConnection)
             break;
         case PROT_J1850_VPW:
             adapter_ = ProtocolAdapter::getAdapter(ADPTR_VPW);
+            adapter_->setSpeed();
             break;
         case PROT_ISO9141:
             adapter_ = ProtocolAdapter::getAdapter(ADPTR_ISO);
@@ -164,7 +165,7 @@ void OBDProfile::onRequest(const string& cmdString)
  */
 int OBDProfile::onRequestImpl(const string& cmdString)
 {
-    const char* OBD_TEST_SEQ = "0100";
+       const char* OBD_TEST_SEQ = "0100";
     uint8_t data[OBD_IN_MSG_LEN];
 
     // Buffer overrun check,
diff --git a/src/adapter/obd/padapter.h b/src/adapter/obd/padapter.h
old mode 100644
new mode 100755
index 4f68b1e..0d62313
--- a/src/adapter/obd/padapter.h
+++ b/src/adapter/obd/padapter.h
@@ -39,7 +39,8 @@ enum ProtocolTypes {
    PROT_ISO15765_1150,
    PROT_ISO15765_2950,
    PROT_ISO15765_1125,
-   PROT_ISO15765_2925
+   PROT_ISO15765_2925,
+   PROT_J1850_VPW4X
 };
 
 // Adapters
@@ -67,6 +68,7 @@ public:
     virtual void close() {}
     virtual void wiringCheck() = 0;
     virtual void sendHeartBeat() {}
+    virtual void setSpeed() {} // vpw adapter overrides this and sets 1x or 4x
     virtual int getProtocol() const = 0;
     virtual void kwDisplay() {}
     bool isConnected() const { return connected_; }
diff --git a/src/adapter/obd/vpw.cpp b/src/adapter/obd/vpw.cpp
old mode 100644
new mode 100755
index ada0b87..38b88e5
--- a/src/adapter/obd/vpw.cpp
+++ b/src/adapter/obd/vpw.cpp
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <adaptertypes.h>
+#include <cstdio> // for sprintf debugging
 #include <GpioDrv.h>
 #include <Timer.h>
 #include <PwmDriver.h>
@@ -44,18 +45,18 @@ void VpwAdapter::close()
  */
 int VpwAdapter::sendToEcu(const Ecumsg* msg)
 {
-    insertToHistory(msg); // Buffer dump
+       insertToHistory(msg); // Buffer dump
     
     // Wait for bus to be inactive
     //
-    if (!driver_->wait4Ready(TV6_TX_NOM, TV4_TX_MIN, timer_)) {
+    if (!driver_->wait4Ready(tv6_tx_nom, tv4_tx_min, timer_)) {
         return 0;
     }
 
     TX_LED(true);  // Turn the transmit LED on
 
     // SOF pulse
-    driver_->sendSofVpw(TV3_TX_NOM); // 200us
+    driver_->sendSofVpw(tv3_tx_nom); // 200us
   
     for (int i = 0; i < msg->length(); i++) {
         uint8_t ch = msg->data()[i];  // sent next byte in buffer
@@ -64,12 +65,12 @@ int VpwAdapter::sendToEcu(const Ecumsg* msg)
         while (bits--) {  // send each bit in the byte
             if (bits & 0x01) { // Passive, set bus to 0
                 if (ch & 0x80)  {
-                    driver_->sendPulseVpw(TV2_TX_NOM); // 128us
-                    Delay1us(TV2_TX_NOM / 2);
+                    driver_->sendPulseVpw(tv2_tx_nom); // 128us
+                    Delay1us(tv2_tx_nom / 2); // huh?
                 }
                 else {
-                    driver_->sendPulseVpw(TV1_TX_NOM); // 64us
-                    Delay1us(TV1_TX_NOM / 2);
+                    driver_->sendPulseVpw(tv1_tx_nom); // 64us
+                    Delay1us(tv1_tx_nom / 2); // huh?
                 }
                 if (driver_->getBit()) {
                     driver_->stop();
@@ -79,10 +80,10 @@ int VpwAdapter::sendToEcu(const Ecumsg* msg)
             }
             else {  // Active, set bus to 1
                 if (ch & 0x80) {
-                    driver_->sendPulseVpw(TV1_TX_NOM); // 64us
+                    driver_->sendPulseVpw(tv1_tx_nom ); // 64us
                 }
                 else {
-                    driver_->sendPulseVpw(TV2_TX_NOM); // 128us
+                    driver_->sendPulseVpw(tv2_tx_nom ); // 128us
                 }
             }
             ch <<= 1;
@@ -101,11 +102,11 @@ int VpwAdapter::sendToEcu(const Ecumsg* msg)
  */
 bool VpwAdapter::waitForSof()
 {
-    for (;;) {
-        uint32_t val = driver_->wait4Sof(TV3_RX_MAX, timer_);
+           for (;;) {
+        uint32_t val = driver_->wait4Sof(tv3_rx_max, timer_);
         if (val == 0xFFFFFFFF) // P2 timer expired
             return false;
-        if (val >= TV3_RX_MIN)
+        if (val >= tv3_rx_min)
             break;
     }
     return true;
@@ -117,7 +118,7 @@ bool VpwAdapter::waitForSof()
  */
 int VpwAdapter::receiveFromEcu(Ecumsg* msg, int maxLen)
 {
-    uint8_t* ptr = msg->data();
+       uint8_t* ptr = msg->data();
     msg->length(0); // Reset the buffer byte length
     
     // Wait SOF
@@ -141,13 +142,13 @@ int VpwAdapter::receiveFromEcu(Ecumsg* msg, int maxLen)
                 goto extr; // EOD Max expired, terminate receive on timeout
             }
             state = !state;
-            if (pulse < TV1_RX_MIN) {
+            if (pulse < tv1_rx_min) {
                 goto exte; // pulse is too short
             }
-            if (pulse > TV2_RX_MAX) {
+            if (pulse > tv2_rx_max) {
                 goto exte; // pulse is too long
             }
-            ch |= (pulse > VPW_RX_MID) ? 1 : 0;
+            ch |= (pulse > vpw_rx_mid) ? 1 : 0;
         }
         *(ptr++) = ch ^ 0x55;
     }
@@ -180,19 +181,20 @@ int VpwAdapter::onRequest(const uint8_t* data, int len)
  * Will try to send PID0 to query the VPW protocol
  * @param[in] sendReply Send reply flag
  * @return PROT_J1850_VPW if ECU is supporting PWM protocol, 0 otherwise
+ * Modified to not auto test. Just trust that this is the protocol we want. Mods break ATSP
  */
 int VpwAdapter::onConnectEcu(bool sendReply)
 {
-    uint8_t testSeq[] = { 0x01, 0x00 };
-
+    //uint8_t testSeq[] = { 0x01, 0x00 };
     open();
-    int reply = requestImpl(testSeq, sizeof(testSeq), sendReply);
-
-    connected_ = (reply == REPLY_NONE);
-    if (!connected_) {
-        close(); // Close only if not succeeded
-    }
-    return connected_ ? PROT_J1850_VPW : 0;
+    //int reply = requestImpl(testSeq, sizeof(testSeq), sendReply);
+    //
+    //connected_ = (reply == REPLY_NONE);
+    //if (!connected_) {
+    //    close(); // Close only if not succeeded
+    //}
+    //return connected_ ? PROT_J1850_VPW : 0;
+    return PROT_J1850_VPW;
 }
 
 int VpwAdapter::requestImpl(const uint8_t* data, int len, bool sendReply)
@@ -274,7 +276,13 @@ int VpwAdapter::getP2MaxTimeout() const
 void VpwAdapter::getDescription()
 {
     bool useAutoSP = config_->getBoolProperty(PAR_USE_AUTO_SP);
-    AdptSendReply(useAutoSP ? "AUTO, SAE J1850 VPW" : "SAE J1850 VPW");
+    bool vpw4x = config_->getBoolProperty(PAR_VPW_4X);
+    if (vpw4x) {
+       AdptSendReply(useAutoSP ? "AUTO, SAE J1850 VPW 4X" : "SAE J1850 VPW 4X");
+    }
+    else {
+       AdptSendReply(useAutoSP ? "AUTO, SAE J1850 VPW" : "SAE J1850 VPW");
+    }
 }
 
 /**
@@ -313,6 +321,50 @@ ext:
     close();
 }
 
+void VpwAdapter::setSpeed()
+{
+       if (!config_->getBoolProperty(PAR_VPW_4X)) {
+               tv1_tx_nom = TV1_TX_NOM;
+               tv2_tx_nom = TV2_TX_NOM;
+               tv3_tx_nom = TV3_TX_NOM;
+               tv4_tx_min = TV4_TX_MIN;
+               tv6_tx_min = TV6_TX_MIN;
+               tv5_tx_nom = TV5_TX_NOM;
+               tv5_tx_max = TV5_TX_MAX;
+               tv6_tx_nom = TV6_TX_NOM;
+               tv1_tx_adj = TV1_TX_ADJ;
+               tv2_tx_adj = TV2_TX_ADJ;
+               tv1_rx_min = TV1_RX_MIN;
+               tv2_rx_max = TV2_RX_MAX;
+               tv3_rx_min = TV3_RX_MIN;
+               tv3_rx_max = TV3_RX_MAX;
+               tv5_rx_min = TV5_RX_MIN;
+               tv6_rx_min = TV6_RX_MIN;
+               vpw_rx_mid = VPW_RX_MID;
+       }
+       else {
+               tv1_tx_nom = TV1_4X_TX_NOM;
+               tv2_tx_nom = TV2_4X_TX_NOM;
+               tv3_tx_nom = TV3_4X_TX_NOM;
+               tv4_tx_min = TV4_4X_TX_MIN;
+               tv6_tx_min = TV6_4X_TX_MIN;
+               tv5_tx_nom = TV5_4X_TX_NOM;
+               tv5_tx_max = TV5_4X_TX_MAX;
+               tv6_tx_nom = TV6_4X_TX_NOM;
+               tv1_tx_adj = TV1_4X_TX_ADJ;
+               tv2_tx_adj = TV2_4X_TX_ADJ;
+               tv1_rx_min = TV1_4X_RX_MIN;
+               tv2_rx_max = TV2_4X_RX_MAX;
+               tv3_rx_min = TV3_4X_RX_MIN;
+               tv3_rx_max = TV3_4X_RX_MAX;
+               tv5_rx_min = TV5_4X_RX_MIN;
+               tv6_rx_min = TV6_4X_RX_MIN;
+               vpw_rx_mid = VPW_4X_RX_MID;
+       }
+}
+
+
+
 //-------------------------------------------------------------------------
 //
 //   +---------+  +----+    +--+  +-----+  +----+    +--+-----+
diff --git a/src/adapter/obd/vpw.h b/src/adapter/obd/vpw.h
old mode 100644
new mode 100755
index 5c0fc09..df1b582
--- a/src/adapter/obd/vpw.h
+++ b/src/adapter/obd/vpw.h
@@ -33,6 +33,27 @@ private:
     int requestImpl(const uint8_t* data, int len, bool sendReply);
     Timer*     timer_;
     PwmDriver* driver_;
+
+    void setSpeed();
+
+    uint32_t tv1_tx_nom;
+    uint32_t tv2_tx_nom;
+    uint32_t tv3_tx_nom;
+    uint32_t tv4_tx_min;
+    uint32_t tv6_tx_min;
+    uint32_t tv5_tx_nom;
+    uint32_t tv5_tx_max;
+    uint32_t tv6_tx_nom;
+    uint32_t tv1_tx_adj;
+    uint32_t tv2_tx_adj;
+    uint32_t tv1_rx_min;
+    uint32_t tv2_rx_max;
+    uint32_t tv3_rx_min;
+    uint32_t tv3_rx_max;
+    uint32_t tv5_rx_min;
+    uint32_t tv6_rx_min;
+    uint32_t vpw_rx_mid;
+
 };
 
 #endif //__VPW_H__
Have you read the FAQ? For lots of information and links to significant threads see here: viewtopic.php?f=7&t=1396

Previous

Return to Hardware

Who is online

Users browsing this forum: No registered users and 1 guest