de6226ad12
Added adjustable -ve threshold for Jess mode (cyclic shutdown if over temp). Added user selectable display blank, dim or do nothing option on keypad inactivity. Added user selectable menu timeout on keypad inactivity.
374 lines
7.9 KiB
C++
374 lines
7.9 KiB
C++
/*
|
|
* 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 "GPIO.h"
|
|
#include "../Protocol/helpers.h"
|
|
#include <driver/adc.h>
|
|
#include "DebugPort.h"
|
|
|
|
const int BREATHINTERVAL = 45;
|
|
const int FADEAMOUNT = 3;
|
|
const int FLASHPERIOD = 2000;
|
|
const int ONFLASHINTERVAL = 50;
|
|
|
|
CGPIOin::CGPIOin()
|
|
{
|
|
_Mode = GPIOinNone;
|
|
_lastKey = 0;
|
|
}
|
|
|
|
void
|
|
CGPIOin::begin(int pin1, int pin2, GPIOinModes mode, int activeState)
|
|
{
|
|
_Debounce.addPin(pin1);
|
|
_Debounce.addPin(pin2);
|
|
_Debounce.setActiveState(activeState);
|
|
|
|
setMode(mode);
|
|
}
|
|
|
|
uint8_t
|
|
CGPIOin::getState(int channel)
|
|
{
|
|
int mask = 0x01 << (channel & 0x01);
|
|
return (_Debounce.getState() & mask) != 0;
|
|
}
|
|
|
|
void
|
|
CGPIOin::manage()
|
|
{
|
|
switch (_Mode) {
|
|
case GPIOinNone:
|
|
break;
|
|
case GPIOinOn1Off2:
|
|
_doOn1Off2();
|
|
break;
|
|
case GPIOinOnHold1:
|
|
_doOnHold1();
|
|
break;
|
|
case GPIOinOn1Off1:
|
|
_doOn1Off1();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOin::_doOn1Off2()
|
|
{
|
|
uint8_t newKey = _Debounce.manage();
|
|
// determine edge events
|
|
uint8_t keyChange = newKey ^ _lastKey;
|
|
_lastKey = newKey;
|
|
|
|
if(keyChange) {
|
|
if(newKey & 0x01) {
|
|
requestOn();
|
|
}
|
|
if(newKey & 0x02) {
|
|
requestOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
// mode where heater runs if input 1 is shorted
|
|
// stops when open
|
|
void
|
|
CGPIOin::_doOnHold1()
|
|
{
|
|
uint8_t newKey = _Debounce.manage();
|
|
// determine edge events
|
|
uint8_t keyChange = newKey ^ _lastKey;
|
|
_lastKey = newKey;
|
|
|
|
if(keyChange) {
|
|
if(newKey & 0x01) {
|
|
requestOn();
|
|
}
|
|
else {
|
|
requestOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
// mode where heater runs if input 1 is shorted
|
|
// stops when open
|
|
void
|
|
CGPIOin::_doOn1Off1()
|
|
{
|
|
uint8_t newKey = _Debounce.manage();
|
|
// determine edge events
|
|
uint8_t keyChange = newKey ^ _lastKey;
|
|
_lastKey = newKey;
|
|
|
|
if(keyChange) {
|
|
if(newKey & 0x01) {
|
|
if(getHeaterInfo().getRunStateEx())
|
|
requestOff();
|
|
else
|
|
requestOn();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CGPIOout::CGPIOout()
|
|
{
|
|
_Mode = GPIOoutNone;
|
|
_pins[0] = 0;
|
|
_pins[1] = 0;
|
|
_breatheDelay = 0;
|
|
_statusState = 0;
|
|
_statusDelay = 0;
|
|
_userState = 0;
|
|
_prevState = -1;
|
|
}
|
|
|
|
void
|
|
CGPIOout::begin(int pin1, int pin2, GPIOoutModes mode)
|
|
{
|
|
_pins[0] = pin1;
|
|
_pins[1] = pin2;
|
|
if(pin1) {
|
|
pinMode(pin1, OUTPUT); // GPIO output pin #1
|
|
digitalWrite(pin1, LOW);
|
|
ledcSetup(0, 500, 8); // create PWM channel for GPIO1: 500Hz, 8 bits
|
|
}
|
|
if(pin2) {
|
|
pinMode(pin2, OUTPUT); // GPIO output pin #2
|
|
digitalWrite(pin2, LOW);
|
|
ledcSetup(1, 500, 8); // create PWM channel for GPIO2: 500Hz, 8 bits
|
|
}
|
|
|
|
setMode(mode);
|
|
}
|
|
|
|
void
|
|
CGPIOout::setMode(GPIOoutModes mode)
|
|
{
|
|
_Mode = mode;
|
|
_prevState = -1;
|
|
ledcDetachPin(_pins[0]); // ensure PWM detached from IO line
|
|
ledcDetachPin(_pins[1]); // ensure PWM detached from IO line
|
|
};
|
|
|
|
void
|
|
CGPIOout::manage()
|
|
{
|
|
switch (_Mode) {
|
|
case GPIOoutNone: break;
|
|
case GPIOoutStatus: _doStatus(); break;
|
|
case GPIOoutUser: _doUser(); break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::_doStatus()
|
|
{
|
|
if(_pins[0] == 0)
|
|
return;
|
|
|
|
// DebugPort.println("GPIOout::_doStatus()");
|
|
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;
|
|
}
|
|
|
|
// change of mode typically requires changing from simple digital out
|
|
// to PWM or vice versa
|
|
if(_prevState != statusMode) {
|
|
_prevState = statusMode;
|
|
_statusState = 0;
|
|
_statusDelay = millis() + BREATHINTERVAL;
|
|
switch(statusMode) {
|
|
case 0:
|
|
ledcDetachPin(_pins[0]); // detach PWM from IO line
|
|
digitalWrite(_pins[0], LOW);
|
|
break;
|
|
case 1:
|
|
ledcAttachPin(_pins[0], 0); // attach PWM to GPIO line
|
|
ledcWrite(0, _statusState);
|
|
_breatheDelay = millis() + BREATHINTERVAL;
|
|
break;
|
|
case 2:
|
|
ledcDetachPin(_pins[0]); // detach PWM from IO line
|
|
digitalWrite(_pins[0], HIGH);
|
|
break;
|
|
case 3:
|
|
ledcAttachPin(_pins[0], 0); // attach PWM to GPIO line
|
|
_statusState = 255;
|
|
ledcWrite(0, _statusState);
|
|
_breatheDelay = millis() + BREATHINTERVAL;
|
|
break;
|
|
case 4:
|
|
ledcDetachPin(_pins[0]); // detach PWM from IO line
|
|
_breatheDelay += (FLASHPERIOD - ONFLASHINTERVAL); // extended off
|
|
digitalWrite(_pins[0], LOW);
|
|
break;
|
|
}
|
|
}
|
|
switch(statusMode) {
|
|
case 1: _doStartMode(); break;
|
|
case 3: _doStopMode(); break;
|
|
case 4: _doSuspendMode(); break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::_doUser()
|
|
{
|
|
// DebugPort.println("GPIOout::_doUser()");
|
|
if(_pins[0]) {
|
|
digitalWrite(_pins[0], (_userState & 0x01) ? HIGH : LOW);
|
|
}
|
|
if(_pins[1]) {
|
|
digitalWrite(_pins[1], (_userState & 0x02) ? HIGH : LOW);
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::_doStartMode() // breath up PWM
|
|
{
|
|
long tDelta = millis() - _breatheDelay;
|
|
if(tDelta >= 0) {
|
|
_breatheDelay += BREATHINTERVAL;
|
|
int expo = ((_statusState >> 5) + 1);
|
|
_statusState += expo;
|
|
_statusState &= 0xff;
|
|
ledcWrite(0, _statusState);
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::_doStopMode() // breath down PWM
|
|
{
|
|
long tDelta = millis() - _breatheDelay;
|
|
if(tDelta >= 0) {
|
|
_breatheDelay += BREATHINTERVAL;
|
|
int expo = ((_statusState >> 5) + 1);
|
|
_statusState -= expo;
|
|
_statusState &= 0xff;
|
|
ledcWrite(0, _statusState);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::_doSuspendMode() // brief flash
|
|
{
|
|
long tDelta = millis() - _breatheDelay;
|
|
if(tDelta >= 0) {
|
|
_statusState++;
|
|
if(_statusState & 0x01) {
|
|
_breatheDelay += ONFLASHINTERVAL; // brief flash on
|
|
digitalWrite(_pins[0], HIGH);
|
|
}
|
|
else {
|
|
_breatheDelay += (FLASHPERIOD - ONFLASHINTERVAL); // extended off
|
|
digitalWrite(_pins[0], LOW);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CGPIOout::setState(int channel, bool state)
|
|
{
|
|
int mask = 0x01 << (channel & 0x01);
|
|
if(state)
|
|
_userState |= mask;
|
|
else
|
|
_userState &= ~mask;
|
|
}
|
|
|
|
bool
|
|
CGPIOout::getState(int channel)
|
|
{
|
|
int mask = 0x01 << (channel & 0x01);
|
|
return (_userState & mask) != 0;
|
|
}
|
|
|
|
|
|
// 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
|
|
|
|
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()
|
|
{
|
|
const float fAlpha = 0.95; // exponential mean alpha
|
|
|
|
if(_Mode != GPIOalgNone) {
|
|
int read_raw;
|
|
char msg[32];
|
|
read_raw = adc1_get_raw( ADC1_CHANNEL_5);
|
|
sprintf(msg, "ADC: %d", read_raw );
|
|
_expMean = _expMean * fAlpha + (1-fAlpha) * float(read_raw);
|
|
// DebugPort.println(msg);
|
|
}
|
|
}
|
|
|
|
int
|
|
CGPIOalg::getValue()
|
|
{
|
|
return _expMean;
|
|
}
|