Added board detection to solve setup of GPIO of modded vs unmodded V2.0 PCBs and inversion of inputs on V1 PCBS.

This commit is contained in:
rljonesau 2019-04-13 09:18:07 +10:00
parent a2fe5e969d
commit 83784a77d2
9 changed files with 285 additions and 22 deletions

View file

@ -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 <DallasTemperature.h>
@ -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)

View file

@ -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;
}

View file

@ -0,0 +1,120 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
//
// 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 <Preferences.h>
#include <driver/adc.h>
#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;
}

View file

@ -0,0 +1,24 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
int BoardDetect();

View file

@ -21,6 +21,8 @@
#include "GPIO.h"
#include "../Protocol/helpers.h"
#include <driver/adc.h>
#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;
}
}
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;
}

View file

@ -23,6 +23,7 @@
#define __BTCGPIO_H__
#include <stdint.h>
#include <driver/adc.h>
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__

View file

@ -22,6 +22,7 @@
#include <Arduino.h>
#include "NVStorage.h"
#include "DebugPort.h"
#include <driver/adc.h>
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

View file

@ -151,7 +151,7 @@ public:
unsigned char getCyclicMode();
GPIOinModes getGPIOinMode();
GPIOoutModes getGPIOoutMode();
unsigned char getGPIOalgMode();
GPIOalgModes getGPIOalgMode();
void setPmin(float);
void setPmax(float);

View file

@ -20,6 +20,7 @@
*/
#include <stdint.h>
#include <driver/adc.h>
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!