2019-04-06 11:06:50 +00:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-07-02 08:27:20 +00:00
|
|
|
#include "BTC_GPIO.h"
|
|
|
|
#include "helpers.h"
|
2019-04-12 23:18:07 +00:00
|
|
|
#include <driver/adc.h>
|
|
|
|
#include "DebugPort.h"
|
2019-06-15 23:09:29 +00:00
|
|
|
#include "../Protocol/Protocol.h"
|
2019-08-25 20:37:41 +00:00
|
|
|
#include "../Utility/NVStorage.h"
|
2019-10-31 22:24:58 +00:00
|
|
|
#include "../RTC/RTCStore.h"
|
2019-04-06 11:06:50 +00:00
|
|
|
|
2019-04-09 23:28:46 +00:00
|
|
|
const int BREATHINTERVAL = 45;
|
2019-04-08 23:12:42 +00:00
|
|
|
const int FADEAMOUNT = 3;
|
|
|
|
const int FLASHPERIOD = 2000;
|
|
|
|
const int ONFLASHINTERVAL = 50;
|
|
|
|
|
2019-08-07 12:04:20 +00:00
|
|
|
const char* GPIOin1Names[] = {
|
2019-06-04 20:15:12 +00:00
|
|
|
"Disabled",
|
2019-08-07 12:04:20 +00:00
|
|
|
"Mom On",
|
|
|
|
"Hold On",
|
2019-08-27 10:35:21 +00:00
|
|
|
"Mom On/Off",
|
|
|
|
"Mom Off"
|
2019-06-04 20:15:12 +00:00
|
|
|
};
|
2019-08-07 12:04:20 +00:00
|
|
|
const char* GPIOin2Names[] = {
|
|
|
|
"Disabled",
|
|
|
|
"Mom Off",
|
2020-03-31 08:46:25 +00:00
|
|
|
"Ext Thermo",
|
|
|
|
"Fuel Reset"
|
2019-08-07 12:04:20 +00:00
|
|
|
};
|
|
|
|
|
2019-06-04 20:15:12 +00:00
|
|
|
|
2019-08-07 12:04:20 +00:00
|
|
|
const char* GPIOout1Names[] = {
|
2019-06-04 20:15:12 +00:00
|
|
|
"Disabled",
|
|
|
|
"Status",
|
2019-10-30 10:48:25 +00:00
|
|
|
"User",
|
2020-03-23 05:54:15 +00:00
|
|
|
"Thresh",
|
|
|
|
"HeaterOn"
|
2019-06-04 20:15:12 +00:00
|
|
|
};
|
2019-08-07 12:04:20 +00:00
|
|
|
const char* GPIOout2Names[] = {
|
|
|
|
"Disabled",
|
2019-10-30 10:48:25 +00:00
|
|
|
"User",
|
2020-03-23 05:54:15 +00:00
|
|
|
"Thresh",
|
|
|
|
"HeaterOn"
|
2019-08-07 12:04:20 +00:00
|
|
|
};
|
2019-06-04 20:15:12 +00:00
|
|
|
|
2019-08-07 08:59:30 +00:00
|
|
|
const char* GPIOalgNames[] = {
|
2019-06-04 20:15:12 +00:00
|
|
|
"Disabled",
|
2019-09-08 00:14:36 +00:00
|
|
|
"Enabled",
|
2019-06-04 20:15:12 +00:00
|
|
|
"HeatDemand"
|
|
|
|
};
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOin1::CGPIOin1()
|
|
|
|
{
|
|
|
|
_Mode = Disabled;
|
2019-08-25 20:37:41 +00:00
|
|
|
_prevActive = false;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin1::begin(CGPIOin1::Modes mode)
|
|
|
|
{
|
|
|
|
setMode(mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
CGPIOin1::Modes CGPIOin1::getMode() const
|
|
|
|
{
|
|
|
|
return _Mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin1::manage(bool active)
|
|
|
|
{
|
2019-08-25 20:37:41 +00:00
|
|
|
if(_prevActive ^ active) {
|
|
|
|
switch (_Mode) {
|
|
|
|
case Disabled: break;
|
|
|
|
case Start: _doStart(active); break;
|
|
|
|
case Run: _doRun(active); break;
|
|
|
|
case StartStop: _doStartStop(active); break;
|
2019-08-27 10:35:21 +00:00
|
|
|
case Stop: _doStop(active); break;
|
2019-08-25 20:37:41 +00:00
|
|
|
}
|
|
|
|
_prevActive = active;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 08:46:25 +00:00
|
|
|
|
|
|
|
// mode where you can start the heater with a short press
|
|
|
|
// stop the heater with a long press
|
2019-08-08 20:13:02 +00:00
|
|
|
void
|
2019-08-10 05:57:46 +00:00
|
|
|
CGPIOin1::_doStart(bool active)
|
2019-08-08 20:13:02 +00:00
|
|
|
{
|
2020-03-31 08:46:25 +00:00
|
|
|
if(active && !_prevActive) {
|
|
|
|
_holdoff = millis();
|
|
|
|
}
|
|
|
|
if(!active && _prevActive) {
|
|
|
|
unsigned long tDelta = millis() - _holdoff;
|
|
|
|
if(tDelta > 50) {
|
|
|
|
if(tDelta < 1500) // longer or shorter than 1.5 seconds?
|
|
|
|
requestOn(); // short press is start
|
|
|
|
else
|
|
|
|
requestOff(); // long press is stop
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mode where heater runs if input 1 is shorted
|
|
|
|
// stops when open
|
|
|
|
void
|
2019-08-10 05:57:46 +00:00
|
|
|
CGPIOin1::_doRun(bool active)
|
2019-08-08 20:13:02 +00:00
|
|
|
{
|
|
|
|
if(active) {
|
|
|
|
requestOn();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
requestOff();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mode where heater runs if input 1 is shorted
|
|
|
|
// stops when open
|
|
|
|
void
|
2019-08-10 05:57:46 +00:00
|
|
|
CGPIOin1::_doStartStop(bool active)
|
2019-08-08 20:13:02 +00:00
|
|
|
{
|
|
|
|
if(active) {
|
2019-10-31 22:24:58 +00:00
|
|
|
if(getHeaterInfo().getRunStateEx() && !RTC_Store.getFrostOn())
|
2019-08-08 20:13:02 +00:00
|
|
|
requestOff();
|
|
|
|
else
|
|
|
|
requestOn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 10:35:21 +00:00
|
|
|
void
|
|
|
|
CGPIOin1::_doStop(bool active)
|
|
|
|
{
|
|
|
|
if(active) {
|
|
|
|
requestOff();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOin2::CGPIOin2()
|
|
|
|
{
|
|
|
|
_Mode = Disabled;
|
2019-08-25 20:37:41 +00:00
|
|
|
_prevActive = false;
|
2020-03-31 08:46:25 +00:00
|
|
|
_holdoff = 0;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin2::begin(CGPIOin2::Modes mode)
|
|
|
|
{
|
|
|
|
setMode(mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
CGPIOin2::Modes CGPIOin2::getMode() const
|
|
|
|
{
|
|
|
|
return _Mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin2::manage(bool active)
|
|
|
|
{
|
|
|
|
switch (_Mode) {
|
2019-08-10 05:57:46 +00:00
|
|
|
case Disabled: break;
|
|
|
|
case Stop: _doStop(active); break;
|
|
|
|
case Thermostat: _doThermostat(active); break;
|
2020-03-31 08:46:25 +00:00
|
|
|
case FuelReset: _doFuelReset(active); break;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
2020-03-31 08:46:25 +00:00
|
|
|
_prevActive = active;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-10 05:57:46 +00:00
|
|
|
CGPIOin2::_doStop(bool active)
|
2019-08-08 20:13:02 +00:00
|
|
|
{
|
2019-08-25 20:37:41 +00:00
|
|
|
if(_prevActive ^ active) {
|
|
|
|
if(active) {
|
|
|
|
requestOff();
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mode where heater runs if input 1 is shorted
|
|
|
|
// stops when open
|
|
|
|
void
|
|
|
|
CGPIOin2::_doThermostat(bool active)
|
|
|
|
{
|
2019-08-25 20:37:41 +00:00
|
|
|
// only if actually using thermostat input, and a timeout is defined do we perform heater start / stop functions
|
|
|
|
if((NVstore.getUserSettings().ThermostatMethod == 3) && NVstore.getUserSettings().ExtThermoTimeout) {
|
|
|
|
if(active && !_prevActive) { // initial switch on of thermostat input
|
|
|
|
DebugPort.println("starting heater due to thermostat contact closure");
|
|
|
|
requestOn(); // request heater to start upon closure of thermostat input
|
|
|
|
}
|
|
|
|
if(!active && _prevActive) { // initial switch off of thermostat input
|
2020-03-31 08:46:25 +00:00
|
|
|
_holdoff = (millis() + NVstore.getUserSettings().ExtThermoTimeout) | 1;
|
2019-08-25 20:37:41 +00:00
|
|
|
DebugPort.printf("thermostat contact opened - will stop in %ldms\r\n", NVstore.getUserSettings().ExtThermoTimeout);
|
|
|
|
}
|
2019-08-26 20:13:01 +00:00
|
|
|
if(active) {
|
2020-03-31 08:46:25 +00:00
|
|
|
_holdoff = 0;
|
2019-08-26 20:13:01 +00:00
|
|
|
int runstate = getHeaterInfo().getRunStateEx();
|
|
|
|
int errstate = getHeaterInfo().getErrState();
|
|
|
|
if(runstate == 0 && errstate == 0) {
|
|
|
|
requestOn(); // request heater to start upon closure of thermostat input (may have shutdown before contact closed again)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-03-31 08:46:25 +00:00
|
|
|
if(_holdoff) {
|
|
|
|
long tDelta = millis() - _holdoff;
|
2019-08-25 20:37:41 +00:00
|
|
|
if(tDelta >= 0) {
|
|
|
|
DebugPort.println("stopping heater due to thermostat contact being open for required dwell");
|
|
|
|
requestOff(); // request heater to stop after thermostat input has stayed open for interval
|
2020-03-31 08:46:25 +00:00
|
|
|
_holdoff = 0;
|
2019-08-25 20:37:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
// handling actually performed at Tx Manage for setting the fuel rate
|
|
|
|
}
|
|
|
|
|
2020-03-31 08:46:25 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin2::_doFuelReset(bool active)
|
|
|
|
{
|
|
|
|
if(active) {
|
|
|
|
if(!_prevActive) {
|
|
|
|
_holdoff = millis() + 1000; // require 1 second hold to reset fuel gauge
|
|
|
|
}
|
|
|
|
if(_holdoff) {
|
|
|
|
long tDelta = millis() - _holdoff;
|
|
|
|
if(tDelta > 0) { // 1 second has expired
|
|
|
|
resetFuelGauge();
|
|
|
|
_holdoff = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_holdoff = 0; // ensure fresh
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-26 20:13:01 +00:00
|
|
|
const char*
|
2020-03-23 05:54:15 +00:00
|
|
|
CGPIOin2:: getExtThermTime()
|
2019-08-26 20:13:01 +00:00
|
|
|
{
|
2020-03-31 08:46:25 +00:00
|
|
|
if((_holdoff == 0) || (NVstore.getUserSettings().ThermostatMethod != 3) || (NVstore.getUserSettings().ExtThermoTimeout == 0))
|
2019-08-26 20:13:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
2020-03-31 08:46:25 +00:00
|
|
|
long tDelta = _holdoff - millis();
|
2019-08-26 20:13:01 +00:00
|
|
|
if(tDelta < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
long secs = tDelta / 1000;
|
|
|
|
long mins = secs / 60;
|
|
|
|
secs -= mins * 60;
|
|
|
|
static char timeStr[8];
|
|
|
|
sprintf(timeStr, "%02ld:%02ld", mins, secs);
|
|
|
|
return timeStr;
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
|
2019-06-04 20:15:12 +00:00
|
|
|
|
2019-04-06 11:06:50 +00:00
|
|
|
CGPIOin::CGPIOin()
|
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
_Input1.setMode(CGPIOin1::Disabled);
|
|
|
|
_Input2.setMode(CGPIOin2::Disabled);
|
2019-04-06 11:06:50 +00:00
|
|
|
_lastKey = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOin::begin(int pin1, int pin2, CGPIOin1::Modes mode1, CGPIOin2::Modes mode2, int activeState)
|
2019-04-06 11:06:50 +00:00
|
|
|
{
|
2019-04-13 09:05:53 +00:00
|
|
|
_Debounce.addPin(pin1);
|
|
|
|
_Debounce.addPin(pin2);
|
|
|
|
_Debounce.setActiveState(activeState);
|
2019-04-06 11:06:50 +00:00
|
|
|
|
2019-08-07 12:04:20 +00:00
|
|
|
setMode(mode1, mode2);
|
2019-04-06 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 23:00:36 +00:00
|
|
|
uint8_t
|
|
|
|
CGPIOin::getState(int channel)
|
|
|
|
{
|
2019-06-04 20:36:29 +00:00
|
|
|
uint8_t retval = 0;
|
|
|
|
|
|
|
|
if((channel & ~0x01) == 0) {
|
|
|
|
// index is in bounds 0 or 1
|
|
|
|
|
|
|
|
// check for transient events
|
|
|
|
if(_eventList[channel].empty()) {
|
|
|
|
// read last actual state
|
|
|
|
int mask = 0x01 << (channel & 0x01);
|
|
|
|
retval = (_Debounce.getState() & mask) != 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// emit transient events if they occured
|
|
|
|
retval = _eventList[channel].front() != 0;
|
|
|
|
_eventList[channel].pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
2019-04-10 23:00:36 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOin1::Modes CGPIOin::getMode1() const
|
2019-06-04 20:15:12 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
return _Input1.getMode();
|
2019-08-07 12:04:20 +00:00
|
|
|
};
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOin2::Modes CGPIOin::getMode2() const
|
2019-08-07 12:04:20 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
return _Input2.getMode();
|
2019-06-04 20:15:12 +00:00
|
|
|
};
|
|
|
|
|
2019-04-06 11:06:50 +00:00
|
|
|
void
|
2019-04-08 23:12:42 +00:00
|
|
|
CGPIOin::manage()
|
2019-06-04 20:15:12 +00:00
|
|
|
{
|
|
|
|
uint8_t newKey = _Debounce.manage();
|
|
|
|
// determine edge events
|
|
|
|
uint8_t keyChange = newKey ^ _lastKey;
|
|
|
|
_lastKey = newKey;
|
|
|
|
|
|
|
|
if(keyChange) {
|
2019-06-04 20:36:29 +00:00
|
|
|
|
|
|
|
// record possible sub sample transients - JSON usage especially
|
|
|
|
if(keyChange & 0x01)
|
|
|
|
_eventList[0].push_back(newKey & 0x01); // mask the channel bit
|
|
|
|
if(keyChange & 0x02)
|
|
|
|
_eventList[1].push_back(newKey & 0x02); // mask the channel bit
|
2019-06-04 20:15:12 +00:00
|
|
|
}
|
2019-08-25 20:37:41 +00:00
|
|
|
simulateKey(newKey);
|
2019-06-04 20:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOin::simulateKey(uint8_t newKey)
|
2019-04-06 11:06:50 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
_Input1.manage((newKey & 0x01) != 0);
|
|
|
|
_Input2.manage((newKey & 0x02) != 0);
|
2019-04-06 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-30 10:48:25 +00:00
|
|
|
|
|
|
|
/*********************************************************************************************************
|
|
|
|
** GPIO out base class
|
|
|
|
*********************************************************************************************************/
|
|
|
|
CGPIOoutBase::CGPIOoutBase()
|
|
|
|
{
|
|
|
|
_pin = 0;
|
|
|
|
_thresh = 0;
|
|
|
|
_userState = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::begin(int pin)
|
|
|
|
{
|
|
|
|
_pin = pin;
|
|
|
|
if(pin) {
|
|
|
|
pinMode(pin, OUTPUT); // GPIO output pin #1
|
|
|
|
digitalWrite(pin, LOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::setThresh(int thresh)
|
|
|
|
{
|
|
|
|
_thresh = thresh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::setState(bool state)
|
|
|
|
{
|
|
|
|
_userState = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CGPIOoutBase::_getUserState()
|
|
|
|
{
|
|
|
|
return _userState;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::_setPinState(int state)
|
|
|
|
{
|
|
|
|
digitalWrite(_pin, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CGPIOoutBase::_getPinState()
|
|
|
|
{
|
|
|
|
return digitalRead(_pin);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::_doUser()
|
|
|
|
{
|
|
|
|
// DebugPort.println("GPIOout::_doUser2()");
|
|
|
|
if(_pin) {
|
|
|
|
digitalWrite(_pin, _userState ? HIGH : LOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOoutBase::_doThresh()
|
|
|
|
{
|
|
|
|
if(_thresh) {
|
|
|
|
float tAct = getTemperatureSensor(0);
|
|
|
|
if(digitalRead(_pin)) {
|
|
|
|
// output is currently active
|
|
|
|
if(_thresh > 0) { // active when OVER threshold mode
|
|
|
|
if((tAct + 0.1) < _thresh) { // test if under threshold +0.1deg hysteresis
|
|
|
|
digitalWrite(_pin, LOW); // deactivate output when less than threshold
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // active if UNDER threshold mode
|
|
|
|
if(tAct > -_thresh) { // inactive if over threshold
|
|
|
|
digitalWrite(_pin, LOW); // deactivate output when over threshold
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// output is not currently active
|
|
|
|
if(_thresh > 0) { // active when OVER threshold mode
|
|
|
|
if(tAct > _thresh) { // test if over threshold
|
|
|
|
digitalWrite(_pin, HIGH); // activate output when over threshold
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // active if UNDER threshold mode
|
|
|
|
if((tAct + 0.1) < -_thresh) { // test if under threshold +0.1deg hysteresis
|
|
|
|
digitalWrite(_pin, HIGH); // activate output when under threshold
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 05:54:15 +00:00
|
|
|
void
|
|
|
|
CGPIOoutBase::_doActive()
|
|
|
|
{
|
|
|
|
int runstate = getHeaterInfo().getRunState(); // raw state, not suspend mode enhanced
|
|
|
|
digitalWrite(_pin, runstate ? HIGH : LOW); // activates output when heater is not in standby
|
|
|
|
}
|
2019-10-30 10:48:25 +00:00
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
/*********************************************************************************************************
|
2019-10-30 10:48:25 +00:00
|
|
|
** GPIO out manager
|
2019-08-08 20:13:02 +00:00
|
|
|
*********************************************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
CGPIOout::CGPIOout()
|
2019-04-06 11:06:50 +00:00
|
|
|
{
|
2019-08-07 12:04:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout::begin(int pin1, int pin2, CGPIOout1::Modes mode1, CGPIOout2::Modes mode2)
|
2019-08-07 12:04:20 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
_Out1.begin(pin1, mode1);
|
|
|
|
_Out2.begin(pin2, mode2);
|
2019-04-06 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout::setMode(CGPIOout1::Modes mode1, CGPIOout2::Modes mode2)
|
|
|
|
{
|
|
|
|
_Out1.setMode(mode1);
|
|
|
|
_Out2.setMode(mode2);
|
|
|
|
};
|
|
|
|
|
2019-10-30 10:48:25 +00:00
|
|
|
void
|
|
|
|
CGPIOout::setThresh(int op1, int op2)
|
|
|
|
{
|
|
|
|
_Out1.setThresh(op1);
|
|
|
|
_Out2.setThresh(op2);
|
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::Modes
|
|
|
|
CGPIOout::getMode1() const
|
2019-04-06 11:06:50 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
return _Out1.getMode();
|
|
|
|
};
|
|
|
|
|
|
|
|
CGPIOout2::Modes
|
|
|
|
CGPIOout::getMode2() const
|
|
|
|
{
|
|
|
|
return _Out2.getMode();
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOout::manage()
|
|
|
|
{
|
|
|
|
_Out1.manage();
|
|
|
|
_Out2.manage();
|
2019-04-06 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
|
2019-04-06 11:06:50 +00:00
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout::setState(int channel, bool state)
|
2019-04-06 11:06:50 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
if(channel)
|
|
|
|
_Out2.setState(state);
|
|
|
|
else
|
|
|
|
_Out1.setState(state);
|
2019-04-06 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 05:57:46 +00:00
|
|
|
uint8_t
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout::getState(int channel)
|
|
|
|
{
|
|
|
|
if(channel)
|
|
|
|
return _Out2.getState();
|
|
|
|
else
|
|
|
|
return _Out1.getState();
|
|
|
|
}
|
2019-04-12 23:18:07 +00:00
|
|
|
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
/*********************************************************************************************************
|
|
|
|
** GPIO out #1
|
|
|
|
*********************************************************************************************************/
|
|
|
|
|
|
|
|
|
2019-10-30 10:48:25 +00:00
|
|
|
CGPIOout1::CGPIOout1() : CGPIOoutBase()
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
_Mode = Disabled;
|
2019-04-08 23:12:42 +00:00
|
|
|
_breatheDelay = 0;
|
|
|
|
_statusState = 0;
|
|
|
|
_statusDelay = 0;
|
2019-04-09 23:28:46 +00:00
|
|
|
_prevState = -1;
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-10-30 10:48:25 +00:00
|
|
|
CGPIOout1::begin(int pin, CGPIOout1::Modes mode)
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
2019-10-30 10:48:25 +00:00
|
|
|
CGPIOoutBase::begin(pin);
|
|
|
|
|
|
|
|
ledcSetup(0, 500, 8); // create PWM channel for GPIO1: 500Hz, 8 bits
|
2019-04-08 23:12:42 +00:00
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
setMode(mode);
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
|
2019-04-09 23:28:46 +00:00
|
|
|
void
|
2019-10-28 06:32:16 +00:00
|
|
|
CGPIOout1::setMode(CGPIOout1::Modes mode)
|
|
|
|
{
|
2020-03-23 05:54:15 +00:00
|
|
|
if(mode >= Disabled && mode <= HtrActive)
|
2019-10-28 06:32:16 +00:00
|
|
|
_Mode = mode;
|
2019-04-09 23:28:46 +00:00
|
|
|
_prevState = -1;
|
2019-10-30 10:48:25 +00:00
|
|
|
if(_getPin())
|
|
|
|
ledcDetachPin(_getPin()); // ensure PWM detached from IO line
|
2019-04-09 23:28:46 +00:00
|
|
|
};
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::Modes CGPIOout1::getMode() const
|
2019-08-07 12:04:20 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
return _Mode;
|
2019-06-04 20:15:12 +00:00
|
|
|
};
|
|
|
|
|
2019-04-08 23:12:42 +00:00
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::manage()
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
2019-08-08 20:13:02 +00:00
|
|
|
switch (_Mode) {
|
2019-10-30 10:48:25 +00:00
|
|
|
case CGPIOout1::Disabled: break;
|
|
|
|
case CGPIOout1::Status: _doStatus(); break;
|
|
|
|
case CGPIOout1::User: _doUser(); break;
|
|
|
|
case CGPIOout1::Thresh: _doThresh(); break;
|
2020-03-23 05:54:15 +00:00
|
|
|
case CGPIOout1::HtrActive: _doActive(); break;
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::_doStatus()
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
2019-10-30 10:48:25 +00:00
|
|
|
int pin = _getPin();
|
|
|
|
if(pin == 0)
|
2019-04-08 23:12:42 +00:00
|
|
|
return;
|
|
|
|
|
2019-04-09 23:28:46 +00:00
|
|
|
// DebugPort.println("GPIOout::_doStatus()");
|
2019-04-08 23:12:42 +00:00
|
|
|
int runstate = getHeaterInfo().getRunStateEx();
|
|
|
|
int statusMode = 0;
|
|
|
|
switch(runstate) {
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 9:
|
|
|
|
// starting modes
|
|
|
|
statusMode = 1;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
// run mode
|
|
|
|
statusMode = 2;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
case 8:
|
|
|
|
case 11:
|
|
|
|
case 12:
|
|
|
|
// cooldown modes
|
|
|
|
statusMode = 3;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
// suspend mode
|
|
|
|
statusMode = 4;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-09 23:28:46 +00:00
|
|
|
|
|
|
|
// change of mode typically requires changing from simple digital out
|
|
|
|
// to PWM or vice versa
|
2019-04-08 23:12:42 +00:00
|
|
|
if(_prevState != statusMode) {
|
|
|
|
_prevState = statusMode;
|
|
|
|
_statusState = 0;
|
|
|
|
_statusDelay = millis() + BREATHINTERVAL;
|
|
|
|
switch(statusMode) {
|
|
|
|
case 0:
|
2019-10-30 10:48:25 +00:00
|
|
|
ledcDetachPin(pin); // detach PWM from IO line
|
|
|
|
_setPinState(LOW);
|
2019-08-10 05:57:46 +00:00
|
|
|
_ledState = 0;
|
2019-04-08 23:12:42 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-10-30 10:48:25 +00:00
|
|
|
ledcAttachPin(pin, 0); // attach PWM to GPIO line
|
2019-04-08 23:12:42 +00:00
|
|
|
ledcWrite(0, _statusState);
|
2019-04-09 23:28:46 +00:00
|
|
|
_breatheDelay = millis() + BREATHINTERVAL;
|
2020-03-23 05:54:15 +00:00
|
|
|
_ledState = 2;
|
2019-04-08 23:12:42 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2019-10-30 10:48:25 +00:00
|
|
|
ledcDetachPin(pin); // detach PWM from IO line
|
|
|
|
_setPinState(HIGH);
|
2019-08-10 05:57:46 +00:00
|
|
|
_ledState = 1;
|
2019-04-08 23:12:42 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2019-10-30 10:48:25 +00:00
|
|
|
ledcAttachPin(pin, 0); // attach PWM to GPIO line
|
2019-04-08 23:12:42 +00:00
|
|
|
_statusState = 255;
|
|
|
|
ledcWrite(0, _statusState);
|
2019-04-09 23:28:46 +00:00
|
|
|
_breatheDelay = millis() + BREATHINTERVAL;
|
2020-03-23 05:54:15 +00:00
|
|
|
_ledState = 3;
|
2019-04-08 23:12:42 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2019-10-30 10:48:25 +00:00
|
|
|
ledcDetachPin(pin); // detach PWM from IO line
|
2019-04-08 23:12:42 +00:00
|
|
|
_breatheDelay += (FLASHPERIOD - ONFLASHINTERVAL); // extended off
|
2019-10-30 10:48:25 +00:00
|
|
|
_setPinState(LOW);
|
2020-03-23 05:54:15 +00:00
|
|
|
_ledState = 4;
|
2019-04-08 23:12:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch(statusMode) {
|
|
|
|
case 1: _doStartMode(); break;
|
|
|
|
case 3: _doStopMode(); break;
|
|
|
|
case 4: _doSuspendMode(); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::_doStartMode() // breath up PWM
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
|
|
|
long tDelta = millis() - _breatheDelay;
|
|
|
|
if(tDelta >= 0) {
|
|
|
|
_breatheDelay += BREATHINTERVAL;
|
2019-04-09 23:28:46 +00:00
|
|
|
int expo = ((_statusState >> 5) + 1);
|
|
|
|
_statusState += expo;
|
|
|
|
_statusState &= 0xff;
|
|
|
|
ledcWrite(0, _statusState);
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
2019-08-10 05:57:46 +00:00
|
|
|
_ledState = 2;
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::_doStopMode() // breath down PWM
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
|
|
|
long tDelta = millis() - _breatheDelay;
|
|
|
|
if(tDelta >= 0) {
|
|
|
|
_breatheDelay += BREATHINTERVAL;
|
2019-04-09 23:28:46 +00:00
|
|
|
int expo = ((_statusState >> 5) + 1);
|
|
|
|
_statusState -= expo;
|
|
|
|
_statusState &= 0xff;
|
|
|
|
ledcWrite(0, _statusState);
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
2020-03-23 05:54:15 +00:00
|
|
|
_ledState = 3;
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::_doSuspendMode() // brief flash
|
2019-04-08 23:12:42 +00:00
|
|
|
{
|
2019-08-10 05:57:46 +00:00
|
|
|
static unsigned long stretch = 0;
|
|
|
|
|
2019-04-08 23:12:42 +00:00
|
|
|
long tDelta = millis() - _breatheDelay;
|
|
|
|
if(tDelta >= 0) {
|
|
|
|
_statusState++;
|
|
|
|
if(_statusState & 0x01) {
|
|
|
|
_breatheDelay += ONFLASHINTERVAL; // brief flash on
|
2019-10-30 10:48:25 +00:00
|
|
|
_setPinState(HIGH);
|
2019-08-10 05:57:46 +00:00
|
|
|
stretch = (millis() + 250) | 1; // pulse extend for UI purposes, ensure non zero
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
_breatheDelay += (FLASHPERIOD - ONFLASHINTERVAL); // extended off
|
2019-10-30 10:48:25 +00:00
|
|
|
_setPinState(LOW);
|
2019-04-08 23:12:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-10 05:57:46 +00:00
|
|
|
if(stretch) {
|
|
|
|
tDelta = millis() - stretch;
|
|
|
|
if(tDelta >= 0)
|
|
|
|
stretch = 0;
|
|
|
|
}
|
2020-03-23 05:54:15 +00:00
|
|
|
_ledState = 4;
|
2019-04-09 23:28:46 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 05:57:46 +00:00
|
|
|
uint8_t
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout1::getState()
|
|
|
|
{
|
2019-08-10 05:57:46 +00:00
|
|
|
switch(_Mode) {
|
2019-10-30 10:48:25 +00:00
|
|
|
case User:
|
|
|
|
case Thresh:
|
2020-03-23 05:54:15 +00:00
|
|
|
case HtrActive:
|
2019-10-30 10:48:25 +00:00
|
|
|
return _getPinState();
|
|
|
|
case Status:
|
|
|
|
return _ledState; // special pulse extender for suspend mode
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************************
|
|
|
|
** GPIO2
|
|
|
|
*********************************************************************************************************/
|
2019-10-30 10:48:25 +00:00
|
|
|
CGPIOout2::CGPIOout2() : CGPIOoutBase()
|
2019-08-08 20:13:02 +00:00
|
|
|
{
|
|
|
|
_Mode = Disabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOout2::begin(int pin, Modes mode)
|
2019-04-09 23:28:46 +00:00
|
|
|
{
|
2019-10-30 10:48:25 +00:00
|
|
|
CGPIOoutBase::begin(pin);
|
|
|
|
|
|
|
|
ledcSetup(1, 500, 8); // create PWM channel for GPIO2: 500Hz, 8 bits
|
2019-08-08 20:13:02 +00:00
|
|
|
|
|
|
|
setMode(mode);
|
2019-04-12 23:18:07 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
void
|
|
|
|
CGPIOout2::setMode(CGPIOout2::Modes mode)
|
|
|
|
{
|
2020-03-23 05:54:15 +00:00
|
|
|
if(mode >= Disabled && mode <= HtrActive)
|
2019-10-28 06:32:16 +00:00
|
|
|
_Mode = mode;
|
2019-10-30 10:48:25 +00:00
|
|
|
int pin = _getPin();
|
|
|
|
if(pin)
|
|
|
|
ledcDetachPin(pin); // ensure PWM detached from IO line
|
2019-08-08 20:13:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
CGPIOout2::Modes CGPIOout2::getMode() const
|
|
|
|
{
|
|
|
|
return _Mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
CGPIOout2::manage()
|
|
|
|
{
|
|
|
|
switch (_Mode) {
|
|
|
|
case CGPIOout2::Disabled: break;
|
|
|
|
case CGPIOout2::User: _doUser(); break;
|
2019-10-30 10:48:25 +00:00
|
|
|
case CGPIOout2::Thresh: _doThresh(); break;
|
2020-03-23 05:54:15 +00:00
|
|
|
case CGPIOout2::HtrActive: _doActive(); break;
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-10 05:57:46 +00:00
|
|
|
uint8_t
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOout2::getState()
|
|
|
|
{
|
2019-10-30 10:48:25 +00:00
|
|
|
switch (_Mode) {
|
|
|
|
case CGPIOout2::User:
|
|
|
|
case CGPIOout2::Thresh:
|
2020-03-23 05:54:15 +00:00
|
|
|
case CGPIOout2::HtrActive:
|
2019-10-30 10:48:25 +00:00
|
|
|
return _getPinState();
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-08 20:13:02 +00:00
|
|
|
}
|
2019-04-12 23:18:07 +00:00
|
|
|
|
2019-04-27 10:41:47 +00:00
|
|
|
// expected external analogue circuit is a 10k pot.
|
|
|
|
// Top end of pot is connected to GPIO Vcc (red wire) via 5k6 fixed resistor. (GPIO Vcc is 5V via schottky diode)
|
|
|
|
// Bottom end of pot is connected to GND (black wire) via 1k fixed resistor.
|
|
|
|
// Wiper is into Pin 6 of GPIO (white wire) - analogue input
|
|
|
|
|
2019-04-12 23:18:07 +00:00
|
|
|
CGPIOalg::CGPIOalg()
|
|
|
|
{
|
|
|
|
_expMean = 0;
|
2019-10-28 06:32:16 +00:00
|
|
|
_Mode = Disabled;
|
2019-04-12 23:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOalg::begin(adc1_channel_t pin, CGPIOalg::Modes mode)
|
2019-04-12 23:18:07 +00:00
|
|
|
{
|
|
|
|
_pin = pin;
|
|
|
|
_Mode = mode;
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
if(_Mode != CGPIOalg::Disabled) {
|
2019-04-12 23:18:07 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
CGPIOalg::Modes CGPIOalg::getMode() const
|
2019-06-04 20:15:12 +00:00
|
|
|
{
|
|
|
|
return _Mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-04-12 23:18:07 +00:00
|
|
|
void CGPIOalg::manage()
|
|
|
|
{
|
2019-04-27 10:41:47 +00:00
|
|
|
const float fAlpha = 0.95; // exponential mean alpha
|
|
|
|
|
2019-08-08 20:13:02 +00:00
|
|
|
if(_Mode != CGPIOalg::Disabled) {
|
2019-04-12 23:18:07 +00:00
|
|
|
int read_raw;
|
|
|
|
char msg[32];
|
|
|
|
read_raw = adc1_get_raw( ADC1_CHANNEL_5);
|
|
|
|
sprintf(msg, "ADC: %d", read_raw );
|
2019-04-27 10:41:47 +00:00
|
|
|
_expMean = _expMean * fAlpha + (1-fAlpha) * float(read_raw);
|
2019-04-12 23:18:07 +00:00
|
|
|
// DebugPort.println(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CGPIOalg::getValue()
|
|
|
|
{
|
|
|
|
return _expMean;
|
|
|
|
}
|