From 83784a77d23cc02b94d93fdef5c531fa21b78a92 Mon Sep 17 00:00:00 2001 From: rljonesau Date: Sat, 13 Apr 2019 09:18:07 +1000 Subject: [PATCH] Added board detection to solve setup of GPIO of modded vs unmodded V2.0 PCBs and inversion of inputs on V1 PCBS. --- Arduino/BTCDieselHeater/BTCDieselHeater.ino | 55 +++++++- .../BTCDieselHeater/src/OLED/GPIOScreen.cpp | 5 + .../src/Utility/BoardDetect.cpp | 120 ++++++++++++++++++ .../BTCDieselHeater/src/Utility/BoardDetect.h | 24 ++++ Arduino/BTCDieselHeater/src/Utility/GPIO.cpp | 52 +++++++- Arduino/BTCDieselHeater/src/Utility/GPIO.h | 29 ++++- .../BTCDieselHeater/src/Utility/NVStorage.cpp | 10 +- .../BTCDieselHeater/src/Utility/NVStorage.h | 2 +- Arduino/BTCDieselHeater/src/cfg/pins.h | 10 +- 9 files changed, 285 insertions(+), 22 deletions(-) create mode 100644 Arduino/BTCDieselHeater/src/Utility/BoardDetect.cpp create mode 100644 Arduino/BTCDieselHeater/src/Utility/BoardDetect.h diff --git a/Arduino/BTCDieselHeater/BTCDieselHeater.ino b/Arduino/BTCDieselHeater/BTCDieselHeater.ino index b16e447..cafcbe2 100644 --- a/Arduino/BTCDieselHeater/BTCDieselHeater.ino +++ b/Arduino/BTCDieselHeater/BTCDieselHeater.ino @@ -102,6 +102,7 @@ #include "src/Utility/UtilClasses.h" #include "src/Utility/BTC_JSON.h" #include "src/Utility/GPIO.h" +#include "src/Utility/BoardDetect.h" #include "src/OLED/ScreenManager.h" #include "src/OLED/keypad.h" #include @@ -144,6 +145,7 @@ long lastTemperatureTime; // used to moderate DS18B20 access float fFilteredTemperature = -100; // -100: force direct update uopn first pass const float fAlpha = 0.95; // exponential mean alpha int DS18B20holdoff = 2; +int BoardRevision = 0; unsigned long lastAnimationTime; // used to sequence updates to LCD for animation @@ -159,6 +161,7 @@ CScreenManager ScreenManager; TelnetSpy DebugPort; CGPIOin GPIOin; CGPIOout GPIOout; +CGPIOalg GPIOalg; sRxLine PCline; long lastRxTime; // used to observe inter character delays @@ -309,6 +312,9 @@ void setup() { sprintf(msg, " Temperature for device#1 (idx 0) is: %.1f", TempSensor.getTempCByIndex(0)); DebugPort.println(msg); + BoardRevision = BoardDetect(); + DebugPort.print("Board revision: V"); DebugPort.println(float(BoardRevision) * 0.1, 1); + #if USE_SPIFFS == 1 // Initialize SPIFFS if(!SPIFFS.begin(true)){ @@ -387,7 +393,7 @@ void setup() { #endif // USE_WIFI - pinMode(ListenOnlyPin, INPUT_PULLUP); // pin to enable passive mode +// pinMode(ListenOnlyPin, INPUT_PULLUP); // pin to enable passive mode pinMode(LED_Pin, OUTPUT); // On board LED indicator digitalWrite(LED_Pin, LOW); @@ -448,6 +454,7 @@ void loop() GPIOin.manage(); GPIOout.manage(); + GPIOalg.manage(); // manage changes in Bluetooth connection status if(Bluetooth.isConnected()) { @@ -670,15 +677,15 @@ void loop() case CommStates::HeaterReport1: if(CommState.delayExpired()) { - if(digitalRead(ListenOnlyPin)) { // pin open, pulled high (STANDARD OPERATION) +/* if(digitalRead(ListenOnlyPin)) { // pin open, pulled high (STANDARD OPERATION)*/ bool isBTCmaster = false; TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes TxManage.Start(timenow); CommState.set(CommStates::BTC_Tx); - } +/* } else { // pin shorted to ground CommState.set(CommStates::TemperatureRead); // "Listen Only" input is held low, don't send our Tx - } + }*/ } break; @@ -1232,8 +1239,44 @@ bool isCyclicActive() void setupGPIO() { - GPIOin.begin(GPIOin1_pin, GPIOin2_pin, NVstore.getGPIOinMode()); - GPIOout.begin(GPIOout1_pin, GPIOout2_pin, NVstore.getGPIOoutMode()); + if(BoardRevision) { + // some special considerations for GPIO inputs, depending upon PCB hardware + // V1.0 PCBs only expose bare inputs, which are pulled high. Active state into ESP32 is LOW + // V2.0+ PCBs use an input transistor buffer. Active state into ESP32 is HIGH (inverted) + int activePinState = BoardRevision == 10 ? LOW : HIGH; + int Input1 = BoardRevision == 20 ? GPIOin1_pinV20 : GPIOin1_pinV21V10; + GPIOin.begin(Input1, GPIOin2_pin, NVstore.getGPIOinMode(), activePinState); + + // GPIO out is always active high from ESP32 + // V1.0 PCBs only expose the bare pins + // V2.0+ PCBs provide an open collector output that conducts when active + GPIOout.begin(GPIOout1_pin, GPIOout2_pin, NVstore.getGPIOoutMode()); + + // ### MAJOR ISSUE WITH ADC INPUTS ### + // + // V2.0 PCBs that have not been modified connect the analogue input to GPIO26. + // This is ADC2 channel (#9). + // Unfortunately it was subsequently discovered that any ADC2 input cannot be + // used if Wifi is enabled. + // THIS ISSUE IS NOT RESOLBVABLE IN SOFTWARE. + // *** It is not possible to use ANY of the 10 ADC2 channels if Wifi is enabled :-( *** + // + // Fix is to cut traces to GPIO33 & GPIO26 and swap the connections. + // This directs GPIO input1 into GPIO26 and the analogue input into GPIO33 (ADC1_CHANNEL_5) + // This will be properly fixed in V2.1 PCBs + // + // As V1.0 PCBS expose the bare pins, the correct GPIO33 input can be readily chosen. + GPIOalgModes algMode = NVstore.getGPIOalgMode(); + if(BoardRevision == 20) + algMode = GPIOalgNone; // force off analogue support in V2.0 PCBs + GPIOalg.begin(GPIOalg_pin, algMode); + } + else { + // unknown board - deny all GPIO operation (unlikely) + GPIOin.begin(0, 0, GPIOinNone, LOW); + GPIOout.begin(0, 0, GPIOoutNone); + GPIOalg.begin(ADC1_CHANNEL_5, GPIOalgNone); + } } void setGPIO(int channel, bool state) diff --git a/Arduino/BTCDieselHeater/src/OLED/GPIOScreen.cpp b/Arduino/BTCDieselHeater/src/OLED/GPIOScreen.cpp index 09c23c5..2351f70 100644 --- a/Arduino/BTCDieselHeater/src/OLED/GPIOScreen.cpp +++ b/Arduino/BTCDieselHeater/src/OLED/GPIOScreen.cpp @@ -30,6 +30,7 @@ extern CGPIOout GPIOout; extern CGPIOin GPIOin; +extern CGPIOalg GPIOalg; /////////////////////////////////////////////////////////////////////////// // @@ -320,6 +321,7 @@ bool CGPIOInfoScreen::show() { CScreenHeader::show(); + char msg[16]; _display.writeFillRect(49, 18, 30, 12, WHITE); _printInverted(64, 20, "GPIO", true, eCentreJustify); @@ -338,6 +340,9 @@ CGPIOInfoScreen::show() _display.drawBitmap(86, 29, GPIOout.getState(0) ? BulbOnIcon : BulbOffIcon, BulbOnIconWidth, BulbOnIconHeight, WHITE); _display.drawBitmap(113, 29, GPIOout.getState(1) ? BulbOnIcon : BulbOffIcon, BulbOnIconWidth, BulbOnIconHeight, WHITE); + sprintf(msg, "%d", GPIOalg.getValue()); + _printMenuText(58, Line1, msg); + _printMenuText(_display.xCentre(), 53, " \021 \030Edit \020 ", true, eCentreJustify); return true; } diff --git a/Arduino/BTCDieselHeater/src/Utility/BoardDetect.cpp b/Arduino/BTCDieselHeater/src/Utility/BoardDetect.cpp new file mode 100644 index 0000000..2a4c44c --- /dev/null +++ b/Arduino/BTCDieselHeater/src/Utility/BoardDetect.cpp @@ -0,0 +1,120 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// +// We need to identify the PCB the firmware is running upon for 2 reasons related to GPIO functions +// +// 1: Digital Inputs +// To the outside world, the digital inputs are always treated as contact closures to ground. +// V1.0 PCBs expose the bare ESP inputs for GPIO, they are normally pulled HIGH. +// V2.0+ PCBs use an input conditioning transistor that inverts the sense state. +// Inactive state for V1.0 is HIGH +// Inactive state for V2.0+ is LOW +// +// 2: Analogue input +// Unfortunately the pin originally chosen for the analogue input on the V2.0 PCB goes to +// an ADC2 channel of the ESP32. +// It turns out NONE of the 10 ADC2 channels can be used if Wifi is enabled! +// The remedy on V2.0 PCBs is to cut the traces leading from Digital input 1 and the Analogue input. +// The signals are then tranposed. +// This then presents Digital Input #1 to GPIO26, and analogue to GPIO33. +// As GPIO33 uses an ADC1 channel no issue is present reading analogue values with wifi enabled. +// +// Board Detection +// Fortunately due to the use of the digital input transistors on V2.0+ PCBs, a logical +// determination of the board configuration can be made. +// By setting the pins as digital inputs with pull ups enabled, the logic level presented +// can be read and thus the input signal paths can be determined. +// Due to the input conditioning transistors, V2.0 PCBs will hold the inputs to the ESP32 +// LOW when inactive, V1.0 PCBs will pull HIGH. +// Likewise, the analogue input is left to float, so it will always be pulled HIGH. +// NOTE: a 100nF capacitor exists on the analogue input so a delay is required to ensure +// a reliable read. +// +// Input state truth table +// GPIO26 GPIO33 +// ------ ------ +// V1.0 HIGH HIGH +// unmodified V2.0 HIGH LOW +// modified V2.0 LOW HIGH +// V2.1 LOW HIGH +// +// +// **************************************************************************************** +// This test only needs to be performed upon the very first firmware execution. +// Once the board has been identified, the result is saved to non volatile memory +// If a valid value is detected, the test is bypassed. +// This avoids future issues should the GPIO inputs be legitimately connected to +// extension hardware that may distort the test results when the system is repowered. +// **************************************************************************************** +// + +#include "BoardDetect.h" +#include +#include +#include "DebugPort.h" + +int BoardDetect() +{ + Preferences preferences; + + preferences.begin("System Info", false); + + uint8_t revision = 0; + uint8_t val = preferences.getUChar("Board Revision", revision); + if(val != 0) { +// return val; + } + + DebugPort.println("Virgin system - attempting to detect revision"); + pinMode(33, INPUT_PULLUP); + pinMode(26, INPUT_PULLUP); + // there is a 100nF capacitor across the analogue input, allow that to charge before testing + delay(100); + int pin33 = digitalRead(33); + int pin26 = digitalRead(26); + + if(pin33 == HIGH && pin26 == HIGH) { + revision = 10; + DebugPort.println("Board detect: digital input test suggests V1.x PCB"); + } + else if(pin33 == LOW && pin26 == HIGH) { + revision = 20; + DebugPort.println("Board detect: digital input test suggests V2.0 PCB"); + } + else if(pin33 == HIGH && pin26 == LOW) { + revision = 21; + DebugPort.println("Board detect: digital input test suggests V2.1 PCB"); + } + else { + DebugPort.println("Board detect: digital input test failed to detect a sane combination!!!"); + } + + pinMode(33, INPUT); // revert to normal inputs (remove pull ups) + pinMode(26, INPUT); + + if(revision) { + preferences.putUChar("Board Revision", revision); + } + + DebugPort.print("Board detect result: V"); DebugPort.println(float(revision)*0.1f, 1); + return revision; +} \ No newline at end of file diff --git a/Arduino/BTCDieselHeater/src/Utility/BoardDetect.h b/Arduino/BTCDieselHeater/src/Utility/BoardDetect.h new file mode 100644 index 0000000..e80a207 --- /dev/null +++ b/Arduino/BTCDieselHeater/src/Utility/BoardDetect.h @@ -0,0 +1,24 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +int BoardDetect(); diff --git a/Arduino/BTCDieselHeater/src/Utility/GPIO.cpp b/Arduino/BTCDieselHeater/src/Utility/GPIO.cpp index 2829e73..23e9244 100644 --- a/Arduino/BTCDieselHeater/src/Utility/GPIO.cpp +++ b/Arduino/BTCDieselHeater/src/Utility/GPIO.cpp @@ -21,6 +21,8 @@ #include "GPIO.h" #include "../Protocol/helpers.h" +#include +#include "DebugPort.h" const int BREATHINTERVAL = 45; const int FADEAMOUNT = 3; @@ -32,6 +34,7 @@ CGPIOin::CGPIOin() _Mode = GPIOinNone; _pins[0] = 0; _pins[1] = 0; + _pinActive = LOW; _prevPins = 0; _lastDebounceTime = 0; _lastKey = 0; @@ -39,10 +42,11 @@ CGPIOin::CGPIOin() } void -CGPIOin::begin(int pin1, int pin2, GPIOinModes mode) +CGPIOin::begin(int pin1, int pin2, GPIOinModes mode, int activeState) { _pins[0] = pin1; _pins[1] = pin2; + _pinActive = activeState; pinMode(pin1, INPUT_PULLUP); // GPIO input pin #1 pinMode(pin2, INPUT_PULLUP); // GPIO input pin #2 @@ -137,8 +141,8 @@ uint8_t CGPIOin::_scanInputs() { uint8_t newPins = 0; - if(_pins[0] && digitalRead(_pins[0]) == HIGH) newPins |= 0x01; - if(_pins[1] && digitalRead(_pins[1]) == HIGH) newPins |= 0x02; + if(_pins[0] && (digitalRead(_pins[0]) == _pinActive)) newPins |= 0x01; + if(_pins[1] && (digitalRead(_pins[1]) == _pinActive)) newPins |= 0x02; if(newPins != _prevPins) { _lastDebounceTime = millis(); @@ -155,6 +159,8 @@ CGPIOin::_scanInputs() return _debouncedPins; } + + CGPIOout::CGPIOout() { _Mode = GPIOoutNone; @@ -352,4 +358,42 @@ CGPIOout::getState(int channel) { int mask = 0x01 << (channel & 0x01); return (_userState & mask) != 0; -} \ No newline at end of file +} + + +CGPIOalg::CGPIOalg() +{ + _expMean = 0; +} + +void +CGPIOalg::begin(adc1_channel_t pin, GPIOalgModes mode) +{ + _pin = pin; + _Mode = mode; + + if(_Mode != GPIOalgNone) { + adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_5); + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_11db); + } +} + +void CGPIOalg::manage() +{ + if(_Mode != GPIOalgNone) { + int read_raw; + char msg[32]; + read_raw = adc1_get_raw( ADC1_CHANNEL_5); +// read_raw = analogRead(33); + sprintf(msg, "ADC: %d", read_raw ); + _expMean = read_raw; +// DebugPort.println(msg); + } +} + +int +CGPIOalg::getValue() +{ + return _expMean; +} diff --git a/Arduino/BTCDieselHeater/src/Utility/GPIO.h b/Arduino/BTCDieselHeater/src/Utility/GPIO.h index 5a8a655..e9cdfdc 100644 --- a/Arduino/BTCDieselHeater/src/Utility/GPIO.h +++ b/Arduino/BTCDieselHeater/src/Utility/GPIO.h @@ -23,6 +23,7 @@ #define __BTCGPIO_H__ #include +#include enum GPIOinModes { GPIOinNone, @@ -37,23 +38,29 @@ enum GPIOoutModes { GPIOoutUser }; +enum GPIOalgModes { + GPIOalgNone, // Unmodified V2.0 PCBs must use this - ADC2 / Wifi unresolvable conflict + GPIOalgHeatDemand, +}; + class CGPIOin { GPIOinModes _Mode; - void _doOn1Off2(); - void _doOnHold1(); - void _doOn1Off1(); + int _pinActive; int _pins[2]; - uint8_t _scanInputs(); uint8_t _prevPins; uint8_t _debouncedPins; uint8_t _lastKey; unsigned long _lastDebounceTime; unsigned long _debounceDelay; + uint8_t _scanInputs(); + void _doOn1Off2(); + void _doOnHold1(); + void _doOn1Off1(); public: CGPIOin(); void setMode(GPIOinModes mode) { _Mode = mode; }; - void begin(int pin1, int pin2, GPIOinModes mode); + void begin(int pin1, int pin2, GPIOinModes mode, int activeState); void manage(); uint8_t getState(int channel); }; @@ -80,4 +87,16 @@ public: bool getState(int channel); }; +class CGPIOalg { + GPIOalgModes _Mode; + float _expMean; + adc1_channel_t _pin; +public: + CGPIOalg(); + void begin(adc1_channel_t pin, GPIOalgModes mode); + void manage(); + int getValue(); +}; + + #endif // __BTCGPIO_H__ diff --git a/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp b/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp index c4977c8..0abca8e 100644 --- a/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp +++ b/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp @@ -22,6 +22,7 @@ #include #include "NVStorage.h" #include "DebugPort.h" +#include bool u8inBounds(uint8_t test, uint8_t minLim, uint8_t maxLim); bool s8inBounds(int8_t test, int8_t minLim, int8_t maxLim); @@ -321,10 +322,15 @@ CHeaterStorage::setGPIOoutMode(unsigned char val) _calValues.Options.GPIOoutMode = val; } -unsigned char +GPIOalgModes CHeaterStorage::getGPIOalgMode() { - return _calValues.Options.GPIOalgMode; + GPIOalgModes algMode = GPIOalgNone; + switch (_calValues.Options.GPIOalgMode) { + case 0: algMode = GPIOalgNone; break; + case 1: algMode = GPIOalgHeatDemand; break; + } + return algMode; } void diff --git a/Arduino/BTCDieselHeater/src/Utility/NVStorage.h b/Arduino/BTCDieselHeater/src/Utility/NVStorage.h index f97ff5f..564436c 100644 --- a/Arduino/BTCDieselHeater/src/Utility/NVStorage.h +++ b/Arduino/BTCDieselHeater/src/Utility/NVStorage.h @@ -151,7 +151,7 @@ public: unsigned char getCyclicMode(); GPIOinModes getGPIOinMode(); GPIOoutModes getGPIOoutMode(); - unsigned char getGPIOalgMode(); + GPIOalgModes getGPIOalgMode(); void setPmin(float); void setPmax(float); diff --git a/Arduino/BTCDieselHeater/src/cfg/pins.h b/Arduino/BTCDieselHeater/src/cfg/pins.h index f28a916..4d3d6b3 100644 --- a/Arduino/BTCDieselHeater/src/cfg/pins.h +++ b/Arduino/BTCDieselHeater/src/cfg/pins.h @@ -20,6 +20,7 @@ */ #include +#include const uint8_t UART_Tx = 1; const uint8_t LED_Pin = 2; @@ -38,17 +39,18 @@ const uint8_t OLED_SDA_pin = 21; // I2C std pins const uint8_t OLED_SCL_pin = 22; // " const uint8_t HC05_SensePin = 23; const uint8_t GPIOin2_pin = 25; -const uint8_t AlgInput_pin = 26; +const uint8_t GPIOin1_pinV21V10 = 26; +const adc2_channel_t GPIOalg_pinINVALID = ADC2_CHANNEL_9; // GPIO 26 - Cannot use ADC2 with WiFi enabled!!! const uint8_t GPIOout1_pin = 27; const uint8_t keyUp_pin = 32; -const uint8_t GPIOin1_pin = 33; +const uint8_t GPIOin1_pinV20 = 33; +const adc1_channel_t GPIOalg_pin = ADC1_CHANNEL_5; // GPIO 33 - OK with Wifi, ADC1 channel const uint8_t keyDown_pin = 34; // input only, no chip pullup const uint8_t keyCentre_pin = 35; // input only, no chip pullup const uint8_t keyRight_pin = 36; // input only, no chip pullup const uint8_t keyLeft_pin = 39; // input only, no chip pullup -const uint8_t ListenOnlyPin = 33; -//const uint8_t WiFi_TriggerPin = 25; +//const uint8_t ListenOnlyPin = 33; const uint8_t WiFi_TriggerPin = 0; // BOOT switch!