Added missing files
This commit is contained in:
parent
2f1605e6a3
commit
8006b98ba2
332
src/OLED/433MHzScreen.cpp
Normal file
332
src/OLED/433MHzScreen.cpp
Normal file
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// C433MHzScreen
|
||||
//
|
||||
// This screen allows the pairing of 433MHz remotes
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "433MHzScreen.h"
|
||||
#include "KeyPad.h"
|
||||
#include "fonts/Arial.h"
|
||||
#include "../RTC/Clock.h"
|
||||
#include "../Utility/macros.h"
|
||||
#include "../Utility/NVStorage.h"
|
||||
#include "../Protocol/433MHz.h"
|
||||
#include "fonts/Icons.h"
|
||||
|
||||
extern bool pair433MHz;
|
||||
static const int column[] = { 64, 84, 104, 120 };
|
||||
static const int line[] = { 42, 31, 20 };
|
||||
|
||||
C433MHzScreen::C433MHzScreen(C128x64_OLED& display, CScreenManager& mgr) : CUIEditScreen(display, mgr)
|
||||
{
|
||||
_initUI();
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzScreen::onSelect()
|
||||
{
|
||||
CScreen::onSelect();
|
||||
_initUI();
|
||||
pair433MHz = true;
|
||||
UHFremote.getCodes(_rawCodes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzScreen::onExit()
|
||||
{
|
||||
pair433MHz = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
C433MHzScreen::_initUI()
|
||||
{
|
||||
CUIEditScreen::_initUI();
|
||||
_repeatCount = 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
C433MHzScreen::show()
|
||||
{
|
||||
_display.clearDisplay();
|
||||
|
||||
if(!CUIEditScreen::show()) {
|
||||
|
||||
_showTitle("433MHz Remote");
|
||||
|
||||
_drawBitmap(61, 13, medStopIconInfo);
|
||||
_drawBitmap(81, 12, medStartIconInfo);
|
||||
_drawBitmap(100, 14, dnIconInfo);
|
||||
_drawBitmap(116, 13, upIconInfo);
|
||||
|
||||
_printMenuText(5, line[2], "Remote 1", _rowSel == 3 && _colSel == 0);
|
||||
_printMenuText(5, line[1], "Remote 2", _rowSel == 2 && _colSel == 0);
|
||||
_printMenuText(5, line[0], "Remote 3", _rowSel == 1 && _colSel == 0);
|
||||
|
||||
if(_rowSel == 0) {
|
||||
_printMenuText(_display.xCentre(), 53, " \021 Exit \020 ", true, eCentreJustify);
|
||||
}
|
||||
else {
|
||||
switch(_colSel) {
|
||||
case 0:
|
||||
_printMenuText(_display.xCentre(), 54, " \020 to start teaching ", false, eCentreJustify);
|
||||
break;
|
||||
case 1:
|
||||
_printMenuText(_display.xCentre(), 54, " Teach \"Off\"", false, eCentreJustify);
|
||||
break;
|
||||
case 2:
|
||||
_printMenuText(_display.xCentre(), 54, " Teach \"On\" ", false, eCentreJustify);
|
||||
break;
|
||||
case 3:
|
||||
_printMenuText(_display.xCentre(), 54, " Teach \"Decrease\" ", false, eCentreJustify);
|
||||
break;
|
||||
case 4:
|
||||
_printMenuText(_display.xCentre(), 54, " Teach \"Increase\" ", false, eCentreJustify);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzScreen::animate()
|
||||
{
|
||||
|
||||
if(_saveBusy()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(UHFremote.available()) {
|
||||
UHFremote.read(_code);
|
||||
DebugPort.printf("UHF remote code = %08lX\r\n", _code);
|
||||
if(_colSel) {
|
||||
if(_code) {
|
||||
_rawCodes[_rowSel-1][_colSel-1] = _code;
|
||||
}
|
||||
else {
|
||||
_colSel++;
|
||||
WRAPLIMITS(_colSel, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int row = 0; row < 3; row++) {
|
||||
for(int col = 0; col < 4; col++) {
|
||||
int xPos = column[col];
|
||||
int yPos = line[row];
|
||||
bool rowColMatch = (row == (_rowSel-1)) && (col == (_colSel-1));
|
||||
if(_rawCodes[row][col]) {
|
||||
if(rowColMatch)
|
||||
_printMenuText(xPos, yPos, "*", true, eCentreJustify);
|
||||
else
|
||||
_printInverted(xPos, yPos, "*", _rawCodes[row][col] == _code, eCentreJustify);
|
||||
}
|
||||
else {
|
||||
_printMenuText(xPos, yPos, " ", rowColMatch, eCentreJustify);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzScreen::keyHandler(uint8_t event)
|
||||
{
|
||||
|
||||
if(CUIEditScreen::keyHandler(event)) { // manage password collection and NV save confirm
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event & keyPressed) {
|
||||
_repeatCount = 0;
|
||||
// press CENTRE
|
||||
if(event & key_Centre) {
|
||||
}
|
||||
// press LEFT
|
||||
if(event & key_Left) {
|
||||
if(_rowSel == 0) {
|
||||
_ScreenManager.prevMenu();
|
||||
}
|
||||
else {
|
||||
_colSel--;
|
||||
WRAPLOWERLIMIT(_colSel, 0, 4);
|
||||
}
|
||||
}
|
||||
// press RIGHT
|
||||
if(event & key_Right) {
|
||||
if(_rowSel == 0) {
|
||||
_ScreenManager.nextMenu();
|
||||
}
|
||||
else {
|
||||
_colSel++;
|
||||
WRAPUPPERLIMIT(_colSel, 4, 0);
|
||||
}
|
||||
}
|
||||
// press UP
|
||||
if(event & key_Up) {
|
||||
_rowSel++;
|
||||
_colSel = 0;
|
||||
UPPERLIMIT(_rowSel, 4);
|
||||
}
|
||||
// press DOWN
|
||||
if(event & key_Down) {
|
||||
_rowSel--;
|
||||
_colSel = 0;
|
||||
LOWERLIMIT(_rowSel, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(event & keyRepeat) {
|
||||
_repeatCount++;
|
||||
UPPERLIMIT(_repeatCount, 5);
|
||||
if(_repeatCount == 2) {
|
||||
if(_rowSel && _colSel) {
|
||||
_rawCodes[_rowSel-1][_colSel-1] = 0; // scrub code for button
|
||||
_colSel++;
|
||||
WRAPLIMITS(_colSel, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event & keyReleased) {
|
||||
// press CENTRE
|
||||
if(event & key_Centre) {
|
||||
if(_rowSel == 0) {
|
||||
_ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // force return to main menu
|
||||
}
|
||||
else if(_repeatCount == 0) {
|
||||
_confirmSave(); // enter save confirm mode
|
||||
_rowSel = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ScreenManager.reqUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Data word in NV ram is stored as follows
|
||||
//
|
||||
// | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// | | | | |
|
||||
// | | | | Enabled | Key Codes |
|
||||
// | Unique ID (20 bits) | U D R S| KCU | KCD | KCR | KCS |
|
||||
//
|
||||
// Key enabled bits
|
||||
// U = Up - key code in b7-b6
|
||||
// D = Down - key code in b5-b5
|
||||
// R = Run - key code in b3-b2
|
||||
// S = Stop - key code in b1-b0
|
||||
//
|
||||
// key code bits
|
||||
// 00 => 0x01
|
||||
// 01 => 0x02
|
||||
// 10 => 0x04
|
||||
// 11 => 0x08
|
||||
//
|
||||
/*void
|
||||
C433MHzScreen::_decode(int idx)
|
||||
{
|
||||
unsigned long code = _savedCodes[idx];
|
||||
for(int i=0; i<4; i++) {
|
||||
int mask = 0x100 << i;
|
||||
if(code & mask) {
|
||||
int uniqueID = (code >> 8) & 0xFFFFF0;
|
||||
int shift = (code >> (i*2)) & 0x3;
|
||||
int keyCode = 1 << shift;
|
||||
_rawCodes[idx][i] = uniqueID | keyCode;
|
||||
}
|
||||
else
|
||||
_rawCodes[idx][i] = 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
/*int
|
||||
C433MHzScreen::_encode(int idx)
|
||||
{
|
||||
unsigned long uniqueCode = _rawCodes[idx][0] & 0xFFFFF0;
|
||||
|
||||
// confirm all recorded keys share the same unique code
|
||||
for(int i=1; i<4; i++) {
|
||||
if(_rawCodes[idx][i] && (uniqueCode != (_rawCodes[idx][i] & 0xFFFFF0))) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// start building the encoded value for NV storage
|
||||
unsigned long encoded = uniqueCode << 8;
|
||||
for(int i=0; i<4; i++) {
|
||||
if(_rawCodes[idx][i]) {
|
||||
int keyCode = _rawCodes[idx][i] & 0xf;
|
||||
switch(keyCode) {
|
||||
case 1:
|
||||
encoded |= (0 << i*2);
|
||||
break;
|
||||
case 2:
|
||||
encoded |= (1 << i*2);
|
||||
break;
|
||||
case 4:
|
||||
encoded |= (2 << i*2);
|
||||
break;
|
||||
case 8:
|
||||
encoded |= (3 << i*2);
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
encoded |= (0x100 << i);
|
||||
}
|
||||
}
|
||||
_savedCodes[idx] = encoded;
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
void
|
||||
C433MHzScreen::_saveNV()
|
||||
{
|
||||
UHFremote.saveNV(_rawCodes);
|
||||
// sUserSettings userSettings = NVstore.getUserSettings();
|
||||
// for(int i=0; i<3; i++) {
|
||||
// int err = _encode(i);
|
||||
// if(err != 0) {
|
||||
// DebugPort.printf("Error encoding UHF code (%d)\r\n", err);
|
||||
// return;
|
||||
// }
|
||||
// userSettings.UHFcode[i] = _savedCodes[i];
|
||||
// }
|
||||
|
||||
// DebugPort.println("UHF Remote encodes");
|
||||
// for(int i = 0; i<3; i++) {
|
||||
// DebugPort.printf("0x%08lX 0x%08lX 0x%08lX 0x%08lX => 0x%08lX\r\n", _rawCodes[i][0], _rawCodes[i][1], _rawCodes[i][2], _rawCodes[i][3], _savedCodes[i]);
|
||||
// }
|
||||
// NVstore.setUserSettings(userSettings);
|
||||
// NVstore.save();
|
||||
}
|
54
src/OLED/433MHzScreen.h
Normal file
54
src/OLED/433MHzScreen.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __433MHZSCREEN_H__
|
||||
#define __433MHZSCREEN_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "UIEditScreen.h"
|
||||
#include "../RTC/BTCDateTime.h"
|
||||
|
||||
class C128x64_OLED;
|
||||
class CScreenManager;
|
||||
class CProtocol;
|
||||
|
||||
class C433MHzScreen : public CUIEditScreen {
|
||||
void _initUI();
|
||||
// void _decode(int idx);
|
||||
// int _encode(int idx);
|
||||
void _saveNV();
|
||||
unsigned long _code;
|
||||
// unsigned long _savedCodes[3];
|
||||
unsigned long _rawCodes[3][4];
|
||||
unsigned long _ID;
|
||||
uint8_t _defined;
|
||||
uint8_t _keyCode;
|
||||
uint8_t _repeatCount;
|
||||
public:
|
||||
C433MHzScreen(C128x64_OLED& display, CScreenManager& mgr);
|
||||
bool onSelect();
|
||||
void onExit();
|
||||
bool show();
|
||||
bool animate();
|
||||
bool keyHandler(uint8_t event);
|
||||
};
|
||||
|
||||
#endif
|
382
src/Protocol/433MHz.cpp
Normal file
382
src/Protocol/433MHz.cpp
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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 <FreeRTOS.h>
|
||||
#include "433MHz.h"
|
||||
#include "../cfg/pins.h"
|
||||
#include "../Utility/macros.h"
|
||||
#include "../Utility/NVStorage.h"
|
||||
#include "../Utility/helpers.h"
|
||||
|
||||
#define DEBUG_433MHz
|
||||
|
||||
C433MHzRemote UHFremote;
|
||||
|
||||
static void IRAM_ATTR rmt_driver_isr_default(void *arg);
|
||||
|
||||
|
||||
C433MHzRemote::C433MHzRemote()
|
||||
{
|
||||
_rxQueue = NULL;
|
||||
_taskHandle = NULL;
|
||||
_runState = 0;
|
||||
_prevCode = 0;
|
||||
_timeout = 0;
|
||||
_debug = false;
|
||||
}
|
||||
|
||||
C433MHzRemote::~C433MHzRemote()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzRemote::_staticTask(void* arg)
|
||||
{
|
||||
C433MHzRemote* pThis = (C433MHzRemote*)arg;
|
||||
|
||||
pThis->_task();
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
C433MHzRemote::_task()
|
||||
{
|
||||
rmt_rx_start(_rxCfg.channel, true);
|
||||
_runState = 1;
|
||||
while(_runState == 1) {
|
||||
_doComms();
|
||||
delay(1);
|
||||
}
|
||||
rmt_rx_stop(_rxCfg.channel);
|
||||
_runState = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
C433MHzRemote::_doComms()
|
||||
{
|
||||
// wait for ring buffer response, or time out
|
||||
size_t rx_size;
|
||||
rmt_item32_t* rxItems = (rmt_item32_t *)xRingbufferReceive(_ringbuffer, &rx_size, 45);
|
||||
|
||||
if (rxItems) {
|
||||
_decodeRxItems(rxItems, rx_size / 4);
|
||||
vRingbufferReturnItem(_ringbuffer, (void *)rxItems);
|
||||
}
|
||||
|
||||
if(_timeout) {
|
||||
long tDelta = xTaskGetTickCount() - _timeout;
|
||||
if(tDelta >= 0) {
|
||||
_timeout = 0;
|
||||
_prevCode = 0;
|
||||
if(_rxQueue)
|
||||
xQueueSend(_rxQueue, &_prevCode, 0); // inject no button press
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzRemote::_decodeRxItems(const rmt_item32_t* rxItems, int size)
|
||||
{
|
||||
// #ifdef DEBUG_433MHz
|
||||
// Serial.printf("433MHz RxItems = %d\r\n", size);
|
||||
// for(int i=0; i<size; i++) {
|
||||
// Serial.printf("[%d] %d:%5d %d:%5d\r\n", i, rxItems[i].level0, rxItems[i].duration0, rxItems[i].level1, rxItems[i].duration1);
|
||||
// }
|
||||
// #endif
|
||||
int workingsize = 0;
|
||||
unsigned long newCode = 0;
|
||||
if(size == 32)
|
||||
workingsize = 23;
|
||||
else if(size == 25)
|
||||
workingsize = 24;
|
||||
else {
|
||||
if(_debug)
|
||||
Serial.printf("433MHz remote incorrect number of transitions: %d?\r\n", size);
|
||||
return false;
|
||||
}
|
||||
// start OK, now read the 24 bit payload
|
||||
int meanBitTime = 0;
|
||||
for (int i = 0; i < workingsize; i++)
|
||||
{
|
||||
meanBitTime += rxItems[i].duration0 + rxItems[i].duration1; // add 1st and 2nd part times
|
||||
}
|
||||
meanBitTime /= workingsize;
|
||||
|
||||
for (int i = 0; i < workingsize; i++)
|
||||
{
|
||||
int bitTime = rxItems[i].duration0 + rxItems[i].duration1; // add 1st and 2nd part times
|
||||
// if(INBOUNDS(bitTime, 900, 1700) // confirm duration
|
||||
if(INBOUNDS(bitTime, meanBitTime - 500, meanBitTime + 500) // confirm duration
|
||||
&& rxItems[i].level0 == 1 // confirm 1st part is high
|
||||
&& rxItems[i].level1 == 0) // confirm 2nd part is low
|
||||
{
|
||||
|
||||
newCode <<= 1;
|
||||
|
||||
// OK, a 1 is accepted if high > 0.6ms
|
||||
// if(rxItems[i].duration0 > 600)
|
||||
if(rxItems[i].duration0 > meanBitTime/2)
|
||||
newCode |= 0x0001;
|
||||
}
|
||||
else {
|
||||
// Serial.printf("433MHz remote @ transition %d: bitTime=%d lvl0=%d lvl1=%d?\r\n", i, bitTime, rxItems[i].level0, rxItems[i].level1);
|
||||
newCode = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Serial.printf("433MHz val = 0x%08lX (%d)\r\n", newCode, size);
|
||||
if(_prevCode != newCode) {
|
||||
_prevCode = newCode;
|
||||
if(_rxQueue)
|
||||
xQueueSend(_rxQueue, &newCode, 0); // queue new button press
|
||||
// #ifdef DEBUG_433MHz
|
||||
if(_debug) {
|
||||
Serial.printf("433MHz RxItems = %d (%d)\r\n", size, meanBitTime);
|
||||
for(int i=0; i<size; i++) {
|
||||
Serial.printf("[%2d] %d:%5d %d:%5d (%d)\r\n", i, rxItems[i].level0, rxItems[i].duration0, rxItems[i].level1, rxItems[i].duration1, rxItems[i].duration0+rxItems[i].duration1);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
_timeout = (xTaskGetTickCount() + 100) | 1; // | 1 ensures non zero - timeout to allow injection of no button press
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
C433MHzRemote::begin(gpio_num_t pin, rmt_channel_t channel)
|
||||
{
|
||||
_readNV();
|
||||
|
||||
_rxCfg.rmt_mode = RMT_MODE_RX;
|
||||
_rxCfg.channel = channel;
|
||||
_rxCfg.gpio_num = pin;
|
||||
_rxCfg.mem_block_num = 1;
|
||||
_rxCfg.clk_div = 80; // 1us / clock
|
||||
_rxCfg.rx_config.filter_en = true;
|
||||
_rxCfg.rx_config.filter_ticks_thresh = 250;
|
||||
_rxCfg.rx_config.idle_threshold = 6000; // > 6ms no transitions => end of Rx
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&_rxCfg));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(_rxCfg.channel, 512, 0));
|
||||
// ringbuffer for rx
|
||||
ESP_ERROR_CHECK(rmt_get_ringbuf_handle(_rxCfg.channel, &_ringbuffer));
|
||||
|
||||
_rxQueue = xQueueCreate(4, sizeof(unsigned long));
|
||||
|
||||
xTaskCreate(_staticTask,
|
||||
"UHFremoteTask",
|
||||
4000,
|
||||
this,
|
||||
TASK_PRIORITY_HEATERCOMMS,
|
||||
&_taskHandle);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzRemote::end()
|
||||
{
|
||||
DebugPort.printf("Stopping UHF remote task %d\r\n", _runState);
|
||||
if(_runState == 1) { // check task is running
|
||||
_runState = 2; // ask task to stop
|
||||
DebugPort.println("Stopping UHF remote task wait");
|
||||
while(_runState != 0) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
_taskHandle = NULL;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(rmt_driver_uninstall(_rxCfg.channel));
|
||||
_ringbuffer = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzRemote::available()
|
||||
{
|
||||
unsigned long test;
|
||||
return xQueuePeek(_rxQueue, &test, 0) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
C433MHzRemote::read(unsigned long& val)
|
||||
{
|
||||
return xQueueReceive(_rxQueue, &val, 0) != 0;
|
||||
}
|
||||
|
||||
// Data word in NV ram is stored as follows
|
||||
//
|
||||
// | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// | | | | |
|
||||
// | | | | Enabled | Key Codes |
|
||||
// | Unique ID (20 bits) | U D R S| KCU | KCD | KCR | KCS |
|
||||
//
|
||||
// Key enabled bits
|
||||
// U = Up - key code in b7-b6
|
||||
// D = Down - key code in b5-b5
|
||||
// R = Run - key code in b3-b2
|
||||
// S = Stop - key code in b1-b0
|
||||
//
|
||||
// key code bits
|
||||
// 00 => 0x01
|
||||
// 01 => 0x02
|
||||
// 10 => 0x04
|
||||
// 11 => 0x08
|
||||
//
|
||||
void
|
||||
C433MHzRemote::_readNV()
|
||||
{
|
||||
for(int rmt=0; rmt<3; rmt++) {
|
||||
unsigned long code = NVstore.getUserSettings().UHFcode[rmt];
|
||||
for(int i=0; i<4; i++) {
|
||||
int mask = 0x100 << i;
|
||||
if(code & mask) {
|
||||
int uniqueID = (code >> 8) & 0xFFFFF0;
|
||||
int shift = (code >> (i*2)) & 0x3;
|
||||
int keyCode = 1 << shift;
|
||||
_rawCodes[rmt][i] = uniqueID | keyCode;
|
||||
}
|
||||
else
|
||||
_rawCodes[rmt][i] = 0;
|
||||
}
|
||||
DebugPort.printf("0x%08lX => 0x%08lX 0x%08lX 0x%08lX 0x%08lX\r\n", code, _rawCodes[rmt][0], _rawCodes[rmt][1], _rawCodes[rmt][2], _rawCodes[rmt][3]);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
C433MHzRemote::saveNV(unsigned long codes[3][4])
|
||||
{
|
||||
sUserSettings userSettings = NVstore.getUserSettings();
|
||||
|
||||
for(int rmt=0; rmt<3; rmt++) {
|
||||
unsigned long uniqueCode = codes[rmt][0] & 0xFFFFF0;
|
||||
|
||||
// confirm all recorded keys share the same unique code
|
||||
for(int i=1; i<4; i++) {
|
||||
if(codes[rmt][i] && (uniqueCode != (codes[rmt][i] & 0xFFFFF0))) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// start building the encoded value for NV storage
|
||||
unsigned long encoded = uniqueCode << 8;
|
||||
for(int i=0; i<4; i++) {
|
||||
if(codes[rmt][i]) {
|
||||
int keyCode = codes[rmt][i] & 0xf;
|
||||
switch(keyCode) {
|
||||
case 1:
|
||||
encoded |= (0 << i*2);
|
||||
break;
|
||||
case 2:
|
||||
encoded |= (1 << i*2);
|
||||
break;
|
||||
case 4:
|
||||
encoded |= (2 << i*2);
|
||||
break;
|
||||
case 8:
|
||||
encoded |= (3 << i*2);
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
encoded |= (0x100 << i);
|
||||
}
|
||||
}
|
||||
userSettings.UHFcode[rmt] = encoded;
|
||||
|
||||
DebugPort.printf("0x%08lX 0x%08lX 0x%08lX 0x%08lX => 0x%08lX\r\n", codes[rmt][0], codes[rmt][1], codes[rmt][2], codes[rmt][3], encoded);
|
||||
}
|
||||
|
||||
NVstore.setUserSettings(userSettings);
|
||||
NVstore.save();
|
||||
|
||||
for(int rmt=0; rmt<3; rmt++) {
|
||||
for(int i=0; i<4; i++) {
|
||||
_rawCodes[rmt][i] = codes[rmt][i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzRemote::getCodes(unsigned long codes[3][4])
|
||||
{
|
||||
for(int rmt=0; rmt<3; rmt++) {
|
||||
for(int i=0; i<4; i++) {
|
||||
codes[rmt][i] = _rawCodes[rmt][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzRemote::manage()
|
||||
{
|
||||
if(available()) {
|
||||
unsigned long code;
|
||||
read(code);
|
||||
DebugPort.printf("UHF remote code = %08lX\r\n", code);
|
||||
|
||||
if(code) { // only react to an actual code, not release
|
||||
const int IDmatch = (code << 8) & 0xfffff000;
|
||||
int rmt;
|
||||
// find a mtaching unique ID
|
||||
for(rmt=0; rmt<3; rmt++) {
|
||||
if( IDmatch == (NVstore.getUserSettings().UHFcode[rmt] & 0xfffff000) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rmt == 3)
|
||||
return; // match not found - abort
|
||||
|
||||
const int subCode = code & 0xf;
|
||||
if(subCode == (_rawCodes[rmt][0] & 0xf) ) {
|
||||
DebugPort.println("UHF stop request!");
|
||||
requestOff();
|
||||
}
|
||||
if(subCode == (_rawCodes[rmt][1] & 0xf) ) {
|
||||
DebugPort.println("UHF start request!");
|
||||
requestOn();
|
||||
}
|
||||
if(subCode == (_rawCodes[rmt][2] & 0xf) ) {
|
||||
DebugPort.println("UHF dec temp request!");
|
||||
CDemandManager::deltaDemand(-1);
|
||||
}
|
||||
if(subCode == (_rawCodes[rmt][3] & 0xf) ) {
|
||||
DebugPort.println("UHF inc temp request!");
|
||||
CDemandManager::deltaDemand(+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
C433MHzRemote::enableISR(bool state)
|
||||
{
|
||||
rmt_set_rx_intr_en(_rxCfg.channel, state);
|
||||
rmt_set_err_intr_en(_rxCfg.channel, state);
|
||||
}
|
||||
|
||||
extern C433MHzRemote UHFremote;
|
||||
|
68
src/Protocol/433MHz.h
Normal file
68
src/Protocol/433MHz.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __433MHzREMOTE_H__
|
||||
#define __433MHzREMOTE_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
class C433MHzRemote {
|
||||
protected:
|
||||
static void _staticTask(void* arg);
|
||||
rmt_config_t _rxCfg;
|
||||
RingbufHandle_t _ringbuffer;
|
||||
QueueHandle_t _rxQueue;
|
||||
TaskHandle_t _taskHandle;
|
||||
int _runState;
|
||||
unsigned long _prevCode;
|
||||
unsigned long _timeout;
|
||||
unsigned long _rawCodes[3][4];
|
||||
bool _debug;
|
||||
|
||||
void _task();
|
||||
|
||||
void _doComms();
|
||||
bool _decodeRxItems(const rmt_item32_t* rxItems, int size);
|
||||
// NV storage
|
||||
void _readNV();
|
||||
|
||||
public:
|
||||
C433MHzRemote();
|
||||
~C433MHzRemote();
|
||||
void begin(gpio_num_t pin, rmt_channel_t channel);
|
||||
void end();
|
||||
|
||||
bool available();
|
||||
bool read(unsigned long& val);
|
||||
void manage();
|
||||
|
||||
void getCodes(unsigned long codes[3][4]);
|
||||
|
||||
// NV storage
|
||||
int saveNV(unsigned long codes[3][4]);
|
||||
void enableISR(bool state);
|
||||
};
|
||||
|
||||
extern C433MHzRemote UHFremote;
|
||||
|
||||
|
||||
#endif
|
608
src/Protocol/AltController.cpp
Normal file
608
src/Protocol/AltController.cpp
Normal file
|
@ -0,0 +1,608 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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 <Arduino.h>
|
||||
#include "AltControllerTask.h"
|
||||
#include "AltController.h"
|
||||
#include "../cfg/BTCConfig.h"
|
||||
#include "../cfg/pins.h"
|
||||
#include "Protocol.h"
|
||||
#include "TxManage.h"
|
||||
// #include "SmartError.h"
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "../Utility/DataFilter.h"
|
||||
#include "../Utility/FuelGauge.h"
|
||||
#include "../Utility/HourMeter.h"
|
||||
#include "../Utility/macros.h"
|
||||
#include "../Utility/DemandManager.h"
|
||||
|
||||
#define MASK_FAN 0x0800
|
||||
#define MASK_PUMP 0x0400
|
||||
#define MASK_PLATEAU 0x0200
|
||||
#define MASK_GLOW 0x0100
|
||||
#define MASK_STOPPING 0x0080
|
||||
#define MASK_ON 0x0040
|
||||
|
||||
void matchDemand(int desired);
|
||||
|
||||
/* known command values
|
||||
0xA0 - status/ Rx code 5
|
||||
0xA1 - switch to thermo mode 'h7' - get 0x5xxx messages in thermo...
|
||||
0xA2 - power on/off
|
||||
0xA3 - increase demand (h1-h6) (also used to leave thermo mode ('h7' -> 'h1'))
|
||||
0xA4 - decrease demand (h1-h6)
|
||||
0xA5 - ??
|
||||
0xA6 - Primary get status
|
||||
0xA7 - status (alternates status and Rx code 9... when stopped)
|
||||
0xA8 - returns status
|
||||
0xA9 - returns status
|
||||
0xAA - enter pump edit mode ->0x3xxx returned / running minimum power (alternates status and Rx code 3...)
|
||||
0xAB - get voltage
|
||||
0xAC - prime
|
||||
0xAD - ?
|
||||
0xAE - toggle plateau
|
||||
0xAF - get body temp
|
||||
|
||||
0x70 - maintain power (thermostat)
|
||||
0x71 - increase power (thermostat)
|
||||
0x72 - decrease power (thermostat)
|
||||
|
||||
EDIT mode:
|
||||
when you send 0xAA and 0x3yxx is returned, you can step thru 'y' by using 0xAD
|
||||
ie 0x30xx, 0x31xx, 0x32xx, 0x33xx, 0x34xx, 0x35xx - Those seem to be the 6 set points.
|
||||
Using 0xA3 and 0xA4 you can increase or decrease 'xx' of the selected 'y' field
|
||||
0xA2 then saves the new setting (but stops the heater if running!)
|
||||
|
||||
Have not found a fan speed anywhere...
|
||||
|
||||
*/
|
||||
|
||||
int
|
||||
sAltHeaterData::errState()
|
||||
{
|
||||
/* convert fault codes from the ECU to classic codes used by more capable unit
|
||||
A02 -> E-01 Power/voltage problem Normal range: 24V (18-32V), 12V (9-16V)
|
||||
A03 -> E-04 Oil pump failure
|
||||
A04 -> E-03 Ignition plug failure
|
||||
A05 -> E-06 Fan failure
|
||||
A06 -> E-09 Body Sensor failure
|
||||
A07 -> E-10 Unsuccessful startup
|
||||
A08 -> E-05 High temperature alarm (inlet air > 50 °C; chassis > 200 ° C)
|
||||
A09 -> E-08 Flameout alarm
|
||||
|
||||
Note that classic errors must +1 of that displayed
|
||||
*/
|
||||
switch(Error) {
|
||||
case 2: return 2; // low volts -> E=01
|
||||
case 3: return 5; // pump -> E-04
|
||||
case 4: return 4; // glow plug -> E-03
|
||||
case 5: return 7; // fan -> E-06
|
||||
case 6: return 10; // sensor -> E-09
|
||||
case 7: return 11; // start fail -> E-10
|
||||
case 8: return 6; // overtemp -> E-05
|
||||
case 9: return 9; // flameout -> E-08
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sAltHeaterData::runState()
|
||||
{
|
||||
if(HeaterOn == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if(Stopping) {
|
||||
return 7;
|
||||
}
|
||||
else {
|
||||
if(GlowOn) {
|
||||
if(!PumpOn)
|
||||
return 9; // heating glow plug
|
||||
else
|
||||
return 2; // igniting
|
||||
}
|
||||
else {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
sAltHeaterData::getPumpRate()
|
||||
{
|
||||
if(PumpOn) {
|
||||
if(INBOUNDS(Demand, 0, 5) && pumpRate[Demand] > 0)
|
||||
return pumpRate[Demand] * 0.1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
sAltHeaterData::getDesiredPumpRate(int Idx)
|
||||
{
|
||||
if(INBOUNDS(Idx, 0, 5) && pumpRate[Idx] > 0)
|
||||
return pumpRate[Idx] * 0.1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::decodeThermoDemand(int rxData)
|
||||
{
|
||||
Demand = rxData & 0x07;
|
||||
thermoMode = true;
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::decodeError(int rxData)
|
||||
{
|
||||
Error = rxData & 0x0f;
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::decodePumpRate(int rxData)
|
||||
{
|
||||
int index = (rxData & 0xF00) >> 8;
|
||||
int rate = rxData & 0xFF;
|
||||
|
||||
if(INBOUNDS(index, 0, 5)) {
|
||||
pumpRate[index] = rate;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::decodeVolts(int rxData)
|
||||
{
|
||||
Volts = rxData & 0x1f;
|
||||
FilteredSamples.ipVolts.update(Volts);
|
||||
FilteredSamples.FastipVolts.update(Volts);
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::decodeBodyT(int rxData)
|
||||
{
|
||||
BodyT = (rxData & 0xFFF);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sAltHeaterData::decodeStatus(int rxData)
|
||||
{
|
||||
if(rxData & MASK_ON) {
|
||||
HeaterOn = true;
|
||||
FanOn = rxData & MASK_FAN ? true : false;
|
||||
GlowOn = rxData & MASK_GLOW ? true : false;
|
||||
PumpOn = rxData & MASK_PUMP ? true : false;
|
||||
Plateau = rxData & MASK_PLATEAU ? true : false;
|
||||
Stopping = rxData & MASK_STOPPING ? true : false;
|
||||
int dmd = rxData & 0x07;
|
||||
if(dmd != 6) {
|
||||
Demand = dmd;
|
||||
thermoMode = false;
|
||||
}
|
||||
else {
|
||||
thermoMode = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
HeaterOn = false;
|
||||
FanOn = false;
|
||||
GlowOn = false;
|
||||
PumpOn = false;
|
||||
Plateau = false;
|
||||
Stopping = false;
|
||||
Error = 0;
|
||||
Volts = rxData & 0x1f;
|
||||
thermoMode = false;
|
||||
|
||||
FilteredSamples.ipVolts.update(Volts);
|
||||
FilteredSamples.FastipVolts.update(Volts);
|
||||
}
|
||||
}
|
||||
|
||||
sAltHeaterData::sAltHeaterData()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::init()
|
||||
{
|
||||
thermoMode = false;
|
||||
Active = 0;
|
||||
HeaterOn = false;
|
||||
Stopping = false;
|
||||
GlowOn = false;
|
||||
FanOn = false;
|
||||
PumpOn = false;
|
||||
Plateau = false;
|
||||
Demand = 0;
|
||||
BodyT = -1;
|
||||
Volts = 0;
|
||||
Error = 0;
|
||||
for(int i=0; i<6; i++) pumpRate[i] = -1;
|
||||
}
|
||||
|
||||
float
|
||||
sAltHeaterData::getBodyTemp()
|
||||
{
|
||||
return BodyT; // TODO: map to real world somehow
|
||||
}
|
||||
|
||||
void
|
||||
sAltHeaterData::report()
|
||||
{
|
||||
char msg[80];
|
||||
sprintf(msg, "On:%d Fan:%d Glow:%d Pump:%d Plateau:%d Demand:%d Battery:%d BodyT:%d\r\n",
|
||||
HeaterOn,
|
||||
FanOn,
|
||||
GlowOn,
|
||||
PumpOn,
|
||||
Plateau,
|
||||
Demand,
|
||||
Volts,
|
||||
BodyT);
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_decode(int rxData)
|
||||
{
|
||||
unsigned int ID = rxData >> 12;
|
||||
switch(ID) {
|
||||
case 0x0: break;
|
||||
case 0x1: break;
|
||||
case 0x2: break;
|
||||
case 0x3:
|
||||
_htrData.decodePumpRate(rxData);
|
||||
break; // 0x3yxx y = H1(0) - h6(5) xx = Hz x10
|
||||
case 0x4:
|
||||
_htrData.decodeBodyT(rxData);
|
||||
break;
|
||||
case 0x5:
|
||||
_htrData.decodeThermoDemand(rxData);
|
||||
break; // heat bars on OEM LCD in thermo mode 0=1 bar
|
||||
case 0x6:
|
||||
_htrData.decodeStatus(rxData);
|
||||
break;
|
||||
case 0x7: break;
|
||||
case 0x8:
|
||||
_htrData.decodeError(rxData);
|
||||
break;
|
||||
case 0x9: break; // 0x9009 in response to A7 when stopped...
|
||||
case 0xa:
|
||||
_htrData.decodeVolts(rxData);
|
||||
break;
|
||||
case 0xb: break;
|
||||
case 0xc: break;
|
||||
case 0xd: break;
|
||||
case 0xe: break;
|
||||
case 0xf: break;
|
||||
}
|
||||
_htrData.Active = millis() + 5000;
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::reportDecode()
|
||||
{
|
||||
_htrData.report();
|
||||
}
|
||||
|
||||
float
|
||||
CAltCommsTask::getFanRPM()
|
||||
{
|
||||
return _htrData.FanOn ? -1 : 0;
|
||||
}
|
||||
|
||||
float
|
||||
CAltCommsTask::getActualPumpRate()
|
||||
{
|
||||
return _htrData.getPumpRate();
|
||||
}
|
||||
|
||||
float
|
||||
CAltCommsTask::getDesiredPumpRate(int idx)
|
||||
{
|
||||
return _htrData.getDesiredPumpRate(idx);
|
||||
}
|
||||
|
||||
float
|
||||
CAltCommsTask::getGlow()
|
||||
{
|
||||
return _htrData.GlowOn ? -1 : 0;
|
||||
}
|
||||
|
||||
float
|
||||
CAltCommsTask::getBodyTemp()
|
||||
{
|
||||
return _htrData.getBodyTemp();
|
||||
}
|
||||
|
||||
int
|
||||
CAltCommsTask::getRunState()
|
||||
{
|
||||
return _htrData.runState();
|
||||
}
|
||||
|
||||
int
|
||||
CAltCommsTask::getErrState()
|
||||
{
|
||||
return _htrData.errState();
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::reqPower()
|
||||
{
|
||||
putTxQueue(0xA2);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_reqStatus()
|
||||
{
|
||||
putTxQueue(0xA6);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_reqVolts()
|
||||
{
|
||||
putTxQueue(0xAB);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CAltCommsTask::_reqBodyT()
|
||||
{
|
||||
putTxQueue(0xAF);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_reqDemand(int dir)
|
||||
{
|
||||
if(_htrData.thermoMode)
|
||||
putTxQueue(0xA1); // switch from thermo mode
|
||||
|
||||
if(dir == 0)
|
||||
putTxQueue(0xA6);
|
||||
else if(dir > 0)
|
||||
putTxQueue(0xA3);
|
||||
else
|
||||
putTxQueue(0xA4);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_reqThermo(int delta)
|
||||
{
|
||||
if(!_htrData.thermoMode)
|
||||
putTxQueue(0xA1); // switch to thermo mode
|
||||
|
||||
if(delta == 0)
|
||||
putTxQueue(0x70);
|
||||
else if(delta > 0)
|
||||
putTxQueue(0x71);
|
||||
else
|
||||
putTxQueue(0x72);
|
||||
}
|
||||
|
||||
void checkAltTxEvents()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::manage()
|
||||
{
|
||||
static bool flipflop = false;
|
||||
if(_connState == 0) {
|
||||
_doStartupProbe();
|
||||
}
|
||||
|
||||
else if(_connState == 1) {
|
||||
long tDelta = xTaskGetTickCount() - _tPause;
|
||||
if(tDelta >= 0) {
|
||||
_tPause += 1000;
|
||||
|
||||
if(CDemandManager::isThermostat()) {
|
||||
_doThermo();
|
||||
}
|
||||
else {
|
||||
// _reqStatus();
|
||||
// _reqDemand(0);
|
||||
_matchDemand(CDemandManager::getPumpHz());
|
||||
}
|
||||
|
||||
if(_htrData.HeaterOn) {
|
||||
if((flipflop = !flipflop))
|
||||
_reqVolts();
|
||||
else
|
||||
_reqBodyT();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_doStartupProbe()
|
||||
{
|
||||
// interrogate the ehater and extract the fixed pump rates for each heat demand setting H1->H6
|
||||
if(_startup.state == 0) {
|
||||
// erase pump rate record
|
||||
_htrData.init();
|
||||
_startup.pumpIdx = 0;
|
||||
putTxQueue(0xAA); // request initial pump rate
|
||||
_tPause = xTaskGetTickCount() + 600;
|
||||
_startup.state++;
|
||||
}
|
||||
|
||||
else if(_startup.state == 1) {
|
||||
if(_htrData.pumpRate[_startup.pumpIdx] > 0) {
|
||||
_startup.state++;
|
||||
}
|
||||
else {
|
||||
long tDelta = xTaskGetTickCount() - _tPause;
|
||||
if(tDelta >= 0) {
|
||||
_startup.state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(_startup.state == 2) {
|
||||
if(_htrData.pumpRate[5] < 0) {
|
||||
_startup.pumpIdx++;
|
||||
if(_startup.pumpIdx == 6) {
|
||||
_startup.state = 0;
|
||||
}
|
||||
else {
|
||||
putTxQueue(0xAD); // req next pump rate
|
||||
_tPause = xTaskGetTickCount() + 600;
|
||||
_startup.state++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
putTxQueue(0xAA); // finished - leave
|
||||
_tPause = xTaskGetTickCount() + 600;
|
||||
_connState = 1;
|
||||
}
|
||||
}
|
||||
|
||||
else if(_startup.state == 3) {
|
||||
if(_htrData.pumpRate[_startup.pumpIdx] > 0) {
|
||||
_startup.state = 2;
|
||||
}
|
||||
else {
|
||||
long tDelta = xTaskGetTickCount() - _tPause;
|
||||
if(tDelta >= 0) {
|
||||
_startup.state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CAltCommsTask::checkEvents()
|
||||
{
|
||||
int rxHeater;
|
||||
if(AltCommsTask.getRxQueue(rxHeater)) {
|
||||
_decode(rxHeater);
|
||||
}
|
||||
isActive();
|
||||
|
||||
manage();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CAltCommsTask::isActive() {
|
||||
if(_htrData.Active) {
|
||||
long tDelta = millis() - _htrData.Active;
|
||||
if(tDelta > 0) {
|
||||
_htrData.Active = 0;
|
||||
}
|
||||
}
|
||||
return _htrData.Active != 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CAltCommsTask::_doThermo()
|
||||
{
|
||||
static int demandMemory = 5;
|
||||
uint8_t ThermostatMode = NVstore.getUserSettings().ThermostatMethod; // get the METHOD of thermostat control
|
||||
float Window = NVstore.getUserSettings().ThermostatWindow;
|
||||
float tCurrent = getTemperatureSensor();
|
||||
float tDesired = float(CDemandManager::getDegC());
|
||||
float tDelta = tCurrent - tDesired;
|
||||
Window /= 2;
|
||||
switch(ThermostatMode) {
|
||||
|
||||
case 3: // GPIO controlled thermostat mode
|
||||
if(CDemandManager::isExtThermostatMode()) {
|
||||
if(CDemandManager::isExtThermostatOn()) {
|
||||
_matchDemand(5);
|
||||
}
|
||||
else {
|
||||
_matchDemand(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// deliberately fall through if not enabled for GPIO control to standard thermostat
|
||||
// |
|
||||
// V
|
||||
case 0: // conventional heater controlled thermostat mode
|
||||
Window = 1.0;
|
||||
// deliberately fall through with +-1C window
|
||||
// |
|
||||
// V
|
||||
case 1: // heater controlled thermostat mode - BUT actual temp is tweaked via a changed window
|
||||
// if(fabs(tDelta) < Window) {
|
||||
// _reqThermo(0); // hold at desired if inside window
|
||||
// }
|
||||
// else if(tDelta < -Window) {
|
||||
// _reqThermo(+1);
|
||||
// }
|
||||
// else {
|
||||
// _reqThermo(-1);
|
||||
// }
|
||||
if(tDelta >= Window) {
|
||||
demandMemory = 0; // flip flop to minimum power when over +ve threshold
|
||||
}
|
||||
else if(tDelta <= -Window){
|
||||
demandMemory = 5; // flip flop to maximum power when under -ve threshold
|
||||
}
|
||||
_matchDemand(demandMemory);
|
||||
break;
|
||||
|
||||
case 2: // BTC controlled thermostat mode
|
||||
// map linear deviation within thermostat window to a Hz value,
|
||||
// Hz mode however uses the desired temperature field, somewhere between 8 - 35 for min/max
|
||||
// so create a desired "temp" according the the current hystersis
|
||||
if(fabs(tDelta) < Window/2) {
|
||||
_matchDemand(3);
|
||||
}
|
||||
else if(fabs(tDelta) < Window) {
|
||||
_matchDemand((tDelta > 0) ? 2 : 4);
|
||||
}
|
||||
else {
|
||||
_matchDemand((tDelta > 0) ? 0 : 5);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
_matchDemand(5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_matchDemand(int desired)
|
||||
{
|
||||
if(_htrData.Demand == desired)
|
||||
_reqDemand(0);
|
||||
else if(_htrData.Demand > desired)
|
||||
_reqDemand(-1);
|
||||
else
|
||||
_reqDemand(+1);
|
||||
}
|
60
src/Protocol/AltController.h
Normal file
60
src/Protocol/AltController.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __ALTCONTROLLER_H__
|
||||
#define __ALTCONTROLLER_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
|
||||
// struct sAltHeaterData {
|
||||
// unsigned long Active;
|
||||
// bool On;
|
||||
// bool Stopping;
|
||||
// bool Glow;
|
||||
// bool Fan;
|
||||
// bool Pump;
|
||||
// bool Plateau;
|
||||
// int Demand;
|
||||
// int BodyT;
|
||||
// int Volts;
|
||||
// int Error;
|
||||
// int pumpRate[6];
|
||||
// sAltHeaterData();
|
||||
// int runState();
|
||||
// int errState();
|
||||
// float getPumpRate();
|
||||
// };
|
||||
|
||||
// void decodeAltHeater(int rxdata);
|
||||
|
||||
// void reqAltPower();
|
||||
// void reqAltStatus();
|
||||
// void reqAltVolts();
|
||||
// void reqAltBodyT();
|
||||
// bool isAltActive();
|
||||
// void checkAltRxEvents();
|
||||
// void checkAltTxEvents();
|
||||
|
||||
|
||||
|
||||
// extern sAltHeaterData AltHeaterData;
|
||||
|
||||
#endif
|
311
src/Protocol/AltControllerTask.cpp
Normal file
311
src/Protocol/AltControllerTask.cpp
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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 "CommsTask.h"
|
||||
#include "AltControllerTask.h"
|
||||
#include "AltController.h"
|
||||
#include "../cfg/BTCConfig.h"
|
||||
#include "../cfg/pins.h"
|
||||
#include "Protocol.h"
|
||||
#include "TxManage.h"
|
||||
// #include "SmartError.h"
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "../Utility/DataFilter.h"
|
||||
#include "../Utility/FuelGauge.h"
|
||||
#include "../Utility/HourMeter.h"
|
||||
#include "../Utility/macros.h"
|
||||
|
||||
#define RX_DATA_TIMOUT 50
|
||||
// #define ALTCTRL_DEBUG
|
||||
|
||||
|
||||
char altdbgMsg[COMMS_MSGQUEUESIZE];
|
||||
|
||||
CAltCommsTask AltCommsTask; // AltCommsTaskInfo;
|
||||
|
||||
volatile int nRunAltController = 0;
|
||||
volatile bool bAltCommsOnline = false;
|
||||
|
||||
void buildTxItems(int val, rmt_item32_t txItems[9]);
|
||||
|
||||
rmt_channel_t CAltCommsTask::__txChannel;
|
||||
volatile int CAltCommsTask::_txPending = 0;
|
||||
|
||||
CAltCommsTask::CAltCommsTask() : CCommsTask()
|
||||
{
|
||||
_connState = 0;
|
||||
_startup.state = 0;
|
||||
_tPause = 0;
|
||||
_startup.pumpIdx = 0;
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::taskStart()
|
||||
{
|
||||
CCommsTask::taskStart();
|
||||
_runState = 0;
|
||||
xTaskCreate(commsTask,
|
||||
"AltCtrlrTask",
|
||||
4000,
|
||||
this,
|
||||
TASK_PRIORITY_HEATERCOMMS,
|
||||
&_taskHandle);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::commsTask(void* arg) {
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Alternate controller data reception
|
||||
//
|
||||
CAltCommsTask* pThis = (CAltCommsTask*)arg;
|
||||
|
||||
pThis->_task();
|
||||
|
||||
vTaskDelete(NULL); // NEVER fall out from a task!
|
||||
for(;;);
|
||||
}
|
||||
|
||||
void
|
||||
CAltCommsTask::_task()
|
||||
{ // create FreeRTOS queues etc
|
||||
// pThis->create(ALTCTRL_DATAQUEUESIZE);
|
||||
create(ALTCTRL_DATAQUEUESIZE);
|
||||
|
||||
pinMode(Tx1Pin, OUTPUT);
|
||||
pinMode(Rx1Pin, INPUT_PULLUP);
|
||||
pinMode(TxEnbPin, OUTPUT);
|
||||
|
||||
// RMT Tx configuration
|
||||
_txCfg.rmt_mode = RMT_MODE_TX;
|
||||
_txCfg.channel = __txChannel = RMT_CHANNEL_2;
|
||||
_txCfg.gpio_num = Tx1Pin;
|
||||
_txCfg.mem_block_num = 1;
|
||||
_txCfg.tx_config.loop_en = 0;
|
||||
_txCfg.tx_config.idle_output_en = 1;
|
||||
_txCfg.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
|
||||
_txCfg.tx_config.carrier_en = 0;
|
||||
_txCfg.tx_config.carrier_duty_percent = 50;
|
||||
_txCfg.tx_config.carrier_freq_hz = 10000;
|
||||
_txCfg.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
_txCfg.clk_div = 80; // 1us / tick
|
||||
|
||||
// RMT Rx configuration
|
||||
_rxCfg.rmt_mode = RMT_MODE_RX;
|
||||
_rxCfg.channel = RMT_CHANNEL_3;
|
||||
_rxCfg.gpio_num = Rx1Pin;
|
||||
_rxCfg.mem_block_num = 1;
|
||||
_rxCfg.clk_div = 80; // 1us / clock
|
||||
_rxCfg.rx_config.filter_en = true;
|
||||
_rxCfg.rx_config.filter_ticks_thresh = 100;
|
||||
_rxCfg.rx_config.idle_threshold = 32000; // > 32ms no transitions => end of Rx
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&_txCfg));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(_txCfg.channel, 0, 0));
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&_rxCfg));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(_rxCfg.channel, 512, 0));
|
||||
// ringbuffer for rx
|
||||
ESP_ERROR_CHECK(rmt_get_ringbuf_handle(_rxCfg.channel, &_ringbuffer));
|
||||
|
||||
// setup call back to terminate tx gate
|
||||
rmt_register_tx_end_callback(RmtSendDone, this);
|
||||
// rmt_register_tx_end_callback(RmtSendDone, pThis);
|
||||
|
||||
_runState = 1;
|
||||
putTxQueue(0xA6); // initial poll to detect heater
|
||||
while(_runState == 1) {
|
||||
|
||||
int command;
|
||||
if(getTxQueue(command)) {
|
||||
doComms(command);
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
// pThis->_runState = 1;
|
||||
// while(pThis->_runState == 1) {
|
||||
|
||||
// int command;
|
||||
// if(pThis->getTxQueue(command)) {
|
||||
// pThis->doComms(command);
|
||||
// }
|
||||
// delay(1);
|
||||
// }
|
||||
|
||||
// disconnect from RMT peripheral
|
||||
rmt_register_tx_end_callback(NULL, NULL);
|
||||
ESP_ERROR_CHECK(rmt_driver_uninstall(_txCfg.channel));
|
||||
ESP_ERROR_CHECK(rmt_driver_uninstall(_rxCfg.channel));
|
||||
_ringbuffer = NULL;
|
||||
|
||||
// return pins to standard GPIO functions
|
||||
pinMode(Tx1Pin, OUTPUT);
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
pinMode(TxEnbPin, OUTPUT);
|
||||
digitalWrite(Tx1Pin, HIGH);
|
||||
digitalWrite(TxEnbPin, LOW);
|
||||
|
||||
// pThis->_runState = 0;
|
||||
_runState = 0;
|
||||
}
|
||||
|
||||
// static callback for end of RMT transmission - used to terminate Tx Gate pulse
|
||||
void
|
||||
CAltCommsTask::RmtSendDone(rmt_channel_t channel, void *arg)
|
||||
{
|
||||
if(channel == __txChannel) {
|
||||
gpio_set_level(TxEnbPin, 0);
|
||||
_txPending = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CAltCommsTask::doComms(int command)
|
||||
{
|
||||
// ensure the ringbuffer is cleared
|
||||
void *p;
|
||||
size_t s;
|
||||
while ((p = xRingbufferReceive(_ringbuffer, &s, 0)))
|
||||
{
|
||||
ESP_LOGD(TAG, "flushing entry");
|
||||
vRingbufferReturnItem(_ringbuffer, p);
|
||||
}
|
||||
|
||||
// send the command, and wait for a response
|
||||
rmt_item32_t txItems[9];
|
||||
buildTxItems(command, txItems); // create transition list
|
||||
digitalWrite(TxEnbPin, HIGH); // enable Tx gate
|
||||
|
||||
NVstore.takeSemaphore(); // an issue with RmtSDendDone occuring during NV saves exists - block NV saves whilst we send
|
||||
_txPending = 1;
|
||||
rmt_write_items(_txCfg.channel, txItems, 9, 0); // send the transitions
|
||||
|
||||
// await reception, TxGate holds Rx high during Tx
|
||||
rmt_rx_start(_rxCfg.channel, true);
|
||||
|
||||
// wait for ring buffer response, or time out
|
||||
size_t rx_size;
|
||||
int toCount = 50;
|
||||
rmt_item32_t* rxItems = NULL;
|
||||
while(--toCount) {
|
||||
rxItems = (rmt_item32_t *)xRingbufferReceive(_ringbuffer, &rx_size, 10);
|
||||
if(_txPending == 2) {
|
||||
NVstore.giveSemaphore();
|
||||
_txPending = 0;
|
||||
}
|
||||
if(rxItems != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if(_txPending) {
|
||||
NVstore.giveSemaphore();
|
||||
_txPending = 0;
|
||||
}
|
||||
|
||||
#ifdef ALTCTRL_DEBUG
|
||||
Serial.printf("RxItems = %d\r\n", rx_size/4);
|
||||
for(int i=0; i<rx_size/4; i++) {
|
||||
Serial.printf("[%d] %d:%5d %d:%5d\r\n", i, rxItems[i].level0, rxItems[i].duration0, rxItems[i].level1, rxItems[i].duration1);
|
||||
}
|
||||
#endif
|
||||
if (rxItems) {
|
||||
bAltCommsOnline |= decodeRxItems(rxItems, rx_size / 4);
|
||||
vRingbufferReturnItem(_ringbuffer, (void *)rxItems);
|
||||
}
|
||||
rmt_rx_stop(_rxCfg.channel);
|
||||
}
|
||||
|
||||
|
||||
void buildTxItems(int val, rmt_item32_t txItems[9])
|
||||
{
|
||||
txItems[0].duration0 = 30000; // 30ms low sync pulse
|
||||
txItems[0].level0 = 0;
|
||||
|
||||
int mask = 0x80;
|
||||
for(int i = 0; i <8 ; i++) {
|
||||
txItems[i].duration1 = mask & val ? 8000 : 4000; // HIGH interval: 8ms for a 1, 4ms for a zero
|
||||
txItems[i].level1 = 1;
|
||||
txItems[i+1].duration0 = mask & val ? 4000 : 8000; // LOW interval: 4ms for a 1, 8ms for a zero
|
||||
txItems[i+1].level0 = 0;
|
||||
mask >>= 1;
|
||||
}
|
||||
txItems[8].duration1 = 250; // 250us to drive line high - not relying upon pull up so much
|
||||
txItems[8].level1 = 1;
|
||||
}
|
||||
|
||||
bool
|
||||
CAltCommsTask::decodeRxItems(rmt_item32_t* rxItems, int size)
|
||||
{
|
||||
int read_data = 0;
|
||||
if (size >= 17)
|
||||
{
|
||||
|
||||
// _ __ __ _ __ _ __________________
|
||||
// |________| x|_| x|_| |x_| x|_| |x_|~~~~~~~|_|
|
||||
// . . . . . .
|
||||
// Start '1' '1' '0' '1' '0' ..... Last
|
||||
//
|
||||
// Start is held in rxItems[0].duration0 & level0, it is ~30ms long
|
||||
// Each data bit is always ~12ms long
|
||||
// a '1' is 8ms high, 4ms low
|
||||
// a '0' is 4ms high, 8ms low
|
||||
// Data bits are held in rxItems[n].level1 for 1st part & rxItems[n+1].duration0 for 2nd half
|
||||
|
||||
// confirm valid start
|
||||
if(rxItems[0].level0 == 0 && INBOUNDS(rxItems[0].duration0, 29500, 30500)) {
|
||||
// start OK, now read the 16 bit payload
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
read_data <<= 1;
|
||||
|
||||
// total bit time should be ~12ms
|
||||
int bitTime = rxItems[i].duration1 + rxItems[i+1].duration0; // add 1st and 2nd part times
|
||||
if(INBOUNDS(bitTime, 11500, 12500) // confirm duration
|
||||
&& rxItems[i].level1 == 1 // confirm 1st part is high
|
||||
&& rxItems[i+1].level0 == 0) // confirm 2nd part is low
|
||||
{
|
||||
// OK, a 1 is accepted if high > 6ms
|
||||
if(rxItems[i].duration1 > 6000)
|
||||
read_data |= 0x0001;
|
||||
}
|
||||
else {
|
||||
sprintf(altdbgMsg, "Alt controller @%d bitTime=%d lvl1=%d lvl0=%d?\r\n", i, bitTime, rxItems[i].level1, rxItems[i+1].level0);
|
||||
putMsgQueue(altdbgMsg);
|
||||
read_data = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
strcpy(altdbgMsg, "Alt Controller decode Invalid start pulse\r\n");
|
||||
putMsgQueue(altdbgMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// sprintf(altdbgMsg, "Alt controller read: 0x%04X\r\n", read_data);
|
||||
// putMsgQueue(altdbgMsg);
|
||||
|
||||
_online = true;
|
||||
putRxQueue(read_data);
|
||||
xSemaphoreGive(_semaphore);
|
||||
return true;
|
||||
}
|
||||
|
131
src/Protocol/AltControllerTask.h
Normal file
131
src/Protocol/AltControllerTask.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __ALTCTRLTASK_H__
|
||||
#define __ALTCTRLTASK_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "CommsTask.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
struct sAltHeaterData {
|
||||
private:
|
||||
int BodyT;
|
||||
public:
|
||||
bool thermoMode;
|
||||
unsigned long Active;
|
||||
bool HeaterOn;
|
||||
bool Stopping;
|
||||
bool GlowOn;
|
||||
bool FanOn;
|
||||
bool PumpOn;
|
||||
bool Plateau;
|
||||
int Demand;
|
||||
int Volts;
|
||||
int Error;
|
||||
int pumpRate[6];
|
||||
sAltHeaterData();
|
||||
void init();
|
||||
int runState();
|
||||
int errState();
|
||||
float getDesiredPumpRate(int idx);
|
||||
float getPumpRate();
|
||||
float getBodyTemp();
|
||||
void decodeBodyT(int rxData);
|
||||
void decodeVolts(int rxData);
|
||||
void decodeStatus(int rxData);
|
||||
void decodePumpRate(int rxData);
|
||||
void decodeThermoDemand(int rxData);
|
||||
void decodeError(int rxData);
|
||||
void report();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CAltCommsTask : public CCommsTask {
|
||||
protected:
|
||||
static void commsTask(void* arg);
|
||||
static void RmtSendDone(rmt_channel_t channel, void *arg);
|
||||
static rmt_channel_t __txChannel;
|
||||
volatile static int _txPending;
|
||||
rmt_config_t _txCfg;
|
||||
rmt_config_t _rxCfg;
|
||||
RingbufHandle_t _ringbuffer;
|
||||
|
||||
void _task();
|
||||
|
||||
void doComms(int command);
|
||||
bool decodeRxItems(rmt_item32_t* rxItems, int size);
|
||||
sAltHeaterData _htrData;
|
||||
int _connState;
|
||||
unsigned long _tPause;
|
||||
struct {
|
||||
int state;
|
||||
int pumpIdx;
|
||||
} _startup;
|
||||
|
||||
void _doStartupProbe();
|
||||
void _decode(int rxData); // interpret data word received from heater
|
||||
void _reqStatus();
|
||||
void _reqVolts();
|
||||
void _reqBodyT();
|
||||
void _reqDemand(int dir);
|
||||
void _reqThermo(int delta);
|
||||
void _doThermo();
|
||||
void _matchDemand(int desired);
|
||||
|
||||
public:
|
||||
CAltCommsTask();
|
||||
void taskStart();
|
||||
|
||||
void manage();
|
||||
void checkEvents();
|
||||
bool isActive(); // comms active and connected
|
||||
void reqPower();
|
||||
void reportDecode();
|
||||
|
||||
float getFanRPM();
|
||||
float getDesiredPumpRate(int idx);
|
||||
float getActualPumpRate();
|
||||
float getGlow();
|
||||
float getBodyTemp();
|
||||
int getRunState();
|
||||
int getErrState();
|
||||
|
||||
void putTxQueue(int command) {
|
||||
CCommsTask::putTxQueue(&command);
|
||||
}
|
||||
bool getTxQueue(int& command) {
|
||||
return CCommsTask::getTxQueue(&command);
|
||||
}
|
||||
void putRxQueue(int response) {
|
||||
CCommsTask::putRxQueue(&response);
|
||||
}
|
||||
bool getRxQueue(int& response) {
|
||||
return CCommsTask::getRxQueue(&response);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern CAltCommsTask AltCommsTask;
|
||||
|
||||
|
||||
#endif
|
124
src/Protocol/CommsTask.h
Normal file
124
src/Protocol/CommsTask.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __COMMSTASK_H__
|
||||
#define __COMMSTASK_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
|
||||
const int COMMS_MSGQUEUESIZE = 192;
|
||||
const int ALTCTRL_DATAQUEUESIZE = sizeof(int);
|
||||
|
||||
extern void AltControllerTask(void*);
|
||||
|
||||
class CCommsTask {
|
||||
protected:
|
||||
TaskHandle_t _taskHandle;
|
||||
QueueHandle_t _msgQueue;
|
||||
QueueHandle_t _rxQueue;
|
||||
QueueHandle_t _txQueue;
|
||||
SemaphoreHandle_t _semaphore;
|
||||
volatile int _runState;
|
||||
volatile bool _online;
|
||||
|
||||
public:
|
||||
public:
|
||||
CCommsTask() {
|
||||
_taskHandle = NULL;
|
||||
_msgQueue = NULL;
|
||||
_rxQueue = NULL;
|
||||
_txQueue = NULL;
|
||||
_semaphore = NULL;
|
||||
_runState = 0;
|
||||
_online = false;
|
||||
}
|
||||
virtual ~CCommsTask() {
|
||||
taskStop();
|
||||
destroy();
|
||||
}
|
||||
void create(int dataElementSize) {
|
||||
if(_msgQueue == NULL) _msgQueue = xQueueCreate(20, COMMS_MSGQUEUESIZE);
|
||||
if(_rxQueue == NULL) _rxQueue = xQueueCreate(4, dataElementSize);
|
||||
if(_txQueue == NULL) _txQueue = xQueueCreate(4, dataElementSize);
|
||||
if(_semaphore == NULL) _semaphore = xSemaphoreCreateBinary();
|
||||
}
|
||||
void destroy() {
|
||||
vQueueDelete(_msgQueue); _msgQueue = NULL;
|
||||
vQueueDelete(_rxQueue); _rxQueue = NULL;
|
||||
vQueueDelete(_txQueue); _txQueue = NULL;
|
||||
vSemaphoreDelete(_semaphore); _semaphore = NULL;
|
||||
}
|
||||
void putTxQueue(void* pData) {
|
||||
if(_txQueue)
|
||||
xQueueSend(_txQueue, pData, 0);
|
||||
}
|
||||
bool getTxQueue(void* pData) {
|
||||
if(_txQueue && xQueueReceive(_txQueue, pData, 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void putRxQueue(void* pData) {
|
||||
if(_rxQueue)
|
||||
xQueueSend(_rxQueue, pData, 0);
|
||||
}
|
||||
bool getRxQueue(void* pData) {
|
||||
if(_rxQueue && xQueueReceive(_rxQueue, pData, 0))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
void putMsgQueue(const char msg[COMMS_MSGQUEUESIZE]) {
|
||||
if(_msgQueue)
|
||||
xQueueSend(_msgQueue, msg, 0);
|
||||
}
|
||||
bool getMsgQueue(char msg[COMMS_MSGQUEUESIZE]) {
|
||||
if(_msgQueue && xQueueReceive(_msgQueue, msg, 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
TaskHandle_t getTaskHandle() const {
|
||||
return _taskHandle;
|
||||
}
|
||||
SemaphoreHandle_t getSemaphore() const {
|
||||
return _semaphore;
|
||||
}
|
||||
virtual void taskStart() {
|
||||
_online = false;
|
||||
}
|
||||
virtual void taskStop() // create task to run blue wire interface
|
||||
{
|
||||
DebugPort.printf("Stopping comms task %d\r\n", _runState);
|
||||
if(_runState == 1) { // check task is running
|
||||
_runState = 2; // ask task to stop
|
||||
DebugPort.println("Stopping comms task wait");
|
||||
while(_runState != 0) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
_taskHandle = NULL;
|
||||
}
|
||||
}
|
||||
bool isOnline() const {
|
||||
return _online;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
396
src/Protocol/HeaterManager.cpp
Normal file
396
src/Protocol/HeaterManager.cpp
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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 <FreeRTOS.h>
|
||||
#include "HeaterManager.h"
|
||||
#include "../Utility/helpers.h"
|
||||
#include "../Utility/macros.h"
|
||||
#include "../Protocol/Protocol.h"
|
||||
#include "Protocol/TxManage.h"
|
||||
#include "Protocol/SmartError.h"
|
||||
#include "Utility/NVStorage.h"
|
||||
|
||||
CHeaterManager HeaterManager;
|
||||
|
||||
|
||||
const char* Runstates [] PROGMEM = {
|
||||
" Stopped/Ready ", // 0
|
||||
"Starting...", // 1
|
||||
"Igniting...", // 2
|
||||
"Ignition retry pause", // 3
|
||||
"Ignited", // 4
|
||||
"Running", // 5
|
||||
"Stopping", // 6
|
||||
"Shutting down", // 7
|
||||
"Cooling", // 8
|
||||
"Heating glow plug", // 9 - interpreted state - actually runstate 2 with no pump action!
|
||||
"Suspended", // 10 - interpreted state - cyclic mode has suspended heater
|
||||
"Suspending...", // 11 - interpreted state - cyclic mode is suspending heater
|
||||
"Suspend cooling", // 12 - interpreted state - cyclic mode is suspending heater
|
||||
"Unknown run state"
|
||||
};
|
||||
|
||||
const char* Errstates [] PROGMEM = {
|
||||
"", // [0]
|
||||
"", // [1]
|
||||
"Low voltage", // [2] E-01
|
||||
"High voltage", // [3] E-02
|
||||
"Glow plug fault", // [4] E-03
|
||||
"Pump fault", // [5] E-04
|
||||
"Overheat", // [6] E-05
|
||||
"Motor fault", // [7] E-06
|
||||
"Comms fault", // [8] E-07
|
||||
"Flame out", // [9] E-08
|
||||
"Temp sense", // [10] E-09
|
||||
"Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5
|
||||
"Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
|
||||
"Excess fuel usage", // [13] E-12 SmartError manufactured state - excess fuel consumed
|
||||
"Unknown error?" // mystery code!
|
||||
};
|
||||
|
||||
const char* ErrstatesEx [] PROGMEM = {
|
||||
"E-00: OK", // [0]
|
||||
"E-00: OK", // [1]
|
||||
"E-01: Low voltage", // [2] E-01
|
||||
"E-02: High voltage", // [3] E-02
|
||||
"E-03: Glow plug fault", // [4] E-03
|
||||
"E-04: Pump fault", // [5] E-04
|
||||
"E-05: Overheat", // [6] E-05
|
||||
"E-06: Motor fault", // [7] E-06
|
||||
"E-07: No heater comms", // [8] E-07
|
||||
"E-08: Flame out", // [9] E-08
|
||||
"E-09: Temp sense", // [10] E-09
|
||||
"E-10: Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5
|
||||
"E-11: Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
|
||||
"E-12: Excess fuel shutdown", // [13] E-12 SmartError manufactured state - excess fuel consumed
|
||||
"Unknown error?" // mystery code!
|
||||
};
|
||||
|
||||
|
||||
CHeaterManager::CHeaterManager() {
|
||||
_heaterStyle = -1;
|
||||
_taskHandle = NULL;
|
||||
_pCommsTask = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
CHeaterManager::detect()
|
||||
{
|
||||
int style = NVstore.getHeaterTuning().heaterStyle;
|
||||
DebugPort.printf("Initial heater style %d\r\n", style);
|
||||
|
||||
bool tested[2] = { false, false};
|
||||
bool found = false;
|
||||
|
||||
for(;;) {
|
||||
if(tested[style]) {
|
||||
setHeaterStyle(NVstore.getHeaterTuning().heaterStyle); // revert to saved heater type for now, but we did not find it
|
||||
return false;
|
||||
}
|
||||
|
||||
tested[style] = true;
|
||||
setHeaterStyle(style);
|
||||
unsigned long timeout = millis() + 1500;
|
||||
|
||||
long tDelta;
|
||||
do {
|
||||
delay(10);
|
||||
if(_pCommsTask && _pCommsTask->isOnline()) {
|
||||
found = true;
|
||||
sHeaterTuning tuning = NVstore.getHeaterTuning();
|
||||
DebugPort.printf("Found heater style %d\r\n", style);
|
||||
if(tuning.heaterStyle != style) {
|
||||
DebugPort.printf("saving heater style %d\r\n", style);
|
||||
tuning.heaterStyle = style;
|
||||
NVstore.setHeaterTuning(tuning);
|
||||
NVstore.save();
|
||||
// NVstore.doSave();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
tDelta = millis() - timeout;
|
||||
} while(tDelta < 0);
|
||||
|
||||
style++;
|
||||
WRAPLIMITS(style, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterManager::setHeaterStyle(int mode)
|
||||
{
|
||||
if(INBOUNDS(mode, 0, 1)) {
|
||||
if(mode != _heaterStyle) {
|
||||
|
||||
|
||||
// stop existing comms connection if running
|
||||
if(_pCommsTask) {
|
||||
_pCommsTask->taskStop();
|
||||
_taskHandle = NULL;
|
||||
_pCommsTask = NULL;
|
||||
}
|
||||
|
||||
// start comms connection
|
||||
_heaterStyle = mode;
|
||||
switch(_heaterStyle) {
|
||||
case 0:
|
||||
_pCommsTask = &BlueWireCommsTask;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_pCommsTask = &AltCommsTask;
|
||||
break;
|
||||
}
|
||||
|
||||
if(_pCommsTask) {
|
||||
_pCommsTask->taskStart();
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TaskHandle_t
|
||||
CHeaterManager::getTaskHandle() const
|
||||
{
|
||||
if(_pCommsTask)
|
||||
return _pCommsTask->getTaskHandle();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SemaphoreHandle_t
|
||||
CHeaterManager::getSemaphore() const
|
||||
{
|
||||
if(_pCommsTask)
|
||||
return _pCommsTask->getSemaphore();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// char dbgMsg[COMMS_MSGQUEUESIZE];
|
||||
|
||||
/*QueueHandle_t
|
||||
CHeaterManager::getMsgQueue() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return BlueWireMsgQueue;
|
||||
case 1:
|
||||
if(AltCommsTask.getMsgQueue(dbgMsg))
|
||||
DebugPort.print(dbgMsg);
|
||||
return NULL;
|
||||
default: return NULL;
|
||||
}
|
||||
}*/
|
||||
|
||||
// QueueHandle_t MsgQueue = HeaterManager.getMsgQueue();
|
||||
|
||||
|
||||
int
|
||||
CHeaterManager::getHeaterStyle() const
|
||||
{
|
||||
return _heaterStyle;
|
||||
}
|
||||
|
||||
|
||||
float CHeaterManager::getFanRPM() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getFan_Actual();
|
||||
case 1: return AltCommsTask.getFanRPM();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
CHeaterManager::getFanVoltage() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getFan_Voltage();
|
||||
case 1: return -1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
CHeaterManager::getPumpDemand() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getPump_Fixed();
|
||||
case 1: return AltCommsTask.getDesiredPumpRate(CDemandManager::getPumpHz()); //AltHeaterData.Demand;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
CHeaterManager::getPumpRate() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0:
|
||||
return getHeaterInfo().getPump_Actual();
|
||||
case 1:
|
||||
return AltCommsTask.getActualPumpRate();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
CHeaterManager::getBodyTemp() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getTemperature_HeatExchg();
|
||||
case 1: return AltCommsTask.getBodyTemp();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
CHeaterManager::getGlowPlugPower() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getGlowPlug_Power();
|
||||
case 1: return AltCommsTask.getGlow();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
CHeaterManager::getRunStateEx() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getRunStateEx();
|
||||
case 1: return AltCommsTask.getRunState();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
int
|
||||
CHeaterManager::getRunState() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getRunState();
|
||||
case 1: return AltCommsTask.getRunState();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
CHeaterManager::getErrState() const
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0: return getHeaterInfo().getErrState();
|
||||
case 1: return AltCommsTask.getErrState();
|
||||
default: return 8;
|
||||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
CHeaterManager::getRunStateStr() const
|
||||
{
|
||||
uint8_t runstate = getRunStateEx();
|
||||
UPPERLIMIT(runstate, 13);
|
||||
if(runstate == 2 && getPumpRate() == 0) { // split runstate 2 - glow, then fuel
|
||||
runstate = 9;
|
||||
}
|
||||
return Runstates[runstate];
|
||||
}
|
||||
|
||||
const char*
|
||||
CHeaterManager::getErrStateStr() const
|
||||
{
|
||||
uint8_t errstate = getErrState();
|
||||
UPPERLIMIT(errstate, 13);
|
||||
return Errstates[errstate];
|
||||
}
|
||||
|
||||
const char*
|
||||
CHeaterManager::getErrStateStrEx() const
|
||||
{
|
||||
uint8_t errstate = getErrState();
|
||||
UPPERLIMIT(errstate, 13);
|
||||
return ErrstatesEx[errstate];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
CHeaterManager::reqOnOff(bool state)
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0:
|
||||
if(state) {
|
||||
TxManage.queueOnRequest();
|
||||
SmartError.reset();
|
||||
}
|
||||
else {
|
||||
TxManage.queueOffRequest();
|
||||
SmartError.inhibit();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
DebugPort.println("Alt heater start request queued");
|
||||
AltCommsTask.reqPower();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterManager::checkRxEvents()
|
||||
{
|
||||
if(_heaterStyle == 0) {
|
||||
checkBlueWireRxEvents();
|
||||
}
|
||||
else if(_heaterStyle == 1) {
|
||||
AltCommsTask.checkEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterManager::checkTxEvents()
|
||||
{
|
||||
if(_heaterStyle == 0) {
|
||||
checkBlueWireTxEvents();
|
||||
}
|
||||
else if(_heaterStyle == 1) {
|
||||
AltCommsTask.manage();//checkAltTxEvents();
|
||||
}
|
||||
}
|
||||
|
||||
char taskMsg[COMMS_MSGQUEUESIZE];
|
||||
|
||||
void
|
||||
CHeaterManager::checkMsgEvents()
|
||||
{
|
||||
if(_pCommsTask && _pCommsTask->getMsgQueue(taskMsg))
|
||||
DebugPort.print(taskMsg);
|
||||
}
|
||||
|
||||
bool
|
||||
CHeaterManager::isOnline()
|
||||
{
|
||||
switch(_heaterStyle) {
|
||||
case 0:
|
||||
return hasHtrData();
|
||||
break;
|
||||
case 1:
|
||||
return AltCommsTask.isActive();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
70
src/Protocol/HeaterManager.h
Normal file
70
src/Protocol/HeaterManager.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2020 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __HEATERMANAGER_H__
|
||||
#define __HEATERMANAGER_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "AltController.h"
|
||||
#include "AltControllerTask.h"
|
||||
#include "BlueWireTask.h"
|
||||
|
||||
|
||||
class CHeaterManager {
|
||||
int _heaterStyle;
|
||||
TaskHandle_t _taskHandle;
|
||||
CCommsTask* _pCommsTask;
|
||||
public:
|
||||
CHeaterManager();
|
||||
bool detect();
|
||||
void setHeaterStyle(int mode);
|
||||
|
||||
float getFanRPM() const;
|
||||
float getFanVoltage() const;
|
||||
float getPumpRate() const;
|
||||
float getPumpDemand() const;
|
||||
float getBodyTemp() const;
|
||||
float getGlowPlugPower() const;
|
||||
int getErrState() const;
|
||||
int getRunState() const;
|
||||
int getRunStateEx() const;
|
||||
int getHeaterStyle() const;
|
||||
const char* getErrStateStr() const;
|
||||
const char* getErrStateStrEx() const;
|
||||
const char* getRunStateStr() const;
|
||||
TaskHandle_t getTaskHandle() const;
|
||||
SemaphoreHandle_t getSemaphore() const;
|
||||
// QueueHandle_t getMsgQueue() const;
|
||||
|
||||
void checkMsgEvents();
|
||||
void checkRxEvents();
|
||||
void checkTxEvents();
|
||||
bool isOnline();
|
||||
|
||||
|
||||
void reqOnOff(bool state);
|
||||
void reqVolts();
|
||||
void reqBodyT();
|
||||
};
|
||||
|
||||
extern CHeaterManager HeaterManager;
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue