rename folders
This commit is contained in:
parent
1ec8effe90
commit
1815597374
63 changed files with 1 additions and 0 deletions
5
ESPMegaPRO-firmware/.gitignore
vendored
Normal file
5
ESPMegaPRO-firmware/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
10
ESPMegaPRO-firmware/.vscode/extensions.json
vendored
Normal file
10
ESPMegaPRO-firmware/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
62
ESPMegaPRO-firmware/.vscode/settings.json
vendored
Normal file
62
ESPMegaPRO-firmware/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"*.cps": "javascript",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"adafruit_ads1x15.h": "c",
|
||||
"*.tcc": "cpp",
|
||||
"memory": "cpp",
|
||||
"random": "cpp",
|
||||
"functional": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"bitset": "cpp",
|
||||
"regex": "cpp"
|
||||
},
|
||||
"cmake.configureOnOpen": true,
|
||||
"cmake.sourceDirectory": "D:/Git/ESPMegaPRO-v3-SDK/Template Project/.pio/libdeps/wt32-eth01/Adafruit BusIO",
|
||||
"editor.tokenColorCustomizations": {
|
||||
"comments": "",
|
||||
"textMateRules": []
|
||||
}
|
||||
}
|
39
ESPMegaPRO-firmware/include/README
Normal file
39
ESPMegaPRO-firmware/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
138
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogCard.cpp
Normal file
138
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogCard.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
#include <AnalogCard.hpp>
|
||||
|
||||
AnalogCard::AnalogCard() : dac0(DAC0_ADDRESS),
|
||||
dac1(DAC1_ADDRESS),
|
||||
dac2(DAC2_ADDRESS),
|
||||
dac3(DAC3_ADDRESS),
|
||||
analogInputBankA(),
|
||||
analogInputBankB()
|
||||
{
|
||||
}
|
||||
|
||||
void AnalogCard::dacWrite(uint8_t pin, uint16_t value)
|
||||
{
|
||||
this->setDACState(pin, value > 0);
|
||||
this->setDACValue(pin, value);
|
||||
}
|
||||
|
||||
void AnalogCard::setDACState(uint8_t pin, bool state)
|
||||
{
|
||||
this->dac_state[pin] = state;
|
||||
this->sendDataToDAC(pin, this->dac_value[pin]*state);
|
||||
for (int i = 0; i < this->dac_change_callbacks.size(); i++)
|
||||
{
|
||||
this->dac_change_callbacks[i](pin, state, this->dac_value[pin]);
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogCard::setDACValue(uint8_t pin, uint16_t value)
|
||||
{
|
||||
this->dac_value[pin] = value;
|
||||
this->sendDataToDAC(pin, value*this->dac_state[pin]);
|
||||
for (int i = 0; i < this->dac_change_callbacks.size(); i++)
|
||||
{
|
||||
this->dac_change_callbacks[i](pin, this->dac_state[pin], value);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t AnalogCard::getDACValue(uint8_t pin)
|
||||
{
|
||||
return this->dac_value[pin];
|
||||
}
|
||||
|
||||
bool AnalogCard::getDACState(uint8_t pin)
|
||||
{
|
||||
return this->dac_state[pin];
|
||||
}
|
||||
|
||||
void AnalogCard::sendDataToDAC(uint8_t pin, uint16_t value)
|
||||
{
|
||||
switch (pin)
|
||||
{
|
||||
case 0:
|
||||
this->dac0.writeDAC(value);
|
||||
break;
|
||||
case 1:
|
||||
this->dac1.writeDAC(value);
|
||||
break;
|
||||
case 2:
|
||||
this->dac2.writeDAC(value);
|
||||
break;
|
||||
case 3:
|
||||
this->dac3.writeDAC(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint16_t AnalogCard::analogRead(uint8_t pin)
|
||||
{
|
||||
if (pin >= 0 && pin <= 3)
|
||||
{
|
||||
return this->analogInputBankA.readADC_SingleEnded(pin);
|
||||
}
|
||||
else if (pin >= 4 && pin <= 7)
|
||||
{
|
||||
return this->analogInputBankB.readADC_SingleEnded(pin - 4);
|
||||
}
|
||||
return 65535;
|
||||
}
|
||||
bool AnalogCard::begin()
|
||||
{
|
||||
if (!this->dac0.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install DAC0");
|
||||
return false;
|
||||
}
|
||||
if (!this->dac1.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install DAC1");
|
||||
return false;
|
||||
}
|
||||
if (!this->dac2.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install DAC2");
|
||||
return false;
|
||||
}
|
||||
if (!this->dac3.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install DAC3");
|
||||
return false;
|
||||
}
|
||||
if (!this->analogInputBankA.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install analog input bank A");
|
||||
return false;
|
||||
}
|
||||
if (!this->analogInputBankB.begin())
|
||||
{
|
||||
Serial.println("Card Analog ERROR: Failed to install analog input bank B");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnalogCard::loop()
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t AnalogCard::getType()
|
||||
{
|
||||
return CARD_TYPE_ANALOG;
|
||||
}
|
||||
|
||||
void AnalogCard::registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
||||
{
|
||||
this->dac_change_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
// void AnalogCard::deregisterDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
||||
// {
|
||||
// for (int i = 0; i < this->dac_change_callbacks.size(); i++)
|
||||
// {
|
||||
// if (this->dac_change_callbacks[i].target<void(uint8_t, bool, uint16_t)>() == callback.target<void(uint8_t, bool, uint16_t)>())
|
||||
// {
|
||||
// this->dac_change_callbacks.erase(this->dac_change_callbacks.begin() + i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
41
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogCard.hpp
Normal file
41
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogCard.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <Adafruit_ADS1X15.h>
|
||||
#include <MCP4725.h>
|
||||
#include <vector>
|
||||
|
||||
#define CARD_TYPE_ANALOG 0x02
|
||||
|
||||
#define ANALOG_INPUT_BANK_A_ADDRESS 0x48
|
||||
#define ANALOG_INPUT_BANK_B_ADDRESS 0x49
|
||||
#define DAC0_ADDRESS 0x60
|
||||
#define DAC1_ADDRESS 0x61
|
||||
#define DAC2_ADDRESS 0x62
|
||||
#define DAC3_ADDRESS 0x63
|
||||
|
||||
class AnalogCard : public ExpansionCard {
|
||||
public:
|
||||
AnalogCard();
|
||||
void dacWrite(uint8_t pin, uint16_t value);
|
||||
void sendDataToDAC(uint8_t pin, uint16_t value);
|
||||
uint16_t analogRead(uint8_t pin);
|
||||
bool begin();
|
||||
void loop();
|
||||
bool getDACState(uint8_t pin);
|
||||
uint16_t getDACValue(uint8_t pin);
|
||||
void setDACState(uint8_t pin, bool state);
|
||||
void setDACValue(uint8_t pin, uint16_t value);
|
||||
void registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
//void deregisterDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
uint8_t getType();
|
||||
private:
|
||||
std::vector<std::function<void(uint8_t, bool, uint16_t)>> dac_change_callbacks;
|
||||
bool dac_state[4];
|
||||
uint16_t dac_value[4];
|
||||
MCP4725 dac0;
|
||||
MCP4725 dac1;
|
||||
MCP4725 dac2;
|
||||
MCP4725 dac3;
|
||||
Adafruit_ADS1115 analogInputBankA;
|
||||
Adafruit_ADS1115 analogInputBankB;
|
||||
};
|
270
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogIoT.cpp
Normal file
270
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogIoT.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include <AnalogIoT.hpp>
|
||||
|
||||
AnalogIoT::AnalogIoT() {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_publish_enabled[i] = false;
|
||||
adc_conversion_interval[i] = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
AnalogIoT::~AnalogIoT() {
|
||||
}
|
||||
bool AnalogIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->card = (AnalogCard*)card;
|
||||
this-> card_id = card_id;
|
||||
this->dac_set_state_length = strlen(DAC_SET_STATE_TOPIC);
|
||||
this->dac_set_value_length = strlen(DAC_SET_VALUE_TOPIC);
|
||||
this->dac_state_length = strlen(DAC_STATE_TOPIC);
|
||||
this->dac_value_length = strlen(DAC_VALUE_TOPIC);
|
||||
this->request_state_length = strlen(REQUEST_STATE_TOPIC);
|
||||
this->dac_publish_enable_length = strlen(DAC_PUBLISH_ENABLE_TOPIC);
|
||||
// Register callbacks
|
||||
auto bindedCallback = std::bind(&AnalogIoT::handleDACChange, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->card->registerDACChangeCallback(bindedCallback);
|
||||
return true;
|
||||
}
|
||||
void AnalogIoT::handleMqttMessage(char *topic, char *payload){
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if(this-> processDACSetStateMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processDACSetValueMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processRequestStateMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processADCSetConversionIntervalMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processADCSetConversionEnabledMessage(topic, payload, topic_length)) return;
|
||||
}
|
||||
void AnalogIoT::publishADCs() {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
this->publishADC(i);
|
||||
}
|
||||
}
|
||||
void AnalogIoT::publishADC(uint8_t pin) {
|
||||
if (this->adc_publish_enabled[pin]) {
|
||||
uint16_t value = this->card->analogRead(pin);
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "adc/%02d/value", pin);
|
||||
char *payload = new char[10];
|
||||
sprintf(payload, "%d", value);
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
// Call all callbacks
|
||||
for (int i = 0; i < this->adc_conversion_callbacks.size(); i++) {
|
||||
this->adc_conversion_callbacks[i](pin, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
void AnalogIoT::setADCsPublishInterval(uint32_t interval) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_conversion_interval[i] = interval;
|
||||
}
|
||||
}
|
||||
void AnalogIoT::setADCsPublishEnabled(bool enabled) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
adc_publish_enabled[i] = enabled;
|
||||
}
|
||||
}
|
||||
void AnalogIoT::registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback) {
|
||||
this->adc_conversion_callbacks.push_back(callback);
|
||||
}
|
||||
// void AnalogIoT::deregisterADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback) {
|
||||
// for (int i = 0; i < this->adc_conversion_callbacks.size(); i++) {
|
||||
// if (this->adc_conversion_callbacks[i].target<void(uint8_t, uint16_t)>() == callback.target<void(uint8_t, uint16_t)>()) {
|
||||
// this->adc_conversion_callbacks.erase(this->adc_conversion_callbacks.begin() + i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
void AnalogIoT::setADCConversionInterval(uint8_t pin, uint16_t interval) {
|
||||
adc_conversion_interval[pin] = interval;
|
||||
}
|
||||
void AnalogIoT::setADCConversionEnabled(uint8_t pin, bool enabled) {
|
||||
adc_publish_enabled[pin] = enabled;
|
||||
}
|
||||
bool AnalogIoT::processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// TODO: Process payload matching the criteria
|
||||
// Topic: adc/<%02d>/set/conversion_interval
|
||||
// The first 4 characters are "adc/"
|
||||
// The length of the topic must be 30 characters
|
||||
// The last 24 characters must be "/set/conversion_interval"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 30) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "adc/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 26, "/set/conversion_interval", 24)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
uint16_t interval = atoi(payload);
|
||||
// Set the interval
|
||||
this->setADCConversionInterval(pin, interval);
|
||||
return true;
|
||||
}
|
||||
bool AnalogIoT::processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: adc/<%02d>/set/conversion_enabled
|
||||
// The first 4 characters are "adc/"
|
||||
// The length of the topic must be 29 characters
|
||||
// The last 23 characters must be ""/set/conversion_enabled
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 29) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "adc/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 25, "/set/conversion_enabled", 23)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
bool enabled = atoi(payload);
|
||||
// Set conversion enabled
|
||||
this->setADCConversionEnabled(pin, enabled);
|
||||
return true;
|
||||
}
|
||||
bool AnalogIoT::processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: dac/<%02d>/set/state
|
||||
// The first 4 characters are "dac/"
|
||||
// The length of the topic must be 16 characters
|
||||
// The last 10 characters must be "/set/state"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 16) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "dac/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 12, "/set/state", 10)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
bool state = atoi(payload);
|
||||
// Set the state
|
||||
this->card->setDACState(pin, state);
|
||||
return true;
|
||||
}
|
||||
bool AnalogIoT::processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: dac/<%02d>/set/value
|
||||
// The first 4 characters are "dac/"
|
||||
// The length of the topic must be 16 characters
|
||||
// The last 10 characters must be "/set/value"
|
||||
// After all these conditions are met, the topic is valid
|
||||
// Extract the pin number from the topic
|
||||
if (topic_length != 16) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, "dac/", 4)) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic + 12, "/set/value", 10)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t pin = (topic[4] - '0') * 10 + (topic[5] - '0');
|
||||
// Extract the payload
|
||||
uint16_t value = atoi(payload);
|
||||
// Set the value
|
||||
this->card->setDACValue(pin, value);
|
||||
return true;
|
||||
}
|
||||
bool AnalogIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Topic: requeststate
|
||||
// The length of the topic must be 12 characters
|
||||
// After all these conditions are met, the topic is valid
|
||||
if (topic_length != 12) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(topic, REQUEST_STATE_TOPIC, 12)) {
|
||||
return false;
|
||||
}
|
||||
// Publish the state of all DACs
|
||||
this->publishDACs();
|
||||
// Publish the state of all ADCs
|
||||
this->publishADCs();
|
||||
return false;
|
||||
}
|
||||
void AnalogIoT::subscribe() {
|
||||
// There are 4 DACs and 8 ADCs
|
||||
// DACs: dac/<%02d>/set/state, dac/<%02d>/set/value, dac/publish_enable
|
||||
// ADCs: adc/<%02d>/set/conversion_interval, adc/<%02d>/set/conversion_enabled
|
||||
|
||||
// Subscribe to all set state topics
|
||||
char topic[20];
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
sprintf(topic, "dac/%02d/set/state", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set value topics
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
sprintf(topic, "dac/%02d/set/value", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set conversion interval topics
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
sprintf(topic, "adc/%02d/set/conversion_interval", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set conversion enabled topics
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
sprintf(topic, "adc/%02d/set/conversion_enabled", i);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to publish enable topic
|
||||
this->subscribeRelative("dac/publish_enable");
|
||||
}
|
||||
void AnalogIoT::loop() {
|
||||
// Iterate over all ADCs and publish if enabled and interval has passed
|
||||
uint32_t now = millis();
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (this->adc_publish_enabled[i] && now - this->last_adc_publish > this->adc_conversion_interval[i]) {
|
||||
this->publishADC(i);
|
||||
this->last_adc_publish = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
void AnalogIoT::publishReport() {
|
||||
publishADCs();
|
||||
publishDACs();
|
||||
}
|
||||
|
||||
uint8_t AnalogIoT::getType() {
|
||||
return CARD_TYPE_ANALOG;
|
||||
}
|
||||
void AnalogIoT::publishDACs() {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
this->publishDAC(i);
|
||||
}
|
||||
}
|
||||
void AnalogIoT::publishDAC(uint8_t pin) {
|
||||
this->publishDACState(pin);
|
||||
this->publishDACValue(pin);
|
||||
}
|
||||
void AnalogIoT::publishDACState(uint8_t pin) {
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "dac/%02d/state", pin);
|
||||
char *payload = new char[2];
|
||||
sprintf(payload, "%d", this->card->getDACState(pin));
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
}
|
||||
void AnalogIoT::publishDACValue(uint8_t pin) {
|
||||
char *topic = new char[15];
|
||||
sprintf(topic, "dac/%02d/value", pin);
|
||||
char *payload = new char[5];
|
||||
sprintf(payload, "%d", this->card->getDACValue(pin));
|
||||
this->publishRelative(topic, payload);
|
||||
delete[] topic;
|
||||
delete[] payload;
|
||||
}
|
||||
|
||||
void AnalogIoT::handleDACChange(uint8_t pin, uint16_t value) {
|
||||
this->publishDAC(pin);
|
||||
}
|
54
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogIoT.hpp
Normal file
54
ESPMegaPRO-firmware/lib/ESPMegaPRO/AnalogIoT.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <vector>
|
||||
|
||||
#define DAC_SET_STATE_TOPIC "/set/state"
|
||||
#define DAC_SET_VALUE_TOPIC "/set/value"
|
||||
#define DAC_STATE_TOPIC "/dac/00/state"
|
||||
#define DAC_VALUE_TOPIC "/dac/00/value"
|
||||
#define DAC_PUBLISH_ENABLE_TOPIC "/publish_enable"
|
||||
#define REQUEST_STATE_TOPIC "requeststate"
|
||||
|
||||
class AnalogIoT : public IoTComponent {
|
||||
public:
|
||||
AnalogIoT();
|
||||
~AnalogIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void handleDACChange(uint8_t pin, uint16_t value);
|
||||
void publishADCs();
|
||||
void publishADC(uint8_t pin);
|
||||
void publishDACs();
|
||||
void publishDAC(uint8_t pin);
|
||||
void publishDACState(uint8_t pin);
|
||||
void publishDACValue(uint8_t pin);
|
||||
void setADCsPublishInterval(uint32_t interval);
|
||||
void setADCsPublishEnabled(bool enabled);
|
||||
void registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback);
|
||||
// void deregisterADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback);
|
||||
void setADCConversionInterval(uint8_t pin, uint16_t interval);
|
||||
void setADCConversionEnabled(uint8_t pin, bool enabled);
|
||||
bool processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
uint8_t dac_set_state_length;
|
||||
uint8_t dac_set_value_length;
|
||||
uint8_t dac_state_length;
|
||||
uint8_t dac_value_length;
|
||||
uint8_t request_state_length;
|
||||
uint8_t dac_publish_enable_length;
|
||||
uint32_t last_adc_publish = 0;
|
||||
AnalogCard *card;
|
||||
bool adc_publish_enabled[8];
|
||||
uint16_t adc_conversion_interval[8];
|
||||
uint32_t last_adc_conversion[8];
|
||||
std::vector<std::function<void(uint8_t, uint16_t)>> adc_conversion_callbacks;
|
||||
};
|
197
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateCard.cpp
Normal file
197
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateCard.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include <ClimateCard.hpp>
|
||||
|
||||
ClimateCard::ClimateCard(uint8_t ir_pin) : irsender(ir_pin)
|
||||
{
|
||||
this->ir_pin = ir_pin;
|
||||
irsender.begin(ir_pin);
|
||||
// Initialize Pointers
|
||||
this->dht = nullptr;
|
||||
this->ds18b20 = nullptr;
|
||||
this->fram = nullptr;
|
||||
// Initialize Variables
|
||||
this->fram_address = 0;
|
||||
this->fram_auto_save = false;
|
||||
this->state.ac_temperature = 0;
|
||||
this->state.ac_mode = 0;
|
||||
this->state.ac_fan_speed = 0;
|
||||
this->humidity = 0;
|
||||
this->room_temperature = 0;
|
||||
// Initialize state
|
||||
this->state.ac_temperature = 25;
|
||||
this->state.ac_mode = 0;
|
||||
this->state.ac_fan_speed = 0;
|
||||
}
|
||||
|
||||
ClimateCard::~ClimateCard()
|
||||
{
|
||||
delete dht;
|
||||
delete ds18b20;
|
||||
}
|
||||
|
||||
bool ClimateCard::begin(AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin)
|
||||
{
|
||||
this->ac = ac;
|
||||
this->sensor_type = sensor_type;
|
||||
this->sensor_pin = sensor_pin;
|
||||
switch (sensor_type)
|
||||
{
|
||||
case AC_SENSOR_TYPE_DHT22:
|
||||
dht = new DHTNEW(sensor_pin);
|
||||
break;
|
||||
case AC_SENSOR_TYPE_DS18B20:
|
||||
OneWire oneWire(sensor_pin);
|
||||
ds18b20 = new DS18B20(&oneWire);
|
||||
break;
|
||||
}
|
||||
updateAirConditioner();
|
||||
}
|
||||
|
||||
bool ClimateCard::begin(AirConditioner ac)
|
||||
{
|
||||
this->begin(ac, AC_SENSOR_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
void ClimateCard::loop()
|
||||
{
|
||||
static uint32_t last_sensor_update = 0;
|
||||
if (millis() - last_sensor_update >= AC_SENSOR_READ_INTERVAL)
|
||||
{
|
||||
last_sensor_update = millis();
|
||||
updateSensor();
|
||||
}
|
||||
}
|
||||
|
||||
void ClimateCard::bindFRAM(FRAM *fram, uint16_t fram_address)
|
||||
{
|
||||
this->fram = fram;
|
||||
this->fram_address = fram_address;
|
||||
}
|
||||
|
||||
void ClimateCard::setFRAMAutoSave(bool autoSave)
|
||||
{
|
||||
this->fram_auto_save = autoSave;
|
||||
}
|
||||
|
||||
void ClimateCard::saveStateToFRAM()
|
||||
{
|
||||
fram->writeObject(fram_address, this->state);
|
||||
}
|
||||
|
||||
void ClimateCard::loadStateFromFRAM()
|
||||
{
|
||||
fram->readObject(fram_address, this->state);
|
||||
}
|
||||
|
||||
void ClimateCard::setTemperature(uint8_t temperature)
|
||||
{
|
||||
this->state.ac_temperature = temperature;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
void ClimateCard::setMode(uint8_t mode)
|
||||
{
|
||||
this->state.ac_mode = mode;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
void ClimateCard::setFanSpeed(uint8_t fan_speed)
|
||||
{
|
||||
this->state.ac_fan_speed = fan_speed;
|
||||
updateAirConditioner();
|
||||
if (fram_auto_save)
|
||||
saveStateToFRAM();
|
||||
}
|
||||
|
||||
void ClimateCard::registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
uint8_t ClimateCard::getType()
|
||||
{
|
||||
return CARD_TYPE_CLIMATE;
|
||||
}
|
||||
|
||||
void ClimateCard::updateSensor()
|
||||
{
|
||||
if (sensor_type == AC_SENSOR_TYPE_NONE)
|
||||
return;
|
||||
// Read sensor data and update variables
|
||||
switch (sensor_type)
|
||||
{
|
||||
case AC_SENSOR_TYPE_DHT22:
|
||||
if (millis() - dht->lastRead() < AC_SENSOR_READ_INTERVAL)
|
||||
return;
|
||||
dht->read();
|
||||
room_temperature = dht->getTemperature();
|
||||
humidity = dht->getHumidity();
|
||||
break;
|
||||
case AC_SENSOR_TYPE_DS18B20:
|
||||
ds18b20->requestTemperatures();
|
||||
uint32_t start = millis();
|
||||
while (!ds18b20->isConversionComplete())
|
||||
{
|
||||
if (millis() - start >= AC_SENSOR_READ_TIMEOUT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
room_temperature = ds18b20->getTempC();
|
||||
break;
|
||||
}
|
||||
for (uint8_t i = 0; i < sensor_callbacks.size(); i++)
|
||||
{
|
||||
sensor_callbacks[i](room_temperature, humidity);
|
||||
}
|
||||
}
|
||||
|
||||
void ClimateCard::updateAirConditioner()
|
||||
{
|
||||
irsender.sendRaw(ac.infraredCodes[this->state.ac_mode][this->state.ac_fan_speed][this->state.ac_temperature],
|
||||
sizeof(ac.infraredCodes[this->state.ac_mode][this->state.ac_fan_speed][this->state.ac_temperature]) / sizeof(uint16_t),
|
||||
NEC_KHZ);
|
||||
for (uint8_t i = 0; i < callbacks.size(); i++)
|
||||
{
|
||||
callbacks[i](this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t ClimateCard::getSensorType()
|
||||
{
|
||||
return sensor_type;
|
||||
}
|
||||
|
||||
float ClimateCard::getRoomTemperature()
|
||||
{
|
||||
return room_temperature;
|
||||
}
|
||||
|
||||
float ClimateCard::getHumidity()
|
||||
{
|
||||
return humidity;
|
||||
}
|
||||
|
||||
uint8_t ClimateCard::getTemperature()
|
||||
{
|
||||
return state.ac_temperature;
|
||||
}
|
||||
|
||||
uint8_t ClimateCard::getMode()
|
||||
{
|
||||
return state.ac_mode;
|
||||
}
|
||||
|
||||
uint8_t ClimateCard::getFanSpeed()
|
||||
{
|
||||
return state.ac_fan_speed;
|
||||
}
|
||||
|
||||
void ClimateCard::registerSensorCallback(std::function<void(float, float)> callback)
|
||||
{
|
||||
sensor_callbacks.push_back(callback);
|
||||
}
|
85
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateCard.hpp
Normal file
85
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateCard.hpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <IRremote.h>
|
||||
#include <FRAM.h>
|
||||
#include <OneWire.h>
|
||||
#include <DS18B20.h>
|
||||
#include <dhtnew.h>
|
||||
#include <vector>
|
||||
|
||||
#define CARD_TYPE_CLIMATE 0x03
|
||||
|
||||
#define AC_SENSOR_TYPE_NONE 0x00
|
||||
#define AC_SENSOR_TYPE_DHT22 0x01
|
||||
#define AC_SENSOR_TYPE_DS18B20 0x02
|
||||
|
||||
#define AC_SENSOR_READ_INTERVAL 5000
|
||||
#define AC_SENSOR_READ_TIMEOUT 250
|
||||
|
||||
struct ClimateCardData {
|
||||
uint8_t ac_temperature;
|
||||
uint8_t ac_mode;
|
||||
uint8_t ac_fan_speed;
|
||||
};
|
||||
|
||||
struct AirConditioner {
|
||||
uint8_t max_temperature;
|
||||
uint8_t min_temperature;
|
||||
uint8_t modes;
|
||||
char **mode_names;
|
||||
uint8_t fan_speeds;
|
||||
char **fan_speed_names;
|
||||
uint16_t ****infraredCodes;
|
||||
};
|
||||
// This requires 3 bytes of FRAM
|
||||
|
||||
class ClimateCard : public ExpansionCard {
|
||||
public:
|
||||
ClimateCard(uint8_t ir_pin);
|
||||
~ClimateCard();
|
||||
bool begin(AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin);
|
||||
bool begin(AirConditioner ac);
|
||||
void loop();
|
||||
void bindFRAM(FRAM *fram, uint16_t fram_address);
|
||||
void setFRAMAutoSave(bool autoSave);
|
||||
void saveStateToFRAM();
|
||||
void loadStateFromFRAM();
|
||||
void setTemperature(uint8_t temperature);
|
||||
uint8_t getTemperature();
|
||||
void setMode(uint8_t mode);
|
||||
uint8_t getMode();
|
||||
void setFanSpeed(uint8_t fan_speed);
|
||||
uint8_t getFanSpeed();
|
||||
float getRoomTemperature();
|
||||
float getHumidity();
|
||||
uint8_t getSensorType();
|
||||
void registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback);
|
||||
void registerSensorCallback(std::function<void(float, float)> callback);
|
||||
uint8_t getType();
|
||||
private:
|
||||
// Sensor objects
|
||||
// We use pointers here because we don't know which sensor will be used
|
||||
DHTNEW *dht;
|
||||
DS18B20 *ds18b20;
|
||||
// Callbacks
|
||||
std::vector<std::function<void(uint8_t, uint8_t, uint8_t)>> callbacks;
|
||||
std::vector<std::function<void(float, float)>> sensor_callbacks;
|
||||
// Update functions
|
||||
void updateSensor();
|
||||
void updateAirConditioner();
|
||||
// IR variables
|
||||
IRsend irsender;
|
||||
uint8_t ir_pin;
|
||||
// Air conditioner variables
|
||||
AirConditioner ac;
|
||||
ClimateCardData state;
|
||||
// Sensor variables
|
||||
uint8_t sensor_type;
|
||||
uint8_t sensor_pin;
|
||||
float humidity;
|
||||
float room_temperature;
|
||||
// FRAM variables
|
||||
FRAM *fram;
|
||||
uint8_t fram_address;
|
||||
bool fram_auto_save;
|
||||
};
|
132
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateIoT.cpp
Normal file
132
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateIoT.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include <ClimateIoT.hpp>
|
||||
|
||||
ClimateIoT::ClimateIoT() {
|
||||
|
||||
}
|
||||
|
||||
ClimateIoT::~ClimateIoT() {
|
||||
// Destructor implementation
|
||||
}
|
||||
|
||||
bool ClimateIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->card = (ClimateCard *)card;
|
||||
// Reister Callbacks
|
||||
auto bindedSensorCallback = std::bind(&ClimateIoT::handleSensorUpdate, this, std::placeholders::_1, std::placeholders::_2);
|
||||
this->card->registerSensorCallback(bindedSensorCallback);
|
||||
auto bindedAirConditionerCallback = std::bind(&ClimateIoT::handleAirConditionerUpdate, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->card->registerChangeCallback(bindedAirConditionerCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClimateIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if (this->processSetTemperatureMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processSetModeMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processSetFanSpeedMessage(topic, payload, topic_length))
|
||||
return;
|
||||
if (this->processRequestStateMessage(topic, payload, topic_length))
|
||||
return;
|
||||
}
|
||||
|
||||
void ClimateIoT::publishClimateTemperature() {
|
||||
char payload[5];
|
||||
itoa(this->card->getTemperature(), payload, 10);
|
||||
this->publishRelative(AC_TEMPERATURE_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
void ClimateIoT::publishClimateMode() {
|
||||
char payload[2];
|
||||
itoa(this->card->getMode(), payload, 10);
|
||||
this->publishRelative(AC_MODE_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
void ClimateIoT::publishClimateFanSpeed() {
|
||||
char payload[2];
|
||||
itoa(this->card->getFanSpeed(), payload, 10);
|
||||
this->publishRelative(AC_FAN_SPEED_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
void ClimateIoT::publishSensor() {
|
||||
this->publishRoomTemperature();
|
||||
this->publishHumidity();
|
||||
}
|
||||
|
||||
void ClimateIoT::publishClimate() {
|
||||
this->publishClimateTemperature();
|
||||
this->publishClimateMode();
|
||||
this->publishClimateFanSpeed();
|
||||
}
|
||||
|
||||
void ClimateIoT::publishRoomTemperature() {
|
||||
char payload[5];
|
||||
itoa(this->card->getRoomTemperature(), payload, 10);
|
||||
this->publishRelative(AC_ROOM_TEMPERATURE_REPORT_TOPIC, payload);
|
||||
}
|
||||
|
||||
void ClimateIoT::publishHumidity() {
|
||||
if (this->card->getSensorType() == AC_SENSOR_TYPE_DHT22) {
|
||||
char payload[5];
|
||||
itoa(this->card->getHumidity(), payload, 10);
|
||||
this->publishRelative(AC_HUMIDITY_REPORT_TOPIC, payload);
|
||||
}
|
||||
}
|
||||
|
||||
void ClimateIoT::handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed) {
|
||||
this->publishClimate();
|
||||
}
|
||||
|
||||
void ClimateIoT::publishReport() {
|
||||
this->publishClimate();
|
||||
this->publishSensor();
|
||||
}
|
||||
|
||||
void ClimateIoT::subscribe() {
|
||||
this->subscribeRelative(AC_TEMPERATURE_SET_TOPIC);
|
||||
this->subscribeRelative(AC_MODE_SET_TOPIC);
|
||||
this->subscribeRelative(AC_FAN_SPEED_SET_TOPIC);
|
||||
}
|
||||
|
||||
void ClimateIoT::loop() {
|
||||
|
||||
}
|
||||
|
||||
uint8_t ClimateIoT::getType() {
|
||||
return CARD_TYPE_CLIMATE;
|
||||
}
|
||||
|
||||
bool ClimateIoT::processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_TEMPERATURE_SET_TOPIC)) {
|
||||
uint8_t temperature = atoi(payload);
|
||||
this->card->setTemperature(temperature);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClimateIoT::processSetModeMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_MODE_SET_TOPIC)) {
|
||||
uint8_t mode = atoi(payload);
|
||||
this->card->setMode(mode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClimateIoT::processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_FAN_SPEED_SET_TOPIC)) {
|
||||
uint8_t fan_speed = atoi(payload);
|
||||
this->card->setFanSpeed(fan_speed);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClimateIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
if (!strcmp(topic, AC_REQUEST_STATE_TOPIC)) {
|
||||
this->publishReport();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
42
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateIoT.hpp
Normal file
42
ESPMegaPRO-firmware/lib/ESPMegaPRO/ClimateIoT.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
|
||||
#define AC_MODE_REPORT_TOPIC "mode"
|
||||
#define AC_MODE_SET_TOPIC "set/mode"
|
||||
#define AC_TEMPERATURE_REPORT_TOPIC "temperature"
|
||||
#define AC_TEMPERATURE_SET_TOPIC "set/temperature"
|
||||
#define AC_FAN_SPEED_REPORT_TOPIC "fan_speed"
|
||||
#define AC_FAN_SPEED_SET_TOPIC "set/fan_speed"
|
||||
#define AC_ROOM_TEMPERATURE_REPORT_TOPIC "room_temperature"
|
||||
#define AC_HUMIDITY_REPORT_TOPIC "humidity"
|
||||
#define AC_REQUEST_STATE_TOPIC "request_state"
|
||||
|
||||
class ClimateIoT : public IoTComponent {
|
||||
public:
|
||||
ClimateIoT();
|
||||
~ClimateIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishClimate();
|
||||
void publishClimateTemperature();
|
||||
void publishClimateMode();
|
||||
void publishClimateFanSpeed();
|
||||
void publishSensor();
|
||||
void publishRoomTemperature();
|
||||
void publishHumidity();
|
||||
void handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed);
|
||||
void handleSensorUpdate(float temperature, float humidity);
|
||||
void handleAirConditionerUpdate(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
ClimateCard *climate_card;
|
||||
bool processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetModeMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
};
|
207
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputCard.cpp
Normal file
207
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputCard.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#include <DigitalInputCard.hpp>
|
||||
|
||||
// Instantiate the card with the specified address
|
||||
DigitalInputCard::DigitalInputCard(uint8_t address_a, uint8_t address_b)
|
||||
{
|
||||
this->address_a = address_a;
|
||||
this->address_b = address_b;
|
||||
}
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
// Bit 0,1,2 are for bank A
|
||||
// Bit 3,4,5 are for bank B
|
||||
DigitalInputCard::DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5)
|
||||
{
|
||||
this->address_a = 0x20;
|
||||
this->address_b = 0x20;
|
||||
this->inputBufferA = 0;
|
||||
this->inputBufferB = 0;
|
||||
if (bit0)
|
||||
this->address_a += 1;
|
||||
if (bit1)
|
||||
this->address_a += 2;
|
||||
if (bit2)
|
||||
this->address_a += 4;
|
||||
if (bit3)
|
||||
this->address_b += 1;
|
||||
if (bit4)
|
||||
this->address_b += 2;
|
||||
if (bit5)
|
||||
this->address_b += 4;
|
||||
}
|
||||
// Initialize the card
|
||||
bool DigitalInputCard::begin()
|
||||
{
|
||||
this->inputBankA = PCF8574(this->address_a);
|
||||
this->inputBankB = PCF8574(this->address_b);
|
||||
if (!this->inputBankA.begin()) {
|
||||
Serial.println("Input Card ERROR: Failed to install input bank A");
|
||||
return false;
|
||||
}
|
||||
if (!this->inputBankB.begin()) {
|
||||
Serial.println("Input Card ERROR: Failed to install input bank B");
|
||||
return false;
|
||||
}
|
||||
// Set the debounce time for all pins to 50ms
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
this->debounceTime[i] = 50;
|
||||
this->lastDebounceTime[i] = 0;
|
||||
}
|
||||
// Initialize the pin map to the default values
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
this->pinMap[i] = i;
|
||||
this->virtualPinMap[i] = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Refresh and Read the input from the specified pin, always refresh the input buffers
|
||||
bool DigitalInputCard::digitalRead(uint8_t pin)
|
||||
{
|
||||
return this->digitalRead(pin, true);
|
||||
}
|
||||
// Read the input from the specified pin, also refresh the input buffers if refresh is true
|
||||
bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
|
||||
{
|
||||
pin = pinMap[pin];
|
||||
// First check if the pin is in bank A or B
|
||||
if (pin >= 0 && pin <= 7)
|
||||
{
|
||||
// Refresh the input buffers if refresh is true
|
||||
if (refresh)
|
||||
refreshInputBankA();
|
||||
// Extract the bit from the buffer
|
||||
return ((inputBufferA >> (7 - pin)) & 1);
|
||||
}
|
||||
else if (pin >= 8 && pin <= 15)
|
||||
{
|
||||
// Refresh the input buffers if refresh is true
|
||||
if (refresh)
|
||||
refreshInputBankB();
|
||||
// Extract the bit from the buffer
|
||||
return ((inputBufferB >> (15 - pin)) & 1);
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
void DigitalInputCard::handlePinChange(int pin, uint8_t ¤tBuffer, uint8_t &previousBuffer)
|
||||
{
|
||||
// Get the index of the pin in the pin map
|
||||
uint8_t virtualPin = virtualPinMap[pin];
|
||||
// Handle Bank A
|
||||
if (((previousBuffer >> (7 - pin)) & 1) != ((currentBuffer >> (7 - pin)) & 1))
|
||||
{
|
||||
if (millis() - lastDebounceTime[pin] > debounceTime[pin])
|
||||
{
|
||||
lastDebounceTime[pin] = millis();
|
||||
previousBuffer ^= (-((currentBuffer >> (7 - pin)) & 1) ^ previousBuffer) & (1UL << (7 - pin));
|
||||
for (int i = 0; i < callbacks.size(); i++)
|
||||
callbacks[i](virtualPin, ((currentBuffer >> (7 - pin)) & 1));
|
||||
}
|
||||
}
|
||||
// Handle Bank B
|
||||
if (((previousBuffer >> (15 - pin)) & 1) != ((currentBuffer >> (15 - pin)) & 1))
|
||||
{
|
||||
if (millis() - lastDebounceTime[pin] > debounceTime[pin])
|
||||
{
|
||||
lastDebounceTime[pin] = millis();
|
||||
previousBuffer ^= (-((currentBuffer >> (15 - pin)) & 1) ^ previousBuffer) & (1UL << (15 - pin));
|
||||
for (int i = 0; i < callbacks.size(); i++)
|
||||
callbacks[i](virtualPin, ((currentBuffer >> (15 - pin)) & 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Preform a loop to refresh the input buffers
|
||||
void DigitalInputCard::loop()
|
||||
{
|
||||
// Refresh the input buffers
|
||||
refreshInputBankA();
|
||||
refreshInputBankB();
|
||||
// Iterate over all pins and check if they changed
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// Check which bank the pin is in
|
||||
if (i < 8)
|
||||
{
|
||||
handlePinChange(i, inputBufferA, previousInputBufferA);
|
||||
}
|
||||
else if (i >= 8 && i <= 15)
|
||||
{
|
||||
handlePinChange(i, inputBufferB, previousInputBufferB);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the input buffer for bank A
|
||||
uint8_t DigitalInputCard::getInputBufferA()
|
||||
{
|
||||
// Rearrange the bits to match the pin map
|
||||
uint8_t inputBufferA_rearranged = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
inputBufferA_rearranged |= ((inputBufferA >> i) & 1) << (7 - i);
|
||||
}
|
||||
return inputBufferA_rearranged;
|
||||
}
|
||||
// Get the input buffer for bank B
|
||||
uint8_t DigitalInputCard::getInputBufferB()
|
||||
{
|
||||
// Rearrange the bits to match the pin map
|
||||
uint8_t inputBufferB_rearranged = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
inputBufferB_rearranged |= ((inputBufferB >> i) & 1) << (7 - i);
|
||||
}
|
||||
return inputBufferB_rearranged;
|
||||
}
|
||||
// Register a callback function to be called when a pin changes
|
||||
void DigitalInputCard::registerCallback(std::function<void(uint8_t, bool)> callback)
|
||||
{
|
||||
callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
// Refresh the input buffer for bank A
|
||||
void DigitalInputCard::refreshInputBankA()
|
||||
{
|
||||
inputBufferA = inputBankA.read8();
|
||||
}
|
||||
// Refresh the input buffer for bank B
|
||||
void DigitalInputCard::refreshInputBankB()
|
||||
{
|
||||
inputBufferB = inputBankB.read8();
|
||||
}
|
||||
|
||||
void DigitalInputCard::setDebounceTime(uint8_t pin, uint32_t debounceTime)
|
||||
{
|
||||
pin = pinMap[pin];
|
||||
this->debounceTime[pin] = debounceTime;
|
||||
}
|
||||
|
||||
// void DigitalInputCard::unregisterCallback(std::function<void(uint8_t, bool)> callback)
|
||||
// {
|
||||
// for (int i = 0; i < callbacks.size(); i++)
|
||||
// {
|
||||
// if (callbacks[i].target<void(uint8_t, bool)>() == callback.target<void(uint8_t, bool)>())
|
||||
// {
|
||||
// callbacks.erase(callbacks.begin() + i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
void DigitalInputCard::loadPinMap(uint8_t pinMap[16])
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// Load the pin map (physical pin to virtual pin)
|
||||
this->pinMap[i] = pinMap[i];
|
||||
// Load the virtual pin map (virtual pin to physical pin)
|
||||
this->virtualPinMap[pinMap[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DigitalInputCard::getType()
|
||||
{
|
||||
return CARD_TYPE_DIGITAL_INPUT;
|
||||
}
|
55
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputCard.hpp
Normal file
55
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputCard.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <PCF8574.h>
|
||||
#include <vector>
|
||||
|
||||
#define CARD_TYPE_DIGITAL_INPUT 0x01
|
||||
|
||||
class DigitalInputCard : public ExpansionCard {
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
DigitalInputCard(uint8_t address_a, uint8_t address_b);
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5);
|
||||
// Initialize the card
|
||||
bool begin();
|
||||
// Refresh and Read the input from the specified pin, always refresh the input buffers
|
||||
bool digitalRead(uint8_t pin);
|
||||
// Read the input from the specified pin, also refresh the input buffers if refresh is true
|
||||
bool digitalRead(uint8_t pin, bool refresh);
|
||||
// Preform a loop to refresh the input buffers
|
||||
void loop();
|
||||
// Get the input buffer for bank A
|
||||
uint8_t getInputBufferA();
|
||||
// Get the input buffer for bank B
|
||||
uint8_t getInputBufferB();
|
||||
// Set the debounce time for the specified pin
|
||||
void setDebounceTime(uint8_t pin, uint32_t debounceTime);
|
||||
// Register a callback function to be called when a pin changes
|
||||
void registerCallback(std::function<void(uint8_t, bool)> callback);
|
||||
// Unregister the callback function
|
||||
//void unregisterCallback(std::function<void(uint8_t, bool)> callback);
|
||||
// Load a new pin map
|
||||
void loadPinMap(uint8_t pinMap[16]);
|
||||
// Get type of card
|
||||
uint8_t getType();
|
||||
private:
|
||||
PCF8574 inputBankA;
|
||||
PCF8574 inputBankB;
|
||||
uint8_t address_a;
|
||||
uint8_t address_b;
|
||||
uint8_t inputBufferA;
|
||||
uint8_t inputBufferB;
|
||||
uint8_t previousInputBufferA;
|
||||
uint8_t previousInputBufferB;
|
||||
uint32_t debounceTime[16];
|
||||
uint32_t lastDebounceTime[16];
|
||||
// A map of the physical pin to the virtual pin
|
||||
uint8_t pinMap[16];
|
||||
// A map of the virtual pin to the physical pin
|
||||
uint8_t virtualPinMap[16];
|
||||
std::vector<std::function<void(uint8_t, bool)>> callbacks;
|
||||
void refreshInputBankA();
|
||||
void refreshInputBankB();
|
||||
void handlePinChange(int pin, uint8_t& currentBuffer, uint8_t& previousBuffer);
|
||||
};
|
68
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputIoT.cpp
Normal file
68
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputIoT.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include <DigitalInputIoT.hpp>
|
||||
|
||||
|
||||
bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->card = (DigitalInputCard *)card;
|
||||
this->card_id = card_id;
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->setDigitalInputsPublishEnabled(true);
|
||||
this->card->registerCallback(std::bind(&DigitalInputIoT::handleValueChange, this, std::placeholders::_1, std::placeholders::_2));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void DigitalInputIoT::subscribe() {
|
||||
char topic[64];
|
||||
sprintf(topic, "%s/%d/%s", this->base_topic, this->card_id, PUBLISH_ENABLE_TOPIC);
|
||||
this->subscribeRelative(topic);
|
||||
}
|
||||
|
||||
void DigitalInputIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
// payload is char '0' or '1'
|
||||
if (!strcmp(topic, PUBLISH_ENABLE_TOPIC)) {
|
||||
if (payload[0] == '1') {
|
||||
this->setDigitalInputsPublishEnabled(true);
|
||||
} else {
|
||||
this->setDigitalInputsPublishEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DigitalInputIoT::publishDigitalInputs() {
|
||||
if (!this->digital_inputs_publish_enabled) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
this->publishDigitalInput(i);
|
||||
}
|
||||
}
|
||||
void DigitalInputIoT::setDigitalInputsPublishEnabled(bool enabled) {
|
||||
this->digital_inputs_publish_enabled = enabled;
|
||||
if (enabled) {
|
||||
this->publishDigitalInputs();
|
||||
}
|
||||
}
|
||||
void DigitalInputIoT::handleValueChange(uint8_t pin, uint8_t value) {
|
||||
if (this->digital_inputs_publish_enabled) {
|
||||
this->publishDigitalInput(pin);
|
||||
}
|
||||
|
||||
}
|
||||
void DigitalInputIoT::publishReport() {
|
||||
this->publishDigitalInputs();
|
||||
}
|
||||
uint8_t DigitalInputIoT::getType() {
|
||||
return CARD_TYPE_DIGITAL_INPUT;
|
||||
}
|
||||
|
||||
|
||||
void DigitalInputIoT::publishDigitalInput(uint8_t pin) {
|
||||
char topic[20] = {0};
|
||||
char payload[20] = {0};
|
||||
topic[0] = pin-pin%10 + '0';
|
||||
topic[1] = pin%10 + '0';
|
||||
topic[2] = '\0';
|
||||
payload[0] = this->card->digitalRead(pin, false) + '0';
|
||||
payload[1] = '\0';
|
||||
this->publishRelative(topic, payload);
|
||||
}
|
23
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputIoT.hpp
Normal file
23
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalInputIoT.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <IoTComponent.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <FRAM.h>
|
||||
|
||||
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
||||
|
||||
class DigitalInputIoT : public IoTComponent {
|
||||
public:
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishDigitalInputs();
|
||||
void publishDigitalInput(uint8_t pin);
|
||||
void setDigitalInputsPublishEnabled(bool enabled);
|
||||
void handleValueChange(uint8_t pin, uint8_t value);
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
uint8_t getType();
|
||||
private:
|
||||
bool digital_inputs_publish_enabled = false;
|
||||
DigitalInputCard *card;
|
||||
};
|
178
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputCard.cpp
Normal file
178
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputCard.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
DigitalOutputCard::DigitalOutputCard(uint8_t address) {
|
||||
this->address = address;
|
||||
// load default pin map
|
||||
for (int i = 0; i < 16; i++) {
|
||||
this->pinMap[i] = i;
|
||||
this->virtualPinMap[i] = i;
|
||||
}
|
||||
}
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
DigitalOutputCard::DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4) {
|
||||
this->address = 0x20;
|
||||
if (bit0) this->address += 1;
|
||||
if (bit1) this->address += 2;
|
||||
if (bit2) this->address += 4;
|
||||
if (bit3) this->address += 8;
|
||||
if (bit4) this->address += 16;
|
||||
|
||||
}
|
||||
// Initialize the card
|
||||
bool DigitalOutputCard::begin() {
|
||||
this->pwm = Adafruit_PWMServoDriver(this->address);
|
||||
this->pwm.begin();
|
||||
pwm.setOutputMode(true);
|
||||
// Output card don't send ack, we can't check if it's connected
|
||||
// so we just return true
|
||||
return true;
|
||||
}
|
||||
// Set the output to the specified state
|
||||
void DigitalOutputCard::digitalWrite(uint8_t pin, bool state) {
|
||||
this->pwm.setPin(virtualPinMap[pin], state ? 4095 : 0);
|
||||
this->state_buffer[pin] = state;
|
||||
this->value_buffer[pin] = state ? 4095 : 0;
|
||||
if (this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
for (int i = 0; i < change_callbacks.size(); i++) {
|
||||
change_callbacks[i](pin, state, state ? 4095 : 0);
|
||||
}
|
||||
}
|
||||
// Set the output to the specified pwm value
|
||||
void DigitalOutputCard::analogWrite(uint8_t pin, uint16_t value) {
|
||||
// If value is greater than 4095, set it to 4095
|
||||
if (value > 4095) value = 4095;
|
||||
// Set the pwm value
|
||||
this->pwm.setPin(virtualPinMap[pin], value);
|
||||
if (this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
this->state_buffer[pin] = value > 0;
|
||||
this->value_buffer[pin] = value;
|
||||
for (int i = 0; i < change_callbacks.size(); i++) {
|
||||
change_callbacks[i](pin, value > 0, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy loop function
|
||||
void DigitalOutputCard::loop() {
|
||||
}
|
||||
|
||||
// Get the state of the specified pin
|
||||
bool DigitalOutputCard::getState(uint8_t pin) {
|
||||
return this->state_buffer[pin];
|
||||
}
|
||||
|
||||
// Get the pwm value of the specified pin
|
||||
uint16_t DigitalOutputCard::getValue(uint8_t pin) {
|
||||
return this->value_buffer[pin];
|
||||
}
|
||||
|
||||
// Get type of card
|
||||
uint8_t DigitalOutputCard::getType() {
|
||||
return CARD_TYPE_DIGITAL_OUTPUT;
|
||||
}
|
||||
|
||||
void DigitalOutputCard::setState(uint8_t pin, bool state) {
|
||||
this-> state_buffer[pin] = state;
|
||||
this->pwm.setPin(pin, state*value_buffer[pin]);
|
||||
if(this->framAutoSave) {
|
||||
this->saveStateToFRAM();
|
||||
}
|
||||
for(int i = 0; i < change_callbacks.size(); i++) {
|
||||
change_callbacks[i](pin, state, value_buffer[pin]);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputCard::setValue(uint8_t pin, uint16_t value) {
|
||||
// If value is greater than 4095, set it to 4095
|
||||
if (value > 4095) value = 4095;
|
||||
this-> value_buffer[pin] = value;
|
||||
this->pwm.setPin(pin, state_buffer[pin]*value);
|
||||
if (this->framAutoSave) {
|
||||
this->savePinValueToFRAM(pin);
|
||||
}
|
||||
for (int i = 0; i < change_callbacks.size(); i++) {
|
||||
change_callbacks[i](pin, state_buffer[pin], value);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputCard::registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback) {
|
||||
this->change_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
// void DigitalOutputCard::deregisterChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback) {
|
||||
// for(int i = 0; i < change_callbacks.size(); i++) {
|
||||
// if(change_callbacks[i].target<void(uint8_t, bool, uint16_t)>() == callback.target<void(uint8_t, bool, uint16_t)>()) {
|
||||
// change_callbacks.erase(change_callbacks.begin()+i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
void DigitalOutputCard::loadPinMap(uint8_t pinMap[16]) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->pinMap[i] = pinMap[i];
|
||||
this->virtualPinMap[pinMap[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputCard::bindFRAM(FRAM *fram, uint16_t address) {
|
||||
this->fram = fram;
|
||||
this->framBinded = true;
|
||||
this->framAddress = address;
|
||||
}
|
||||
|
||||
uint16_t DigitalOutputCard::packStates() {
|
||||
uint16_t packed = 0;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
packed |= (state_buffer[i] << i);
|
||||
}
|
||||
return packed;
|
||||
}
|
||||
|
||||
void DigitalOutputCard::unpackStates(uint16_t states) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->setState(i, (states >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputCard::saveToFRAM() {
|
||||
if(!framBinded) return;
|
||||
// Save the state
|
||||
uint16_t packed = packStates();
|
||||
this->fram->write16(framAddress, packed);
|
||||
// Save the value
|
||||
this->fram->write(framAddress+2, (uint8_t*)value_buffer, 32);
|
||||
}
|
||||
|
||||
void DigitalOutputCard::loadFromFRAM() {
|
||||
if(!framBinded) return;
|
||||
// Load the state
|
||||
uint16_t packed = this->fram->read16(framAddress);
|
||||
unpackStates(packed);
|
||||
// Load the value
|
||||
uint16_t value[16];
|
||||
this->fram->read(framAddress+2, (uint8_t*)value, 32);
|
||||
for(int i = 0; i < 16; i++) {
|
||||
this->setValue(i, value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputCard::setAutoSaveToFRAM(bool autoSave) {
|
||||
this->framAutoSave = autoSave;
|
||||
}
|
||||
|
||||
void DigitalOutputCard::savePinValueToFRAM(uint8_t pin) {
|
||||
if(!framBinded) return;
|
||||
this->fram->write(framAddress+2+pin*2, (uint8_t*)&value_buffer[pin], 2);
|
||||
}
|
||||
|
||||
void DigitalOutputCard::saveStateToFRAM() {
|
||||
if(!framBinded) return;
|
||||
uint16_t packed = packStates();
|
||||
this->fram->write16(framAddress, packed);
|
||||
}
|
102
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputCard.hpp
Normal file
102
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputCard.hpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
#include <FRAM.h>
|
||||
#include <vector>
|
||||
|
||||
// Protocol for digital output card
|
||||
// Note that pin is always 2 characters long and padded with 0 if necessary
|
||||
// Set pin state topic: <pin>/set/state payload: 0/1
|
||||
// Set pin pwm topic: <pin>/set/value payload: 0-4095
|
||||
// Publish pin state topic: <pin>/state payload: 0/1
|
||||
// Publish pin pwm topic: <pin>/value payload: 0-4095
|
||||
// Publish all topic: requeststate payload: N/A
|
||||
// Enable/disable publish topic: publish_enable payload: 0/1
|
||||
|
||||
#define SET_STATE_TOPIC "/set/state"
|
||||
#define SET_VALUE_TOPIC "/set/value"
|
||||
#define STATE_TOPIC "/state"
|
||||
#define VALUE_TOPIC "/value"
|
||||
#define REQUEST_STATE_TOPIC "requeststate"
|
||||
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
||||
|
||||
#define CARD_TYPE_DIGITAL_OUTPUT 0x00
|
||||
|
||||
class DigitalOutputCard : public ExpansionCard
|
||||
{
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
DigitalOutputCard(uint8_t address);
|
||||
// Instantiate the card with the specified position on the dip switch
|
||||
DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4);
|
||||
// Initialize the card
|
||||
bool begin();
|
||||
// Dummy loop function
|
||||
void loop();
|
||||
// Set the output to the specified state
|
||||
// This function set both the state and the pwm value
|
||||
void digitalWrite(uint8_t pin, bool state);
|
||||
// Set the output to the specified pwm value
|
||||
// This function set both the state and the pwm value
|
||||
void analogWrite(uint8_t pin, uint16_t value);
|
||||
// Set the state of the specified pin
|
||||
void setState(uint8_t pin, bool state);
|
||||
// Set the pwm value of the specified pin
|
||||
void setValue(uint8_t pin, uint16_t value);
|
||||
// Get the state of the specified pin
|
||||
bool getState(uint8_t pin);
|
||||
// Get the pwm value of the specified pin
|
||||
uint16_t getValue(uint8_t pin);
|
||||
// Register a callback function that will be called when the state of a pin changes
|
||||
void registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
// Deregister the callback function
|
||||
// void deregisterChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
// Load a new pin map
|
||||
void loadPinMap(uint8_t pinMap[16]);
|
||||
// Bind the fram object to the card
|
||||
// The Output card use the fram in this layout:
|
||||
// [2 bytes] 0-1 : state
|
||||
// [32 bytes] 2-33 : value
|
||||
void bindFRAM(FRAM *fram, uint16_t address);
|
||||
// Save the state and value to the fram
|
||||
void saveToFRAM();
|
||||
// Load the state and value from the fram
|
||||
void loadFromFRAM();
|
||||
// Set the auto save to fram flag
|
||||
void setAutoSaveToFRAM(bool autoSave);
|
||||
// Save a single pin value to fram
|
||||
void savePinValueToFRAM(uint8_t pin);
|
||||
// Save state to fram
|
||||
void saveStateToFRAM();
|
||||
// Save value to fram
|
||||
void saveValueToFRAM();
|
||||
// Get type of card
|
||||
uint8_t getType();
|
||||
private:
|
||||
// FRAM address
|
||||
uint16_t framAddress;
|
||||
// FRAM is binded
|
||||
bool framBinded = false;
|
||||
// Auto save to fram
|
||||
bool framAutoSave = false;
|
||||
// The fram object pointer
|
||||
FRAM *fram;
|
||||
// The pwm driver
|
||||
Adafruit_PWMServoDriver pwm;
|
||||
// The address of the card
|
||||
uint8_t address;
|
||||
// The state of the card
|
||||
bool state_buffer[16];
|
||||
// The pwm value of the card
|
||||
uint16_t value_buffer[16];
|
||||
// The callback function
|
||||
std::vector<std::function<void(uint8_t, bool, uint16_t)>> change_callbacks;
|
||||
// Physical pin to virtual pin map
|
||||
uint8_t pinMap[16];
|
||||
// Return 16 bit value representing all 16 channels
|
||||
uint16_t packStates();
|
||||
// Unpack the 16 bit value to the state buffer
|
||||
void unpackStates(uint16_t states);
|
||||
// Virtual pin to physical pin map
|
||||
uint8_t virtualPinMap[16];
|
||||
};
|
197
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputIoT.cpp
Normal file
197
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputIoT.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include <DigitalOutputIoT.hpp>
|
||||
|
||||
DigitalOutputIoT::DigitalOutputIoT() {
|
||||
this->state_report_topic = new char[10];
|
||||
this->value_report_topic = new char[10];
|
||||
this->digital_outputs_publish_enabled = true;
|
||||
}
|
||||
|
||||
DigitalOutputIoT::~DigitalOutputIoT() {
|
||||
delete[] this->state_report_topic;
|
||||
delete[] this->value_report_topic;
|
||||
}
|
||||
|
||||
bool DigitalOutputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||
this->mqtt = mqtt;
|
||||
this->base_topic = base_topic;
|
||||
this->card = (DigitalOutputCard *) card;
|
||||
this-> card_id = card_id;
|
||||
this->set_state_length = strlen(SET_STATE_TOPIC);
|
||||
this->set_value_length = strlen(SET_VALUE_TOPIC);
|
||||
this->state_length = strlen(STATE_TOPIC);
|
||||
this->value_length = strlen(VALUE_TOPIC);
|
||||
this->request_state_length = strlen(REQUEST_STATE_TOPIC);
|
||||
this->publish_enable_length = strlen(PUBLISH_ENABLE_TOPIC);
|
||||
strcpy(this->state_report_topic, "00/state");
|
||||
strcpy(this->value_report_topic, "00/value");
|
||||
// Register callbacks
|
||||
auto bindedCallback = std::bind(&DigitalOutputIoT::handleValueChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
this->card->registerChangeCallback(bindedCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Protocol for digital output card
|
||||
// Note that pin is always 2 characters long and padded with 0 if necessary
|
||||
// Set pin state topic: <pin>/set/state payload: 0/1
|
||||
// Set pin pwm topic: <pin>/set/value payload: 0-4095
|
||||
// Publish pin state topic: <pin>/state payload: 0/1
|
||||
// Publish pin pwm topic: <pin>/value payload: 0-4095
|
||||
// Publish all topic: requeststate payload: N/A
|
||||
// Enable/disable publish topic: publish_enable payload: 0/1
|
||||
void DigitalOutputIoT::handleMqttMessage(char *topic, char *payload) {
|
||||
uint8_t topic_length = strlen(topic);
|
||||
if(this-> processSetStateMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processSetValueMessage(topic, payload, topic_length)) return;
|
||||
if(this-> processRequestStateMessage(topic, payload, topic_length)) return;
|
||||
}
|
||||
|
||||
bool DigitalOutputIoT::processSetStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Check if the topic is a set state topic
|
||||
// The correct format is <pin>/set/state
|
||||
// This mean that the topic must end with /set/state
|
||||
// Check if the 3rd character is /
|
||||
if(topic[2] != '/') {
|
||||
return false;
|
||||
}
|
||||
// The topic must be set_state_length + 2 characters long
|
||||
if(topic_length != set_state_length + 2) {
|
||||
return false;
|
||||
}
|
||||
// Check if the topic ends with /set/state
|
||||
if (!strncmp(topic+2, SET_STATE_TOPIC, set_state_length)) {
|
||||
// Get the pin number
|
||||
uint8_t pin = (topic[0] - '0')*10 + (topic[1] - '0');
|
||||
// Get the state
|
||||
bool state = false;
|
||||
char state_char = payload[0];
|
||||
if (state_char == '0') {
|
||||
state = false;
|
||||
} else if (state_char == '1') {
|
||||
state = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// Set the state
|
||||
card->setState(pin, state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DigitalOutputIoT::processSetValueMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Check if the topic is a set value topic
|
||||
// The correct format is <pin>/set/value
|
||||
// This mean that the topic must end with /set/value
|
||||
// Check if the 3rd character is /
|
||||
if(topic[2] != '/') {
|
||||
return false;
|
||||
}
|
||||
// The topic must be set_value_length + 2 characters long
|
||||
if(topic_length != set_value_length + 2) {
|
||||
return false;
|
||||
}
|
||||
// Check if the topic ends with /set/value
|
||||
if (!strncmp(topic+2, SET_VALUE_TOPIC, set_value_length)) {
|
||||
// Get the pin number
|
||||
uint8_t pin = (topic[0] - '0')*10 + (topic[1] - '0');
|
||||
// Get the value
|
||||
uint16_t value = atoi(payload);
|
||||
// Set the value
|
||||
card->setValue(pin, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DigitalOutputIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||
// Check if the topic is a request state topic
|
||||
// The correct format is requeststate
|
||||
// This mean that the topic must be request_state_length characters long
|
||||
// The topic must be request_state_length characters long
|
||||
if(topic_length != request_state_length) {
|
||||
return false;
|
||||
}
|
||||
// Check if the topic is requeststate
|
||||
if (!strncmp(topic, REQUEST_STATE_TOPIC, request_state_length)) {
|
||||
// Publish the state of all pins
|
||||
publishDigitalOutputs();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::publishDigitalOutputs() {
|
||||
if(!digital_outputs_publish_enabled) return;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
publishDigitalOutput(i);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::publishDigitalOutput(uint8_t pin) {
|
||||
publishDigitalOutputState(pin);
|
||||
publishDigitalOutputValue(pin);
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::publishDigitalOutputState(uint8_t pin) {
|
||||
if(!digital_outputs_publish_enabled) return;
|
||||
state_report_topic[0] = pin / 10 + '0';
|
||||
state_report_topic[1] = pin % 10 + '0';
|
||||
publishRelative(state_report_topic, card->getState(pin) ? "1" : "0");
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::publishDigitalOutputValue(uint8_t pin) {
|
||||
if(!digital_outputs_publish_enabled) return;
|
||||
value_report_topic[0] = pin / 10 + '0';
|
||||
value_report_topic[1] = pin % 10 + '0';
|
||||
char payload[5];
|
||||
sprintf(payload, "%d", card->getValue(pin));
|
||||
publishRelative(value_report_topic, payload);
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::setDigitalOutputsPublishEnabled(bool enabled) {
|
||||
digital_outputs_publish_enabled = enabled;
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::handleValueChange(uint8_t pin, bool state, uint16_t value) {
|
||||
publishDigitalOutput(pin);
|
||||
if(value_change_callback != NULL) {
|
||||
value_change_callback(pin, state, value);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback) {
|
||||
value_change_callback = callback;
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::deregisterChangeCallback() {
|
||||
value_change_callback = NULL;
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::publishReport() {
|
||||
publishDigitalOutputs();
|
||||
}
|
||||
|
||||
uint8_t DigitalOutputIoT::getType() {
|
||||
return CARD_TYPE_DIGITAL_OUTPUT;
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::subscribe() {
|
||||
char topic[20];
|
||||
// Subscribe to all set state topics
|
||||
for(int i = 0; i < 16; i++) {
|
||||
sprintf(topic, "%02d/set/state", i);
|
||||
subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to all set value topics
|
||||
for(int i = 0; i < 16; i++) {
|
||||
sprintf(topic, "%02d/set/value", i);
|
||||
subscribeRelative(topic);
|
||||
}
|
||||
// Subscribe to request state topic
|
||||
subscribeRelative(REQUEST_STATE_TOPIC);
|
||||
// Subscribe to publish enable topic
|
||||
subscribeRelative(PUBLISH_ENABLE_TOPIC);
|
||||
}
|
||||
|
||||
void DigitalOutputIoT::loop() {
|
||||
}
|
39
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputIoT.hpp
Normal file
39
ESPMegaPRO-firmware/lib/ESPMegaPRO/DigitalOutputIoT.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include <IoTComponent.hpp>
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
class DigitalOutputIoT : public IoTComponent {
|
||||
public:
|
||||
DigitalOutputIoT();
|
||||
~DigitalOutputIoT();
|
||||
bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
void handleMqttMessage(char *topic, char *payload);
|
||||
void publishDigitalOutputs();
|
||||
void publishDigitalOutput(uint8_t pin);
|
||||
void publishDigitalOutputState(uint8_t pin);
|
||||
void publishDigitalOutputValue(uint8_t pin);
|
||||
void setDigitalOutputsPublishEnabled(bool enabled);
|
||||
void handleValueChange(uint8_t pin, bool state, uint16_t value);
|
||||
void registerChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback);
|
||||
void deregisterChangeCallback();
|
||||
void publishReport();
|
||||
void subscribe();
|
||||
void loop();
|
||||
uint8_t getType();
|
||||
private:
|
||||
bool digital_outputs_publish_enabled = false;
|
||||
bool processSetStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processSetValueMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length);
|
||||
std::function<void(uint8_t, bool, uint16_t)> value_change_callback;
|
||||
DigitalOutputCard *card;
|
||||
char *state_report_topic;
|
||||
char *value_report_topic;
|
||||
uint8_t set_state_length;
|
||||
uint8_t set_value_length;
|
||||
uint8_t state_length;
|
||||
uint8_t value_length;
|
||||
uint8_t request_state_length;
|
||||
uint8_t publish_enable_length;
|
||||
};
|
348
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp
Normal file
348
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp
Normal file
|
@ -0,0 +1,348 @@
|
|||
#include <ESPMegaDisplay.hpp>
|
||||
|
||||
/**
|
||||
* @brief Receives and processes serial commands from the display adapter.
|
||||
* @param process Flag indicating whether to process the received commands.
|
||||
* @return True if data is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::recieveSerialCommand(bool process){
|
||||
bool dataRecieved = false;
|
||||
// Read the serial buffer if available
|
||||
while(displayAdapter->available()) {
|
||||
rx_buffer[rx_buffer_index] = displayAdapter->read();
|
||||
rx_buffer_index++;
|
||||
// Check for overflow
|
||||
if(rx_buffer_index>=256){
|
||||
rx_buffer_index = 0;
|
||||
}
|
||||
if(process) this-> processSerialCommand();
|
||||
dataRecieved = true;
|
||||
}
|
||||
return dataRecieved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives and processes serial commands from the display adapter.
|
||||
* @return True if data is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::recieveSerialCommand(){
|
||||
return recieveSerialCommand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the received serial command.
|
||||
*/
|
||||
void ESPMegaDisplay::processSerialCommand(){
|
||||
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
|
||||
if(!payloadIsValid()) return;
|
||||
// The rx buffer ended with stop bytes
|
||||
// Check if the rx buffer is a push payload
|
||||
// The payload type is the first byte of the payload
|
||||
// 0x00 is invalid instruction
|
||||
// 0x01 is success
|
||||
// 0x65 is a touch event
|
||||
// 0x66 is a page report event
|
||||
if(rx_buffer[0]==0x65){
|
||||
processTouchPayload();
|
||||
}
|
||||
else if(rx_buffer[0]==0x66){
|
||||
processPageReportPayload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the touch event payload.
|
||||
*/
|
||||
void ESPMegaDisplay::processTouchPayload() {
|
||||
if (rx_buffer_index != 4) return;
|
||||
// The second byte of the payload is the page number
|
||||
uint8_t page = rx_buffer[1];
|
||||
// The third byte of the payload is the component id
|
||||
uint8_t component = rx_buffer[2];
|
||||
// The fourth byte of the payload is the event
|
||||
uint8_t event = rx_buffer[3];
|
||||
// 0x01 is press, 0x00 is release
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the page report event payload.
|
||||
*/
|
||||
void ESPMegaDisplay::processPageReportPayload() {
|
||||
// The second byte of the payload is the page number
|
||||
this->currentPage = rx_buffer[1];
|
||||
if(pageChangeCallback!=NULL){
|
||||
pageChangeCallback(this->currentPage);
|
||||
}
|
||||
rx_buffer_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends stop bytes to the display adapter.
|
||||
*/
|
||||
void ESPMegaDisplay::sendStopBytes() {
|
||||
displayAdapter->write(0xFF);
|
||||
displayAdapter->write(0xFF);
|
||||
displayAdapter->write(0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a command to the display adapter.
|
||||
* @param command The command to send.
|
||||
*/
|
||||
void ESPMegaDisplay::sendCommand(char* command) {
|
||||
displayAdapter->print(command);
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Jumps to the specified page on the display.
|
||||
* @param page The page number to jump to.
|
||||
*/
|
||||
void ESPMegaDisplay::jumpToPage(int page) {
|
||||
this->displayAdapter->print("page ");
|
||||
this->displayAdapter->print(page);
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a number component on the display.
|
||||
* @param component The component name.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void ESPMegaDisplay::setNumber(const char* component, int value) {
|
||||
this->displayAdapter->print(component);
|
||||
this->displayAdapter->print("=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a string component on the display.
|
||||
* @param component The component name.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void ESPMegaDisplay::setString(const char* component, const char* value) {
|
||||
this->displayAdapter->print(component);
|
||||
this->displayAdapter->print("=\"");
|
||||
this->displayAdapter->print(value);
|
||||
this->displayAdapter->print("\"");
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a number component from the display.
|
||||
* @param component The component name.
|
||||
* @return The value of the number component.
|
||||
*/
|
||||
uint32_t ESPMegaDisplay::getNumber(const char* component) {
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
// Wait for the response
|
||||
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return 0;
|
||||
// The rx buffer is valid
|
||||
// The expected payload is type 0x71
|
||||
if(rx_buffer[0]!=0x71) return 0;
|
||||
// The 2nd to 5th byte of the payload is the value
|
||||
// It's a 4 byte 32-bit value in little endian order.
|
||||
// Convert the 4 bytes to a 32-bit value
|
||||
uint32_t value = 0;
|
||||
value |= rx_buffer[1];
|
||||
value |= rx_buffer[2]<<8;
|
||||
value |= rx_buffer[3]<<16;
|
||||
value |= rx_buffer[4]<<24;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a string component from the display.
|
||||
* @param component The component name.
|
||||
* @return The value of the string component.
|
||||
* @note The returned char array must be freed after use.
|
||||
*/
|
||||
const char* ESPMegaDisplay::getString(const char* component) {
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
// Wait for the response
|
||||
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return "";
|
||||
// The rx buffer is valid
|
||||
// The expected payload is type 0x70
|
||||
if(rx_buffer[0]!=0x70) return "";
|
||||
// The 2nd bytes onwards before the stop bytes is the string
|
||||
// The length of the string is the length of the payload minus 4
|
||||
uint8_t length = rx_buffer_index-4;
|
||||
// First we malloc a char array with the length of the string
|
||||
char* value = (char*)malloc(length+1);
|
||||
// Copy the string from the rx buffer to the char array
|
||||
memcpy(value, rx_buffer+1, length);
|
||||
// Add the null terminator
|
||||
value[length] = '\0';
|
||||
// Return the char array
|
||||
return value;
|
||||
}
|
||||
|
||||
bool ESPMegaDisplay::getStringToBuffer(const char* component, char* buffer, uint8_t buffer_size) {
|
||||
uint32_t start = millis();
|
||||
// Send the get command
|
||||
this->displayAdapter->print("get ");
|
||||
this->displayAdapter->print(component);
|
||||
sendStopBytes();
|
||||
// Wait for the response
|
||||
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return false;
|
||||
// The rx buffer is valid
|
||||
// The expected payload is type 0x70
|
||||
if(rx_buffer[0]!=0x70) return false;
|
||||
// The 2nd bytes onwards before the stop bytes is the string
|
||||
// The length of the string is the length of the payload minus 4
|
||||
uint8_t length = rx_buffer_index-4;
|
||||
// Check if the buffer is large enough to hold the string
|
||||
if(length>buffer_size) return false;
|
||||
// Copy the string from the rx buffer to the char array
|
||||
memcpy(buffer, rx_buffer+1, length);
|
||||
// Add the null terminator
|
||||
buffer[length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for a valid payload from the display adapter.
|
||||
* @param timeout The timeout value in milliseconds.
|
||||
* @return True if a valid payload is received, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout) {
|
||||
uint32_t start = millis();
|
||||
// If the payload is not valid, keep reading the serial buffer until timeout
|
||||
while(!this->payloadIsValid()){
|
||||
if(millis()-start>timeout){
|
||||
return false;
|
||||
}
|
||||
recieveSerialCommand(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the received payload is valid.
|
||||
* @return True if the payload is valid, false otherwise.
|
||||
*/
|
||||
bool ESPMegaDisplay::payloadIsValid() {
|
||||
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
|
||||
if(rx_buffer_index<3) return false;
|
||||
if(rx_buffer[rx_buffer_index-1]!=0xFF) return false;
|
||||
if(rx_buffer[rx_buffer_index-2]!=0xFF) return false;
|
||||
if(rx_buffer[rx_buffer_index-3]!=0xFF) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the brightness of the display.
|
||||
* @param value The brightness value.
|
||||
*/
|
||||
void ESPMegaDisplay::setBrightness(int value) {
|
||||
this->displayAdapter->print("dim=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the volume of the display.
|
||||
* @param value The volume value.
|
||||
*/
|
||||
void ESPMegaDisplay::setVolume(int value) {
|
||||
this->displayAdapter->print("vol=");
|
||||
this->displayAdapter->print(value);
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restarts the display.
|
||||
*/
|
||||
void ESPMegaDisplay::reset() {
|
||||
// First we send a stop bytes to clear the serial buffer
|
||||
// This ensures that the display is ready to receive the reset command
|
||||
sendStopBytes();
|
||||
this->displayAdapter->print("rest");
|
||||
sendStopBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function that will be called when a push event is received.
|
||||
* @param callback The callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::registerPushCallback(std::function<void(uint8_t, uint8_t)> callback) {
|
||||
this->pushCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function that will be called when a pop event is received.
|
||||
* @param callback The callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::registerPopCallback(std::function<void(uint8_t, uint8_t)> callback) {
|
||||
this->popCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a callback function that will be called when a page change event is received.
|
||||
* @param callback The callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::registerPageChangeCallback(std::function<void(uint8_t)> callback) {
|
||||
this->pageChangeCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters the push callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterPushCallback() {
|
||||
this->pushCallback = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters the pop callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterPopCallback() {
|
||||
this->popCallback = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregisters the page change callback function.
|
||||
*/
|
||||
void ESPMegaDisplay::unregisterPageChangeCallback() {
|
||||
this->pageChangeCallback = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the ESPMegaDisplay class.
|
||||
* @param displayAdapter The serial adapter connected to the display.
|
||||
*/
|
||||
ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter) {
|
||||
this->displayAdapter = displayAdapter;
|
||||
this->currentPage = 0;
|
||||
this->rx_buffer_index = 0;
|
||||
this->pushCallback = NULL;
|
||||
this->popCallback = NULL;
|
||||
this->pageChangeCallback = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the display.
|
||||
*/
|
||||
void ESPMegaDisplay::begin() {
|
||||
this->displayAdapter->begin(115200);
|
||||
this->displayAdapter->setTimeout(100);
|
||||
this->displayAdapter->flush();
|
||||
this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main loop function of the display.
|
||||
*/
|
||||
void ESPMegaDisplay::loop() {
|
||||
// Check if there is data in the serial buffer
|
||||
// If there is data, process the data
|
||||
recieveSerialCommand();
|
||||
}
|
47
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.hpp
Normal file
47
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
#define DISPLAY_FETCH_TIMEOUT 100 // ms
|
||||
|
||||
class ESPMegaDisplay
|
||||
{
|
||||
public:
|
||||
ESPMegaDisplay(HardwareSerial *displayAdapter);
|
||||
void begin();
|
||||
void loop();
|
||||
void reset();
|
||||
void setBrightness(int value);
|
||||
void setVolume(int value);
|
||||
void jumpToPage(int page);
|
||||
void setString(const char* component, const char* value);
|
||||
void setNumber(const char* component, int value);
|
||||
const char* getString(const char* component);
|
||||
bool getStringToBuffer(const char* component, char* buffer, uint8_t buffer_size);
|
||||
uint32_t getNumber(const char* component);
|
||||
void handlePwmStateChange(uint8_t pin, uint16_t value);
|
||||
void handleInputStateChange(uint8_t pin, bool state);
|
||||
void registerPushCallback(std::function<void(uint8_t, uint8_t)> callback);
|
||||
void registerPopCallback(std::function<void(uint8_t, uint8_t)> callback);
|
||||
void registerPageChangeCallback(std::function<void(uint8_t)> callback);
|
||||
void unregisterPushCallback();
|
||||
void unregisterPopCallback();
|
||||
void unregisterPageChangeCallback();
|
||||
protected:
|
||||
uint8_t currentPage;
|
||||
uint8_t rx_buffer_index;
|
||||
char rx_buffer[256];
|
||||
char tx_buffer[256];
|
||||
bool recieveSerialCommand();
|
||||
bool recieveSerialCommand(bool process);
|
||||
void processSerialCommand();
|
||||
void processTouchPayload();
|
||||
void processPageReportPayload();
|
||||
void sendStopBytes();
|
||||
void sendCommand(char* command);
|
||||
bool payloadIsValid();
|
||||
bool waitForValidPayload(uint32_t timeout);
|
||||
HardwareSerial *displayAdapter;
|
||||
std::function<void(uint8_t, uint8_t)> pushCallback;
|
||||
std::function<void(uint8_t, uint8_t)> popCallback;
|
||||
std::function<void(uint8_t)> pageChangeCallback;
|
||||
};
|
409
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp
Normal file
409
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp
Normal file
|
@ -0,0 +1,409 @@
|
|||
#include <ESPMegaIoT.hpp>
|
||||
#include <ETH.h>
|
||||
#define NETWORK_CONFIG_ADDRESS 34
|
||||
#define MQTT_CONFIG_ADDRESS 34 + sizeof(NetworkConfig)
|
||||
|
||||
ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient)
|
||||
{
|
||||
tcpClient.setTimeout(1);
|
||||
// Initialize the components array
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
components[i] = NULL;
|
||||
}
|
||||
active = false;
|
||||
mqtt_connected = false;
|
||||
}
|
||||
|
||||
ESPMegaIoT::~ESPMegaIoT()
|
||||
{
|
||||
}
|
||||
|
||||
void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||
{
|
||||
// Create a null terminated string from the payload
|
||||
memcpy(payload_buffer, payload, length);
|
||||
payload_buffer[length] = '\0';
|
||||
// Remove the base topic from the topic
|
||||
char *topic_without_base = topic + strlen(base_topic) + 1;
|
||||
if (user_relative_mqtt_callback != NULL)
|
||||
{
|
||||
user_relative_mqtt_callback(topic_without_base + 3, payload_buffer);
|
||||
}
|
||||
if (user_mqtt_callback != NULL)
|
||||
{
|
||||
user_mqtt_callback(topic, payload_buffer);
|
||||
}
|
||||
// Call the respective card's mqtt callback
|
||||
// Note that after the base topic, there should be the card id
|
||||
// /base_topic/card_id/...
|
||||
// First, get the card id in integer form
|
||||
char *card_id_str = strtok(topic_without_base, "/");
|
||||
uint8_t card_id = atoi(card_id_str);
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
components[card_id]->handleMqttMessage(topic_without_base + 3, payload_buffer);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::setBaseTopic(char *base_topic)
|
||||
{
|
||||
strcpy(this->base_topic, base_topic);
|
||||
base_topic_length = strlen(base_topic);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::intr_begin(ExpansionCard *cards[])
|
||||
{
|
||||
this->cards = cards;
|
||||
active = true;
|
||||
}
|
||||
void ESPMegaIoT::loop()
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
// Call each component's loop function
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->loop();
|
||||
}
|
||||
}
|
||||
mqtt.loop();
|
||||
sessionKeepAlive();
|
||||
}
|
||||
|
||||
// Register Existing Card for use with IoT
|
||||
void ESPMegaIoT::registerCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is already registered
|
||||
if (components[card_id] != NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Get the card type
|
||||
uint8_t card_type = cards[card_id]->getType();
|
||||
// Create the respective IoT component
|
||||
switch (card_type)
|
||||
{
|
||||
case CARD_TYPE_ANALOG:
|
||||
components[card_id] = new AnalogIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_DIGITAL_INPUT:
|
||||
components[card_id] = new DigitalInputIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
case CARD_TYPE_DIGITAL_OUTPUT:
|
||||
components[card_id] = new DigitalOutputIoT();
|
||||
components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic);
|
||||
if (mqtt_connected)
|
||||
{
|
||||
components[card_id]->subscribe();
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
break;
|
||||
// case CARD_TYPE_CLIMATE:
|
||||
// components[card_id] = new ClimateIoT();
|
||||
// components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic);
|
||||
// if (mqtt_connected)
|
||||
// {
|
||||
// components[card_id]->subscribe();
|
||||
// components[card_id]->publishReport();
|
||||
// }
|
||||
// break;
|
||||
default:
|
||||
Serial.println("Unsupported card type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
void ESPMegaIoT::deregisterCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Delete the IoT component
|
||||
delete components[card_id];
|
||||
components[card_id] = NULL;
|
||||
}
|
||||
void ESPMegaIoT::publishCard(uint8_t card_id)
|
||||
{
|
||||
// Check if the card is registered
|
||||
if (components[card_id] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Publish the card
|
||||
components[card_id]->publishReport();
|
||||
}
|
||||
void ESPMegaIoT::subscribeToTopic(char *topic)
|
||||
{
|
||||
mqtt.subscribe(topic);
|
||||
}
|
||||
void ESPMegaIoT::unsubscribeFromTopic(char *topic)
|
||||
{
|
||||
mqtt.unsubscribe(topic);
|
||||
}
|
||||
void ESPMegaIoT::connectToWifi(char *ssid, char *password)
|
||||
{
|
||||
WiFi.begin(ssid, password);
|
||||
}
|
||||
void ESPMegaIoT::connectToWifi(char *ssid)
|
||||
{
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
void ESPMegaIoT::disconnectFromWifi()
|
||||
{
|
||||
WiFi.disconnect();
|
||||
}
|
||||
bool ESPMegaIoT::wifiConnected()
|
||||
{
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
}
|
||||
bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password)
|
||||
{
|
||||
mqtt.setServer(mqtt_server, mqtt_port);
|
||||
auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
mqtt.setCallback(boundCallback);
|
||||
if (mqtt.connect(client_id, mqtt_user, mqtt_password))
|
||||
{
|
||||
sessionKeepAlive();
|
||||
mqttSubscribe();
|
||||
// Publish all cards
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->publishReport();
|
||||
}
|
||||
}
|
||||
mqtt_connected = true;
|
||||
return true;
|
||||
}
|
||||
mqtt_connected = false;
|
||||
return false;
|
||||
}
|
||||
bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port)
|
||||
{
|
||||
mqtt.setServer(mqtt_server, mqtt_port);
|
||||
auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
mqtt.setCallback(boundCallback);
|
||||
if (mqtt.connect(client_id))
|
||||
{
|
||||
sessionKeepAlive();
|
||||
mqttSubscribe();
|
||||
// Publish all cards
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->publishReport();
|
||||
}
|
||||
}
|
||||
mqtt_connected = true;
|
||||
return true;
|
||||
}
|
||||
Serial.println("Failed to connect to mqtt");
|
||||
mqtt_connected = false;
|
||||
return false;
|
||||
}
|
||||
void ESPMegaIoT::disconnectFromMqtt()
|
||||
{
|
||||
mqtt.disconnect();
|
||||
}
|
||||
void ESPMegaIoT::publishToTopic(char *topic, char *payload)
|
||||
{
|
||||
mqtt.publish(topic, payload);
|
||||
}
|
||||
void ESPMegaIoT::registerMqttCallback(void (*callback)(char *, char *))
|
||||
{
|
||||
user_mqtt_callback = callback;
|
||||
}
|
||||
void ESPMegaIoT::mqttSubscribe()
|
||||
{
|
||||
if (user_subscribe_callback != NULL)
|
||||
{
|
||||
user_subscribe_callback();
|
||||
}
|
||||
// Subscribe to all topics
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
if (components[i] != NULL)
|
||||
{
|
||||
components[i]->subscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic);
|
||||
mqtt.publish(absolute_topic, payload);
|
||||
}
|
||||
|
||||
bool ESPMegaIoT::mqttReconnect()
|
||||
{
|
||||
if (this->mqtt_config.mqtt_useauth)
|
||||
{
|
||||
return this->connectToMqtt(this->network_config.hostname, this->mqtt_config.mqtt_server, this->mqtt_config.mqtt_port, this->mqtt_config.mqtt_user, this->mqtt_config.mqtt_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->connectToMqtt(this->network_config.hostname, this->mqtt_config.mqtt_server, this->mqtt_config.mqtt_port);
|
||||
}
|
||||
}
|
||||
|
||||
void ESPMegaIoT::sessionKeepAlive()
|
||||
{
|
||||
static unsigned long lastSessionKeepAlive = 0;
|
||||
if (millis() - lastSessionKeepAlive > 30000)
|
||||
{
|
||||
lastSessionKeepAlive = millis();
|
||||
// Check if mqtt is connected
|
||||
if (!mqtt.connected())
|
||||
{
|
||||
// Try to reconnect
|
||||
mqtt_connected = mqttReconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ESPMegaIoT::registerRelativeMqttCallback(void (*callback)(char *, char *))
|
||||
{
|
||||
user_relative_mqtt_callback = callback;
|
||||
}
|
||||
|
||||
void ESPMegaIoT::publishRelative(char *topic, char *payload)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%s", base_topic, topic);
|
||||
mqtt.publish(absolute_topic, payload);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::subscribeRelative(char *topic)
|
||||
{
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%s", base_topic, topic);
|
||||
mqtt.subscribe(absolute_topic);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::registerSubscribeCallback(void (*callback)(void))
|
||||
{
|
||||
user_subscribe_callback = callback;
|
||||
}
|
||||
|
||||
void ESPMegaIoT::setNetworkConfig(NetworkConfig network_config)
|
||||
{
|
||||
this->network_config = network_config;
|
||||
}
|
||||
|
||||
void ESPMegaIoT::loadNetworkConfig()
|
||||
{
|
||||
// Load the network config from FRAM
|
||||
fram->read(0, (uint8_t *)&network_config, sizeof(NetworkConfig));
|
||||
}
|
||||
|
||||
void ESPMegaIoT::saveNetworkConfig()
|
||||
{
|
||||
// Save the network config to FRAM
|
||||
fram->write(NETWORK_CONFIG_ADDRESS, (uint8_t *)&network_config, sizeof(NetworkConfig));
|
||||
}
|
||||
|
||||
void ESPMegaIoT::ethernetBegin()
|
||||
{
|
||||
ethernetIface->setHostname(network_config.hostname);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::loadMqttConfig()
|
||||
{
|
||||
// Load the mqtt config from FRAM
|
||||
fram->read(sizeof(NetworkConfig), (uint8_t *)&this->mqtt_config, sizeof(MqttConfig));
|
||||
// Populate the mqtt connection parameters
|
||||
}
|
||||
|
||||
void ESPMegaIoT::saveMqttConfig()
|
||||
{
|
||||
fram->write(MQTT_CONFIG_ADDRESS, (uint8_t *)&mqtt_config, sizeof(MqttConfig));
|
||||
}
|
||||
|
||||
void ESPMegaIoT::connectToMqtt()
|
||||
{
|
||||
if (mqtt_config.mqtt_useauth)
|
||||
this->connectToMqtt(network_config.hostname, mqtt_config.mqtt_server, mqtt_config.mqtt_port, mqtt_config.mqtt_user, mqtt_config.mqtt_password);
|
||||
else
|
||||
this->connectToMqtt(network_config.hostname, mqtt_config.mqtt_server, mqtt_config.mqtt_port);
|
||||
}
|
||||
|
||||
void ESPMegaIoT::connectNetwork()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
{
|
||||
if (network_config.wifiUseAuth)
|
||||
this->connectToWifi(network_config.ssid, network_config.password);
|
||||
else
|
||||
this->connectToWifi(network_config.ssid);
|
||||
if (network_config.useStaticIp)
|
||||
WiFi.config(network_config.ip, network_config.gateway, network_config.subnet);
|
||||
else
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ethernetBegin();
|
||||
if (network_config.useStaticIp)
|
||||
ethernetIface->config(network_config.ip, network_config.gateway, network_config.subnet, network_config.dns1, network_config.dns2);
|
||||
}
|
||||
}
|
||||
|
||||
void ESPMegaIoT::setMqttConfig(MqttConfig mqtt_config)
|
||||
{
|
||||
this->mqtt_config = mqtt_config;
|
||||
}
|
||||
|
||||
void ESPMegaIoT::bindEthernetInterface(ETHClass *ethernetIface)
|
||||
{
|
||||
this->ethernetIface = ethernetIface;
|
||||
}
|
||||
|
||||
IoTComponent *ESPMegaIoT::getComponent(uint8_t card_id)
|
||||
{
|
||||
return components[card_id];
|
||||
}
|
||||
|
||||
NetworkConfig* ESPMegaIoT::getNetworkConfig()
|
||||
{
|
||||
return &network_config;
|
||||
}
|
||||
|
||||
MqttConfig* ESPMegaIoT::getMqttConfig()
|
||||
{
|
||||
return &mqtt_config;
|
||||
}
|
||||
|
||||
bool ESPMegaIoT::mqttConnected()
|
||||
{
|
||||
return mqtt_connected;
|
||||
}
|
||||
|
||||
bool ESPMegaIoT::networkConnected()
|
||||
{
|
||||
if (network_config.useWifi)
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
else
|
||||
return ethernetIface->linkUp();
|
||||
}
|
114
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp
Normal file
114
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <AnalogIoT.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalInputIoT.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <DigitalOutputIoT.hpp>
|
||||
#include <IoTComponent.hpp>
|
||||
#include <PubSubClient.h>
|
||||
#include <ETH.h>
|
||||
#include <WiFi.h>
|
||||
#include <FRAM.h>
|
||||
|
||||
struct NetworkConfig
|
||||
{
|
||||
IPAddress ip;
|
||||
IPAddress gateway;
|
||||
IPAddress subnet;
|
||||
IPAddress dns1;
|
||||
IPAddress dns2;
|
||||
char hostname[32];
|
||||
bool useStaticIp;
|
||||
bool useWifi;
|
||||
bool wifiUseAuth;
|
||||
char ssid[32];
|
||||
char password[32];
|
||||
};
|
||||
|
||||
struct MqttConfig
|
||||
{
|
||||
char mqtt_server[32];
|
||||
uint16_t mqtt_port;
|
||||
char mqtt_user[32];
|
||||
char mqtt_password[32];
|
||||
bool mqtt_useauth;
|
||||
char base_topic[32];
|
||||
};
|
||||
|
||||
class ESPMegaIoT
|
||||
{
|
||||
public:
|
||||
ESPMegaIoT();
|
||||
~ESPMegaIoT();
|
||||
void intr_begin(ExpansionCard *cards[]);
|
||||
void loop();
|
||||
void registerCard(uint8_t card_id);
|
||||
void deregisterCard(uint8_t card_id);
|
||||
void publishCard(uint8_t card_id);
|
||||
// Publish topic appended with base topic
|
||||
void publishRelative(char *topic, char *payload);
|
||||
// Subscribe topic appended with base topic
|
||||
void subscribeRelative(char *topic);
|
||||
void subscribeToTopic(char *topic);
|
||||
void unsubscribeFromTopic(char *topic);
|
||||
void connectToWifi(char *ssid, char *password);
|
||||
void connectToWifi(char *ssid);
|
||||
void disconnectFromWifi();
|
||||
bool wifiConnected();
|
||||
void ethernetBegin();
|
||||
void loadNetworkConfig();
|
||||
void saveNetworkConfig();
|
||||
NetworkConfig* getNetworkConfig();
|
||||
MqttConfig* getMqttConfig();
|
||||
void setMqttConfig(MqttConfig mqtt_config);
|
||||
void saveMqttConfig();
|
||||
void loadMqttConfig();
|
||||
void connectNetwork();
|
||||
void setNetworkConfig(NetworkConfig network_config);
|
||||
void connectToMqtt();
|
||||
bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password);
|
||||
bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port);
|
||||
bool mqttConnected();
|
||||
void disconnectFromMqtt();
|
||||
void publishToTopic(char *topic, char *payload);
|
||||
void registerMqttCallback(void (*callback)(char *, char *));
|
||||
void registerRelativeMqttCallback(void (*callback)(char *, char *));
|
||||
void registerSubscribeCallback(void (*callback)(void));
|
||||
void setBaseTopic(char *base_topic);
|
||||
void bindEthernetInterface(ETHClass *ethernetIface);
|
||||
bool networkConnected();
|
||||
|
||||
IoTComponent* getComponent(uint8_t card_id);
|
||||
IPAddress getETHIp();
|
||||
|
||||
private:
|
||||
FRAM *fram;
|
||||
bool useWifi;
|
||||
bool WifiUseAuth;
|
||||
char ssid[32];
|
||||
char password[32];
|
||||
WiFiClient tcpClient;
|
||||
void sessionKeepAlive();
|
||||
bool mqttReconnect();
|
||||
void wifiReconnect();
|
||||
void mqttSubscribe();
|
||||
void mqttCallback(char *topic, byte *payload, unsigned int length);
|
||||
void (*user_mqtt_callback)(char *, char *);
|
||||
void (*user_relative_mqtt_callback)(char *, char *);
|
||||
void (*user_subscribe_callback)(void);
|
||||
void publishRelative(uint8_t card_id, char *topic, char *payload);
|
||||
bool active;
|
||||
PubSubClient mqtt;
|
||||
IoTComponent *components[255];
|
||||
char payload_buffer[200];
|
||||
char base_topic[100];
|
||||
uint8_t base_topic_length;
|
||||
ExpansionCard **cards; // Points to card array in ESPMegaPRO Core
|
||||
// MQTT Connection Parameters
|
||||
bool mqtt_connected;
|
||||
NetworkConfig network_config;
|
||||
MqttConfig mqtt_config;
|
||||
ETHClass *ethernetIface;
|
||||
};
|
178
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO.cpp
Normal file
178
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
|
||||
uint8_t inputBufferA;
|
||||
uint8_t inputBufferB;
|
||||
|
||||
PCF8574 inputBankA(INPUT_BANK_A_ADDRESS);
|
||||
PCF8574 inputBankB(INPUT_BANK_B_ADDRESS);
|
||||
Adafruit_PWMServoDriver pwmBank = Adafruit_PWMServoDriver(PWM_BANK_ADDRESS);
|
||||
FRAM ESPMega_FRAM;
|
||||
|
||||
#ifdef ANALOG_CARD_ENABLE
|
||||
Adafruit_ADS1115 analogInputBankA;
|
||||
Adafruit_ADS1115 analogInputBankB;
|
||||
MCP4725 DAC0(DAC0_ADDRESS);
|
||||
MCP4725 DAC1(DAC1_ADDRESS);
|
||||
MCP4725 DAC2(DAC2_ADDRESS);
|
||||
MCP4725 DAC3(DAC3_ADDRESS);
|
||||
#endif
|
||||
|
||||
void ESPMega_begin()
|
||||
{
|
||||
Wire.begin(14, 33);
|
||||
inputBankA.begin();
|
||||
inputBankB.begin();
|
||||
pwmBank.begin();
|
||||
ESPMega_FRAM.begin(FRAM_ADDRESS);
|
||||
// ESPMegaPRO v3 use the PWMBank to drive Half Bridge
|
||||
// Push Pull Output is required.
|
||||
pwmBank.setOutputMode(true);
|
||||
|
||||
#ifdef USE_INTERRUPT
|
||||
pinMode(INPUT_BANK_A_INTERRUPT, INPUT_PULLUP);
|
||||
pinMode(INPUT_BANK_B_INTERRUPT, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(INPUT_BANK_A_INTERRUPT), refreshInputBankA, FALLING);
|
||||
attachInterrupt(digitalPinToInterrupt(INPUT_BANK_B_INTERRUPT), refreshInputBankB, FALLING);
|
||||
#endif
|
||||
|
||||
#ifdef ANALOG_CARD_ENABLE
|
||||
analogInputBankA.begin(ANALOG_INPUT_BANK_A_ADDRESS);
|
||||
analogInputBankB.begin(ANALOG_INPUT_BANK_B_ADDRESS);
|
||||
DAC0.begin();
|
||||
DAC1.begin();
|
||||
DAC2.begin();
|
||||
DAC3.begin();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ESPMega_loop()
|
||||
{
|
||||
}
|
||||
|
||||
bool ESPMega_digitalRead(int id)
|
||||
{
|
||||
if (id >= 0 && id <= 7)
|
||||
{
|
||||
#ifndef USE_INTERRUPT
|
||||
refreshInputBankA(); // Only poll if interrupt is not enabled
|
||||
#endif
|
||||
|
||||
return ((inputBufferA >> (7 - id)) & 1); // Extract bit from buffer
|
||||
}
|
||||
if (id >= 8 && id <= 15)
|
||||
{
|
||||
|
||||
#ifndef USE_INTERRUPT
|
||||
refreshInputBankB(); // Only poll if interrupt is not enabled
|
||||
#endif
|
||||
if (id >= 8 && id <= 11)
|
||||
return ((inputBufferB >> (15 - id)) & 1); // Extract bit from buffer
|
||||
else if (id >= 12 && id <= 15)
|
||||
return ((inputBufferB >> (id - 12)) & 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ESPMega_analogWrite(int id, int value)
|
||||
{
|
||||
if (id >= 0 && id <= 7)
|
||||
id += 8;
|
||||
else if (id >= 8 && id <= 15)
|
||||
id -= 8;
|
||||
pwmBank.setPin(id, value);
|
||||
}
|
||||
|
||||
void ESPMega_digitalWrite(int id, bool value)
|
||||
{
|
||||
if (value)
|
||||
pwmBank.setPin(id, 4095);
|
||||
else
|
||||
pwmBank.setPin(id, 0);
|
||||
}
|
||||
|
||||
void IRAM_ATTR refreshInputBankA()
|
||||
{
|
||||
inputBufferA = inputBankA.read8();
|
||||
}
|
||||
|
||||
void IRAM_ATTR refreshInputBankB()
|
||||
{
|
||||
inputBufferB = inputBankB.read8();
|
||||
}
|
||||
|
||||
rtctime_t ESPMega_getTime()
|
||||
{
|
||||
tmElements_t timeElement;
|
||||
RTC.read(timeElement);
|
||||
rtctime_t time;
|
||||
time.hours = timeElement.Hour;
|
||||
time.minutes = timeElement.Minute;
|
||||
time.seconds = timeElement.Second;
|
||||
time.day = timeElement.Day;
|
||||
time.month = timeElement.Month;
|
||||
time.year = timeElement.Year + 1970;
|
||||
return time;
|
||||
}
|
||||
|
||||
void ESPMega_setTime(int hours, int minutes, int seconds, int day, int month, int year)
|
||||
{
|
||||
tmElements_t timeElement;
|
||||
timeElement.Hour = hours;
|
||||
timeElement.Minute = minutes;
|
||||
timeElement.Second = seconds;
|
||||
timeElement.Day = day;
|
||||
timeElement.Month = month;
|
||||
timeElement.Year = year - 1970;
|
||||
RTC.write(timeElement);
|
||||
}
|
||||
|
||||
#ifdef ANALOG_CARD_ENABLE
|
||||
int16_t ESPMega_analogRead(int id)
|
||||
{
|
||||
if (id >= 0 && id <= 3)
|
||||
return analogInputBankA.readADC_SingleEnded(3 - id);
|
||||
else if (id >= 4 && id <= 7)
|
||||
return analogInputBankB.readADC_SingleEnded(7 - id);
|
||||
return 0;
|
||||
}
|
||||
void ESPMega_dacWrite(int id, int value)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0:
|
||||
DAC0.setValue(value);
|
||||
break;
|
||||
case 1:
|
||||
DAC1.setValue(value);
|
||||
break;
|
||||
case 2:
|
||||
DAC2.setValue(value);
|
||||
break;
|
||||
case 3:
|
||||
DAC3.setValue(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ESPMega_updateTimeFromNTP()
|
||||
{
|
||||
struct tm timeinfo;
|
||||
if (getLocalTime(&timeinfo))
|
||||
{
|
||||
rtctime_t rtctime = ESPMega_getTime();
|
||||
if (rtctime.hours != timeinfo.tm_hour || rtctime.minutes != timeinfo.tm_min ||
|
||||
rtctime.seconds != timeinfo.tm_sec || rtctime.day != timeinfo.tm_mday ||
|
||||
rtctime.month != timeinfo.tm_mon + 1 || rtctime.year != timeinfo.tm_year + 1900)
|
||||
{
|
||||
ESPMega_setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
||||
timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
132
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO.h
Normal file
132
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
#ifndef ESPMEGA
|
||||
#define ESPMEGA
|
||||
#define ANALOG_CARD_ENABLE
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
#include <PCF8574.h>
|
||||
#include <FRAM.h>
|
||||
#include <TimeLib.h>
|
||||
#include <DS1307RTC.h>
|
||||
#include <time.h>
|
||||
#ifdef ANALOG_CARD_ENABLE
|
||||
#include <Adafruit_ADS1X15.h>
|
||||
#include <MCP4725.h>
|
||||
#endif
|
||||
|
||||
#warning "The procedural ESPMega library does not support installing card. If you want to use the card system, please use the OOP version of the library."
|
||||
|
||||
#define INPUT_BANK_A_ADDRESS 0x21
|
||||
#define INPUT_BANK_B_ADDRESS 0x22
|
||||
#define PWM_BANK_ADDRESS 0x5F
|
||||
#define RTC_ADDRESS 0x68
|
||||
#define ANALOG_INPUT_BANK_A_ADDRESS 0x48
|
||||
#define ANALOG_INPUT_BANK_B_ADDRESS 0x49
|
||||
#define DAC0_ADDRESS 0x60
|
||||
#define DAC1_ADDRESS 0x61
|
||||
#define DAC2_ADDRESS 0x62
|
||||
#define DAC3_ADDRESS 0x63
|
||||
#define FRAM_ADDRESS 0x56
|
||||
|
||||
//#define USE_INTERRUPT
|
||||
#define INPUT_BANK_A_INTERRUPT 36
|
||||
#define INPUT_BANK_B_INTERRUPT 39
|
||||
extern FRAM ESPMega_FRAM;
|
||||
#define ESPMega_configNTP configTime
|
||||
struct rtctime_t {
|
||||
uint8_t hours;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiate ESPMega PRO Internal Components
|
||||
*
|
||||
* This function will initiate the PWM bank, Input banks, and the EEPROM.
|
||||
*/
|
||||
void ESPMega_begin();
|
||||
|
||||
/**
|
||||
* Run ESPMega PRO Internal Routines
|
||||
*
|
||||
* This function must be called regularly for the correct
|
||||
* operation of the ESPMega!
|
||||
*/
|
||||
void ESPMega_loop();
|
||||
|
||||
/**
|
||||
* Read one of the ESPMega's Digital Input pins (I0-I15)
|
||||
*
|
||||
* @param id The number of the pin to be read
|
||||
* @return The state of the pin (HIGH/LOW)
|
||||
*/
|
||||
bool ESPMega_digitalRead(int id);
|
||||
|
||||
/**
|
||||
* Write a pulse wave modulated signal to one of the ESPMega's
|
||||
* PWM pins (P0-P15)
|
||||
*
|
||||
* @param id The number of the pin to write to
|
||||
* @param value the "HIGH" duty cycle of the PWM wave (0-4095)
|
||||
*/
|
||||
void ESPMega_analogWrite(int id, int value);
|
||||
|
||||
/**
|
||||
* Write a digital signal to one of the ESPMega's
|
||||
* PWM pins (P0-P15)
|
||||
*
|
||||
* @param id The number of the pin to write to
|
||||
* @param value the new state for the pin (HIGH/LOW)
|
||||
*/
|
||||
void ESPMega_digitalWrite(int id, bool value);
|
||||
void IRAM_ATTR refreshInputBankA();
|
||||
void IRAM_ATTR refreshInputBankB();
|
||||
|
||||
/**
|
||||
* Get time from the onboard RTC as a struct
|
||||
*
|
||||
* @return Time Element Struct
|
||||
*/
|
||||
rtctime_t ESPMega_getTime();
|
||||
/**
|
||||
* Set the onboard RTC's time
|
||||
*
|
||||
* @param hours
|
||||
* @param minutes
|
||||
* @param seconds
|
||||
* @param day Day of the month
|
||||
* @param month Month in numerical form
|
||||
* @param year Years in AD
|
||||
*/
|
||||
void ESPMega_setTime(int hours,int minutes, int seconds, int day, int month, int year);
|
||||
|
||||
/**
|
||||
* Update the onboard RTC's time
|
||||
* by using time from the NTP server
|
||||
* configured with ESPMega_configNTP();
|
||||
* @return true when updated successfully.
|
||||
*/
|
||||
bool ESPMega_updateTimeFromNTP();
|
||||
|
||||
#ifdef ANALOG_CARD_ENABLE
|
||||
/**
|
||||
* Read one of the ESPMega Analog Card's Analog Input pins (A0-A7)
|
||||
*
|
||||
* @param id The number of the pin to be read
|
||||
* @return The value of the pin (0-4095)
|
||||
*/
|
||||
int16_t ESPMega_analogRead(int id);
|
||||
/**
|
||||
* Write a True Analog Signal to one of the ESPMega Analog Card's
|
||||
* Analog Output pins (AO0-AO3)
|
||||
*
|
||||
* @param id The number of the pin to write to
|
||||
* @param value the analog value of the pin (0-4095)
|
||||
*/
|
||||
void ESPMega_dacWrite(int id, int value);
|
||||
#endif
|
||||
|
||||
#endif
|
98
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp
Normal file
98
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <ESPMegaPRO_OOP.hpp>
|
||||
ESPMegaPRO::ESPMegaPRO() {
|
||||
|
||||
}
|
||||
bool ESPMegaPRO::begin() {
|
||||
Wire.begin(14, 33);
|
||||
fram.begin(FRAM_ADDRESS);
|
||||
Serial.begin(115200);
|
||||
this->installCard(1, &outputs);
|
||||
outputs.bindFRAM(&fram,0);
|
||||
outputs.loadFromFRAM();
|
||||
if(!this->installCard(0, &inputs)) {
|
||||
Serial.println("Failed to initialize inputs");
|
||||
Serial.println("Is this an ESPMegaPRO device?");
|
||||
return false;
|
||||
}
|
||||
uint8_t pinMap[16] = {0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8};
|
||||
inputs.loadPinMap(pinMap);
|
||||
return true;
|
||||
}
|
||||
void ESPMegaPRO::loop() {
|
||||
inputs.loop();
|
||||
outputs.loop();
|
||||
for (int i = 0; i < 255; i++) {
|
||||
if (cardInstalled[i]) {
|
||||
cards[i]->loop();
|
||||
}
|
||||
}
|
||||
iot.loop();
|
||||
}
|
||||
bool ESPMegaPRO::installCard(uint8_t slot, ExpansionCard* card) {
|
||||
if (slot > 255) return false;
|
||||
if (cardInstalled[slot]) {
|
||||
Serial.println("Card already installed");
|
||||
return false;
|
||||
}
|
||||
if (!card->begin()) {
|
||||
Serial.print("Failed to install card at slot ");
|
||||
Serial.println(slot);
|
||||
return false;
|
||||
}
|
||||
cards[slot] = card;
|
||||
cardInstalled[slot] = true;
|
||||
cardCount++;
|
||||
return true;
|
||||
|
||||
}
|
||||
bool ESPMegaPRO::updateTimeFromNTP() {
|
||||
struct tm timeinfo;
|
||||
if (getLocalTime(&timeinfo))
|
||||
{
|
||||
rtctime_t rtctime = this->getTime();
|
||||
if (rtctime.hours != timeinfo.tm_hour || rtctime.minutes != timeinfo.tm_min ||
|
||||
rtctime.seconds != timeinfo.tm_sec || rtctime.day != timeinfo.tm_mday ||
|
||||
rtctime.month != timeinfo.tm_mon + 1 || rtctime.year != timeinfo.tm_year + 1900)
|
||||
{
|
||||
this->setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
||||
timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
rtctime_t ESPMegaPRO::getTime() {
|
||||
tmElements_t timeElement;
|
||||
RTC.read(timeElement);
|
||||
rtctime_t time;
|
||||
time.hours = timeElement.Hour;
|
||||
time.minutes = timeElement.Minute;
|
||||
time.seconds = timeElement.Second;
|
||||
time.day = timeElement.Day;
|
||||
time.month = timeElement.Month;
|
||||
time.year = timeElement.Year + 1970;
|
||||
return time;
|
||||
}
|
||||
|
||||
void ESPMegaPRO::setTime(int hours, int minutes, int seconds, int day, int month, int year)
|
||||
{
|
||||
tmElements_t timeElement;
|
||||
timeElement.Hour = hours;
|
||||
timeElement.Minute = minutes;
|
||||
timeElement.Second = seconds;
|
||||
timeElement.Day = day;
|
||||
timeElement.Month = month;
|
||||
timeElement.Year = year - 1970;
|
||||
RTC.write(timeElement);
|
||||
}
|
||||
|
||||
void ESPMegaPRO::enableIotModule() {
|
||||
iot.intr_begin(cards);
|
||||
}
|
||||
|
||||
ExpansionCard* ESPMegaPRO::getCard(uint8_t slot) {
|
||||
if (slot > 255) return nullptr;
|
||||
if (!cardInstalled[slot]) return nullptr;
|
||||
return cards[slot];
|
||||
}
|
43
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp
Normal file
43
ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <ClimateCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <FRAM.h>
|
||||
#include <TimeLib.h>
|
||||
#include <DS1307RTC.h>
|
||||
#include <time.h>
|
||||
#include <TimeStructure.hpp>
|
||||
#include <ESPMegaDisplay.hpp>
|
||||
#include <InternalDisplay.hpp>
|
||||
|
||||
#define FRAM_ADDRESS 0x56
|
||||
#define INPUT_BANK_A_ADDRESS 0x21
|
||||
#define INPUT_BANK_B_ADDRESS 0x22
|
||||
#define PWM_BANK_ADDRESS 0x5F
|
||||
#define RTC_ADDRESS 0x68
|
||||
|
||||
class ESPMegaPRO {
|
||||
public:
|
||||
ESPMegaPRO();
|
||||
bool begin();
|
||||
void loop();
|
||||
bool installCard(uint8_t slot, ExpansionCard* card);
|
||||
bool updateTimeFromNTP();
|
||||
void enableIotModule();
|
||||
rtctime_t getTime();
|
||||
void setTime(int hours, int minutes, int seconds, int day, int month, int year);
|
||||
ExpansionCard* getCard(uint8_t slot);
|
||||
FRAM fram;
|
||||
DigitalInputCard inputs = DigitalInputCard(INPUT_BANK_A_ADDRESS, INPUT_BANK_B_ADDRESS);
|
||||
DigitalOutputCard outputs = DigitalOutputCard(PWM_BANK_ADDRESS);
|
||||
ESPMegaIoT iot = ESPMegaIoT();
|
||||
private:
|
||||
ExpansionCard* cards[255];
|
||||
bool cardInstalled[255];
|
||||
uint8_t cardCount = 0;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <Update.h>
|
||||
|
||||
class ESPMegaWebServer
|
||||
{
|
||||
public:
|
||||
ESPMegaWebServer(uint16_t port);
|
||||
~ESPMegaWebServer();
|
||||
void begin(ESPMegaIoT *iot);
|
||||
void loop();
|
||||
private:
|
||||
// Web Server
|
||||
AsyncWebServer server;
|
||||
uint16_t port;
|
||||
// ESPMegaIoT
|
||||
ESPMegaIoT *iot;
|
||||
// Endpoints Handlers
|
||||
void dashboardHandler(AsyncWebServerRequest *request);
|
||||
void configHandler(AsyncWebServerRequest *request);
|
||||
void saveConfigHandler(AsyncWebServerRequest *request);
|
||||
void otaHandler(AsyncWebServerRequest *request);
|
||||
};
|
13
ESPMegaPRO-firmware/lib/ESPMegaPRO/ExpansionCard.hpp
Normal file
13
ESPMegaPRO-firmware/lib/ESPMegaPRO/ExpansionCard.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
class ExpansionCard {
|
||||
public:
|
||||
// Instantiate the card with the specified address
|
||||
ExpansionCard() {}
|
||||
virtual bool begin();
|
||||
// Preform a loop to refresh the input buffers
|
||||
virtual void loop();
|
||||
// Get the card type
|
||||
virtual uint8_t getType();
|
||||
};
|
180
ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp
Normal file
180
ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include <InternalDisplay.hpp>
|
||||
|
||||
|
||||
void InternalDisplay::begin(ESPMegaIoT *iot, std::function<rtctime_t()> getRtcTime) {
|
||||
this->iot = iot;
|
||||
this->getRtcTime = getRtcTime;
|
||||
this->mqttConfig = this->iot->getMqttConfig();
|
||||
this->networkConfig = this->iot->getNetworkConfig();
|
||||
// Register callbacks
|
||||
this->inputCard->registerCallback(std::bind(&InternalDisplay::handleInputStateChange, this, std::placeholders::_1, std::placeholders::_2));
|
||||
this->outputCard->registerChangeCallback(std::bind(&InternalDisplay::handlePwmStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
|
||||
void InternalDisplay::loop() {
|
||||
// Keep reading the Serial Adapter
|
||||
this->recieveSerialCommand();
|
||||
// Refresh the top bar every 5 seconds
|
||||
static uint32_t lastTopBarRefresh;
|
||||
if (millis() - lastTopBarRefresh > INTERNAL_DISPLAY_TOP_BAR_REFRESH_INTERVAL) {
|
||||
this->updateStatusIcons(this->iot->networkConnected(), this->iot->mqttConnected());
|
||||
lastTopBarRefresh = millis();
|
||||
}
|
||||
// Refresh the clock every 10 seconds
|
||||
static uint32_t lastClockRefresh;
|
||||
if (millis() - lastClockRefresh > INTERNAL_DISPLAY_CLOCK_REFRESH_INTERVAL) {
|
||||
this->updateClock();
|
||||
lastClockRefresh = millis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InternalDisplay::handleInputStateChange(uint8_t pin, bool state) {
|
||||
// If the input card is binded to the display and the current page is the input page
|
||||
// then update the respective input component
|
||||
if (this->inputCard!=nullptr || this->currentPage != INTERNAL_DISPLAY_INPUT_PAGE) return;
|
||||
// Update the input state
|
||||
this->setInputMarker(pin, state);
|
||||
}
|
||||
|
||||
void InternalDisplay::handlePwmStateChange(uint8_t pin, bool state, uint16_t value) {
|
||||
// If the output card is binded to the display and the current page is the output page
|
||||
// then update the respective output component
|
||||
if (this->outputCard!=nullptr || this->currentPage != INTERNAL_DISPLAY_OUTPUT_PAGE) return;
|
||||
// Update the output state
|
||||
this->setOutputBar(pin, value);
|
||||
this->setOutputStateColor(pin, state);
|
||||
}
|
||||
|
||||
void InternalDisplay::handlePageChange(uint8_t page) {
|
||||
// Refresh the page
|
||||
this->refreshPage(page);
|
||||
}
|
||||
|
||||
void InternalDisplay::saveNetworkConfig() {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
void InternalDisplay::saveMQTTConfig() {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
void InternalDisplay::updateStatusIcons(bool networkStatus, bool mqttStatus) {
|
||||
this->setNumber("server.pic", mqttStatus ? PIC_MQTT_CONNECTED : PIC_MQTT_DISCONNECTED);
|
||||
this->setNumber("lan.pic", networkStatus ? PIC_LAN_CONNECTED : PIC_LAN_DISCONNECTED);
|
||||
}
|
||||
|
||||
void InternalDisplay::updateClock() {
|
||||
rtctime_t time = this->getRtcTime();
|
||||
this->displayAdapter->print("time.txt=");
|
||||
this->displayAdapter->print(time.hours%12);
|
||||
this->displayAdapter->print(":");
|
||||
this->displayAdapter->print(time.minutes);
|
||||
this->displayAdapter->print(" ");
|
||||
this->displayAdapter->print(time.hours/12 ? "PM" : "AM");
|
||||
this->sendStopBytes();
|
||||
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshPage() {
|
||||
this->refreshPage(this->currentPage);
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshPage(uint8_t page) {
|
||||
switch (page) {
|
||||
case INTERNAL_DISPLAY_DASHBOARD_PAGE:
|
||||
this->refreshDashboard();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_INPUT_PAGE:
|
||||
this->refreshInput();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_OUTPUT_PAGE:
|
||||
this->refreshOutput();
|
||||
break;
|
||||
case INTERNAL_DISPLAY_AC_PAGE:
|
||||
this->refreshAC();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshDashboard() {
|
||||
// The dashboard have the following components:
|
||||
// 1. Hostname
|
||||
// 2. IP Address
|
||||
// 3. MQTT Server with port
|
||||
// 4. MQTT Connection status
|
||||
this->setString("hostname.txt", this->networkConfig->hostname);
|
||||
// Construct the IP address string
|
||||
static char ip_address[25];
|
||||
sprintf(ip_address, "%d.%d.%d.%d", this->networkConfig->ip[0], this->networkConfig->ip[1], this->networkConfig->ip[2], this->networkConfig->ip[3]);
|
||||
this->setString("ip_address.txt", ip_address);
|
||||
// Send the MQTT server and port
|
||||
this->displayAdapter->print("server_address.txt=");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_server);
|
||||
this->displayAdapter->print(":");
|
||||
this->displayAdapter->print(this->mqttConfig->mqtt_port);
|
||||
this->sendStopBytes();
|
||||
// Send the MQTT connection status
|
||||
this->setString("status_txt.txt", this->iot->mqttConnected() ? MSG_MQTT_CONNECTED : MSG_MQTT_DISCONNECTED);
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshInput() {
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
this->setInputMarker(i, this->inputCard->digitalRead(i));
|
||||
}
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshOutput() {
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
this->setOutputBar(i, this->outputCard->getValue(i));
|
||||
this->setOutputStateColor(i, this->outputCard->getState(i));
|
||||
}
|
||||
}
|
||||
|
||||
void InternalDisplay::refreshAC() {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
|
||||
void InternalDisplay::setPWMAdjustmentSlider(uint16_t value) {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
void InternalDisplay::setPWMAdjustmentPin(uint8_t pin) {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
void InternalDisplay::setPWMAdjustmentButton(bool state) {
|
||||
// TODO: implementation
|
||||
}
|
||||
|
||||
void InternalDisplay::setOutputBar(uint8_t pin, uint16_t value) {
|
||||
// Write the value to the output bar
|
||||
this->displayAdapter->print("j");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".val=");
|
||||
this->displayAdapter->print((int)(value*100/4095));
|
||||
this->sendStopBytes();
|
||||
}
|
||||
|
||||
void InternalDisplay::setOutputStateColor(uint8_t pin, bool state) {
|
||||
this->displayAdapter->print("j");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".ppic=");
|
||||
this->displayAdapter->print(state ? PIC_PWM_BAR_ON : PIC_PWM_BAR_OFF);
|
||||
this->sendStopBytes();
|
||||
|
||||
}
|
||||
|
||||
void InternalDisplay::setInputMarker(uint8_t pin, bool state) {
|
||||
this->displayAdapter->print("I");
|
||||
this->displayAdapter->print(pin);
|
||||
this->displayAdapter->print(".val=");
|
||||
this->displayAdapter->print(state ? 0:1);
|
||||
this->sendStopBytes();
|
||||
}
|
||||
|
||||
InternalDisplay::InternalDisplay(HardwareSerial *displayAdapter) : ESPMegaDisplay(displayAdapter) {
|
||||
this->currentPage = INTERNAL_DISPLAY_DASHBOARD_PAGE;
|
||||
}
|
68
ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.hpp
Normal file
68
ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
#include <ESPMegaDisplay.hpp>
|
||||
#include <TimeStructure.hpp>
|
||||
#include <ESPMegaIoT.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
|
||||
// Page IDs
|
||||
#define INTERNAL_DISPLAY_DASHBOARD_PAGE 0
|
||||
#define INTERNAL_DISPLAY_INPUT_PAGE 1
|
||||
#define INTERNAL_DISPLAY_OUTPUT_PAGE 2
|
||||
#define INTERNAL_DISPLAY_AC_PAGE 3
|
||||
|
||||
// Picture IDs
|
||||
#define PIC_LAN_DISCONNECTED 2
|
||||
#define PIC_LAN_CONNECTED 3
|
||||
#define PIC_MQTT_DISCONNECTED 4
|
||||
#define PIC_MQTT_CONNECTED 5
|
||||
#define PIC_PWM_BAR_ON 33
|
||||
#define PIC_PWM_BAR_OFF 48
|
||||
|
||||
// Messages
|
||||
#define MSG_MQTT_CONNECTED "BMS Managed"
|
||||
#define MSG_MQTT_DISCONNECTED "Standalone"
|
||||
|
||||
// Refresh Interval
|
||||
#define INTERNAL_DISPLAY_CLOCK_REFRESH_INTERVAL 15000
|
||||
#define INTERNAL_DISPLAY_TOP_BAR_REFRESH_INTERVAL 5000
|
||||
|
||||
class InternalDisplay : public ESPMegaDisplay {
|
||||
public:
|
||||
InternalDisplay(HardwareSerial *displayAdapter);
|
||||
void begin(ESPMegaIoT *iot, std::function<rtctime_t()> getRtcTime);
|
||||
void loop();
|
||||
void bindInputCard(DigitalInputCard *inputCard);
|
||||
void bindOutputCard(DigitalOutputCard *outputCard);
|
||||
|
||||
private:
|
||||
DigitalInputCard *inputCard;
|
||||
DigitalOutputCard *outputCard;
|
||||
// Previously registered callbacks of input and output cards
|
||||
// We need to call them when the respective card is binded to the display
|
||||
// Because we will replace the callbacks with the display's own callbacks
|
||||
void handleInputStateChange(uint8_t pin, bool state);
|
||||
void handlePwmStateChange(uint8_t pin, bool state, uint16_t value);
|
||||
void handlePageChange(uint8_t page);
|
||||
void setOutputBar(uint8_t pin, uint16_t value);
|
||||
void setOutputStateColor(uint8_t pin, bool state);
|
||||
void setInputMarker(uint8_t pin, bool state);
|
||||
void setPWMAdjustmentSlider(uint16_t value);
|
||||
void setPWMAdjustmentPin(uint8_t pin);
|
||||
void setPWMAdjustmentButton(bool state);
|
||||
void saveNetworkConfig();
|
||||
void saveMQTTConfig();
|
||||
void updateStatusIcons(bool networkStatus, bool mqttStatus);
|
||||
void updateClock();
|
||||
void refreshPage();
|
||||
void refreshPage(uint8_t page);
|
||||
void refreshDashboard();
|
||||
void refreshInput();
|
||||
void refreshOutput();
|
||||
void refreshAC();
|
||||
MqttConfig *mqttConfig;
|
||||
NetworkConfig *networkConfig;
|
||||
// Pointers to various data
|
||||
ESPMegaIoT *iot;
|
||||
std::function<rtctime_t()> getRtcTime;
|
||||
};
|
21
ESPMegaPRO-firmware/lib/ESPMegaPRO/IoTComponent.cpp
Normal file
21
ESPMegaPRO-firmware/lib/ESPMegaPRO/IoTComponent.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <IoTComponent.hpp>
|
||||
|
||||
void IoTComponent::setMqttClient(PubSubClient *mqtt) {
|
||||
this->mqtt = mqtt;
|
||||
}
|
||||
|
||||
void IoTComponent::publishRelative(const char *topic, const char *payload) {
|
||||
char absolute_topic[100];
|
||||
sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic);
|
||||
mqtt->publish(absolute_topic, payload);
|
||||
}
|
||||
|
||||
void IoTComponent::subscribeRelative(const char *topic) {
|
||||
char absolute_topic[50];
|
||||
sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic);
|
||||
mqtt->subscribe(absolute_topic);
|
||||
}
|
||||
|
||||
void IoTComponent::loop() {
|
||||
// Placeholder, Do nothing
|
||||
}
|
20
ESPMegaPRO-firmware/lib/ESPMegaPRO/IoTComponent.hpp
Normal file
20
ESPMegaPRO-firmware/lib/ESPMegaPRO/IoTComponent.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include <ExpansionCard.hpp>
|
||||
#include <PubSubClient.h>
|
||||
class IoTComponent {
|
||||
public:
|
||||
virtual bool begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic);
|
||||
virtual void handleMqttMessage(char *topic, char *payload);
|
||||
void setMqttClient(PubSubClient *mqtt);
|
||||
virtual void publishReport();
|
||||
virtual uint8_t getType();
|
||||
virtual void subscribe();
|
||||
|
||||
void loop();
|
||||
protected:
|
||||
char *base_topic;
|
||||
void publishRelative(const char *topic, const char *payload);
|
||||
void subscribeRelative(const char *topic);
|
||||
PubSubClient *mqtt;
|
||||
uint8_t card_id;
|
||||
};
|
11
ESPMegaPRO-firmware/lib/ESPMegaPRO/TimeStructure.hpp
Normal file
11
ESPMegaPRO-firmware/lib/ESPMegaPRO/TimeStructure.hpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct rtctime_t {
|
||||
uint8_t hours;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
};
|
46
ESPMegaPRO-firmware/lib/README
Normal file
46
ESPMegaPRO-firmware/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
34
ESPMegaPRO-firmware/platformio.ini
Normal file
34
ESPMegaPRO-firmware/platformio.ini
Normal file
|
@ -0,0 +1,34 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:wt32-eth01]
|
||||
platform = espressif32
|
||||
board = wt32-eth01
|
||||
framework = arduino
|
||||
;siwats/ESPMegaPROR3@1.0.1
|
||||
lib_deps = adafruit/Adafruit PWM Servo Driver Library@^2.4.1
|
||||
adafruit/Adafruit ADS1X15@^2.4.0
|
||||
adafruit/Adafruit BusIO
|
||||
robtillaart/PCF8574@^0.3.7
|
||||
arduino-libraries/Arduino_BuiltIn@^1.0.0
|
||||
SPI@^2.0.0
|
||||
robtillaart/MCP4725@^0.3.7
|
||||
robtillaart/FRAM_I2C@^0.6.1
|
||||
paulstoffregen/Time@^1.6.1
|
||||
paulstoffregen/DS1307RTC@0.0.0-alpha+sha.c2590c0033
|
||||
knolleary/pubsubclient@^2.8.0
|
||||
seithan/Easy Nextion Library@^1.0.6
|
||||
z3t0/IRremote@^4.2.0
|
||||
bblanchon/ArduinoJson@^6.21.4
|
||||
robtillaart/DS18B20@^0.2.1
|
||||
robtillaart/DHTNEW@^0.4.18
|
||||
#esphome/ESPAsyncWebServer-esphome@^3.1.0
|
||||
monitor_speed = 115200
|
||||
|
18
ESPMegaPRO-firmware/src/.vscode/c_cpp_properties.json
vendored
Normal file
18
ESPMegaPRO-firmware/src/.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "windows-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "gcc",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
24
ESPMegaPRO-firmware/src/.vscode/launch.json
vendored
Normal file
24
ESPMegaPRO-firmware/src/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"cwd": "d:/Git/ESPMegaPRO-v3-SDK/Template Project/src",
|
||||
"program": "d:/Git/ESPMegaPRO-v3-SDK/Template Project/src/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
58
ESPMegaPRO-firmware/src/.vscode/settings.json
vendored
Normal file
58
ESPMegaPRO-firmware/src/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false
|
||||
}
|
22
ESPMegaPRO-firmware/src/analogdemo.cpp.disabled
Normal file
22
ESPMegaPRO-firmware/src/analogdemo.cpp.disabled
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
#define ADC
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
ESPMega_begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
#ifdef DAC
|
||||
for(int i=0;i<4096;i++) {
|
||||
ESPMega_dacWrite(0,i);
|
||||
double dac_val_sine = 2047.5*sin(i*2*PI/2047.5)+2047.5;
|
||||
ESPMega_dacWrite(1,(int)dac_val_sine);
|
||||
}
|
||||
#endif
|
||||
#ifdef ADC
|
||||
Serial.printf("A0: %d",ESPMega_analogRead(0));
|
||||
#endif
|
||||
}
|
76
ESPMegaPRO-firmware/src/cardsdemo.cpp.disabled
Normal file
76
ESPMegaPRO-firmware/src/cardsdemo.cpp.disabled
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <ESPMegaPRO_OOP.hpp>
|
||||
#include <DigitalInputCard.hpp>
|
||||
#include <DigitalOutputCard.hpp>
|
||||
#include <AnalogCard.hpp>
|
||||
|
||||
// This code demonstrates how to use the cards
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
AnalogCard analogCard = AnalogCard();
|
||||
|
||||
void inputCallback(uint8_t pin, bool state)
|
||||
{
|
||||
Serial.print("Input ");
|
||||
Serial.print(pin);
|
||||
Serial.print(" changed to ");
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void printInputs()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
Serial.print("Input ");
|
||||
Serial.print(i);
|
||||
Serial.print(": ");
|
||||
Serial.print(espmega.inputs.digitalRead(i));
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Instantiate ESPMega
|
||||
espmega.begin();
|
||||
Serial.println("ESPMega initialized");
|
||||
// Read all the inputs and print them
|
||||
printInputs();
|
||||
// Turn on all the outputs
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
espmega.outputs.digitalWrite(i, true);
|
||||
}
|
||||
// Set the debounce time for all inputs to 200ms
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
espmega.inputs.setDebounceTime(i, 200);
|
||||
}
|
||||
// Register the callback function
|
||||
espmega.inputs.registerCallback(inputCallback);
|
||||
|
||||
// Install the analog card
|
||||
Serial.println("Installing analog card");
|
||||
if (espmega.installCard(0, &analogCard))
|
||||
{
|
||||
Serial.println("Analog card installed");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Failed to install analog card");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long previousMillis = 0;
|
||||
const unsigned long interval = 1000; // 1 second
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
if (currentMillis - previousMillis >= interval)
|
||||
{
|
||||
previousMillis = currentMillis;
|
||||
printInputs();
|
||||
}
|
||||
espmega.loop();
|
||||
}
|
37
ESPMegaPRO-firmware/src/fram_demo.cpp.disabled
Normal file
37
ESPMegaPRO-firmware/src/fram_demo.cpp.disabled
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
void setup()
|
||||
{
|
||||
ESPMega_begin();
|
||||
Serial.begin(115200);
|
||||
uint8_t a = 35;
|
||||
uint8_t b = 42;
|
||||
uint8_t c = 66;
|
||||
uint8_t d = 251;
|
||||
|
||||
// ESPMega_FRAM.write8(0,a);
|
||||
// ESPMega_FRAM.write8(1,b);
|
||||
// ESPMega_FRAM.write8(2,c);
|
||||
// ESPMega_FRAM.write8(3,d);
|
||||
|
||||
uint8_t e = ESPMega_FRAM.read8(0);
|
||||
uint8_t f = ESPMega_FRAM.read8(1);
|
||||
uint8_t g = ESPMega_FRAM.read8(2);
|
||||
uint8_t h = ESPMega_FRAM.read8(3);
|
||||
Serial.println(e);
|
||||
Serial.println(f);
|
||||
Serial.println(g);
|
||||
Serial.println(h);
|
||||
char ll[2000] = "Everyone has a dream they strive to achieve, and so does the musically talented Kanon Shibuya. However, due to her stage fright, Kanon fails to make it into Yuigaoka Girls' High School's music program and instead ends up in the general curriculum. Even though Kanon makes the conscious decision to quit music altogether, her classmate Tang Keke rekindles Kanon's passion for music with her own: a passion for school idols.";
|
||||
|
||||
// ESPMega_FRAM.write(4,(uint8_t*)ll,2000);
|
||||
|
||||
char llr[2000];
|
||||
ESPMega_FRAM.read(4,(uint8_t*)llr,2000);
|
||||
Serial.println(llr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
84
ESPMegaPRO-firmware/src/i2c_scanner.cpp.disabled
Normal file
84
ESPMegaPRO-firmware/src/i2c_scanner.cpp.disabled
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
// --------------------------------------
|
||||
// i2c_scanner
|
||||
//
|
||||
// Version 1
|
||||
// This program (or code that looks like it)
|
||||
// can be found in many places.
|
||||
// For example on the Arduino.cc forum.
|
||||
// The original author is not know.
|
||||
// Version 2, Juni 2012, Using Arduino 1.0.1
|
||||
// Adapted to be as simple as possible by Arduino.cc user Krodal
|
||||
// Version 3, Feb 26 2013
|
||||
// V3 by louarnold
|
||||
// Version 4, March 3, 2013, Using Arduino 1.0.3
|
||||
// by Arduino.cc user Krodal.
|
||||
// Changes by louarnold removed.
|
||||
// Scanning addresses changed from 0...127 to 1...119,
|
||||
// according to the i2c scanner by Nick Gammon
|
||||
// https://www.gammon.com.au/forum/?id=10896
|
||||
// Version 5, March 28, 2013
|
||||
// As version 4, but address scans now to 127.
|
||||
// A sensor seems to use address 120.
|
||||
// Version 6, November 27, 2015.
|
||||
// Added waiting for the Leonardo serial communication.
|
||||
//
|
||||
//
|
||||
// This sketch tests the standard 7-bit addresses
|
||||
// Devices with higher bit address might not be seen properly.
|
||||
//
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.setClock(50000);
|
||||
Wire.begin(14,33);
|
||||
|
||||
Serial.begin(115200); // Leonardo: wait for serial monitor
|
||||
Serial.println("\nI2C Scanner");
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
byte error, address;
|
||||
int nDevices;
|
||||
|
||||
Serial.println("Scanning...");
|
||||
|
||||
nDevices = 0;
|
||||
for(address = 1; address < 127; address++ )
|
||||
{
|
||||
// The i2c_scanner uses the return value of
|
||||
// the Write.endTransmisstion to see if
|
||||
// a device did acknowledge to the address.
|
||||
Wire.beginTransmission(address);
|
||||
error = Wire.endTransmission();
|
||||
|
||||
if (error == 0)
|
||||
{
|
||||
Serial.print("I2C device found at address 0x");
|
||||
if (address<16)
|
||||
Serial.print("0");
|
||||
Serial.print(address,HEX);
|
||||
Serial.println(" !");
|
||||
|
||||
nDevices++;
|
||||
}
|
||||
else if (error==4)
|
||||
{
|
||||
Serial.print("Unknown error at address 0x");
|
||||
if (address<16)
|
||||
Serial.print("0");
|
||||
Serial.println(address,HEX);
|
||||
}
|
||||
}
|
||||
if (nDevices == 0)
|
||||
Serial.println("No I2C devices found\n");
|
||||
else
|
||||
Serial.println("done\n");
|
||||
|
||||
delay(5000); // wait 5 seconds for next scan
|
||||
}
|
505
ESPMegaPRO-firmware/src/i2cmssc.cpp.disabled
Normal file
505
ESPMegaPRO-firmware/src/i2cmssc.cpp.disabled
Normal file
|
@ -0,0 +1,505 @@
|
|||
//
|
||||
// FILE: MultiSpeedI2CScanner.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.17
|
||||
// PURPOSE: I2C scanner at different speeds
|
||||
// DATE: 2013-11-05
|
||||
// URL: https://github.com/RobTillaart/MultiSpeedI2CScanner
|
||||
// URL: http://forum.arduino.cc/index.php?topic=197360
|
||||
//
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// FOR INTERNAL I2C BUS NANO 33 BLE
|
||||
// #define WIRE_IMPLEMENT_WIRE1 1
|
||||
// extern TwoWire Wire1;
|
||||
|
||||
void I2Cscan();
|
||||
void displayHelp();
|
||||
void reset();
|
||||
void setAddress();
|
||||
void setSpeed(char sp);
|
||||
char getCommand();
|
||||
|
||||
|
||||
TwoWire *wire;
|
||||
|
||||
const char version[] = "0.1.16";
|
||||
|
||||
|
||||
// INTERFACE COUNT (TESTED TEENSY 3.5 AND ARDUINO DUE ONLY)
|
||||
int wirePortCount = 1;
|
||||
int selectedWirePort = 0;
|
||||
|
||||
|
||||
// scans devices from 50 to 800 KHz I2C speeds.
|
||||
// speed lower than 50 and above 400 can cause problems
|
||||
long speed[10] = { 100, 200, 300, 400 };
|
||||
int speeds;
|
||||
|
||||
int addressStart = 8;
|
||||
int addressEnd = 119;
|
||||
|
||||
|
||||
// DELAY BETWEEN TESTS
|
||||
// for delay between tests of found devices.
|
||||
#ifndef RESTORE_LATENCY
|
||||
#define RESTORE_LATENCY 5
|
||||
#endif
|
||||
|
||||
bool delayFlag = false;
|
||||
|
||||
|
||||
// MINIMIZE OUTPUT
|
||||
bool printAll = true;
|
||||
bool header = true;
|
||||
bool disableIRQ = false;
|
||||
|
||||
|
||||
// STATE MACHINE
|
||||
enum states {
|
||||
STOP, ONCE, CONT, HELP
|
||||
};
|
||||
states state = STOP;
|
||||
|
||||
|
||||
// TIMING
|
||||
uint32_t startScan;
|
||||
uint32_t stopScan;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MAIN CODE
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
|
||||
Wire.begin(14,33);
|
||||
|
||||
#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
|
||||
Wire1.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
|
||||
Wire2.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
|
||||
Wire3.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE4 || WIRE_INTERFACES_COUNT > 4
|
||||
Wire4.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE5 || WIRE_INTERFACES_COUNT > 5
|
||||
Wire5.begin();
|
||||
wirePortCount++;
|
||||
#endif
|
||||
|
||||
wire = &Wire;
|
||||
|
||||
Serial.println();
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
yield();
|
||||
char command = getCommand();
|
||||
switch (command)
|
||||
{
|
||||
case '@':
|
||||
selectedWirePort = (selectedWirePort + 1) % wirePortCount;
|
||||
Serial.print(F("<I2C PORT=Wire"));
|
||||
Serial.print(selectedWirePort);
|
||||
Serial.println(F(">"));
|
||||
switch (selectedWirePort)
|
||||
{
|
||||
case 0:
|
||||
wire = &Wire;
|
||||
break;
|
||||
#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
|
||||
case 1:
|
||||
wire = &Wire1;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
|
||||
case 2:
|
||||
wire = &Wire2;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
|
||||
case 3:
|
||||
wire = &Wire3;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE4 || WIRE_INTERFACES_COUNT > 4
|
||||
case 4:
|
||||
wire = &Wire4;
|
||||
break;
|
||||
#endif
|
||||
#if defined WIRE_IMPLEMENT_WIRE5 || WIRE_INTERFACES_COUNT > 5
|
||||
case 5:
|
||||
wire = &Wire5;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
state = ONCE;
|
||||
break;
|
||||
case 'c':
|
||||
state = CONT;
|
||||
break;
|
||||
case 'd':
|
||||
delayFlag = !delayFlag;
|
||||
Serial.print(F("<delay="));
|
||||
Serial.println(delayFlag ? F("5>") : F("0>"));
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
// eeprom test TODO
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
header = !header;
|
||||
Serial.print(F("<header="));
|
||||
Serial.println(header ? F("yes>") : F("no>"));
|
||||
break;
|
||||
case 'p':
|
||||
printAll = !printAll;
|
||||
Serial.print(F("<print="));
|
||||
Serial.println(printAll ? F("all>") : F("found>"));
|
||||
break;
|
||||
case 'i':
|
||||
disableIRQ = !disableIRQ;
|
||||
Serial.print(F("<irq="));
|
||||
Serial.println(disableIRQ ? F("diabled>") : F("enabled>"));
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '4':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
setSpeed(command);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
reset();
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
setAddress();
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
case '?':
|
||||
state = HELP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ONCE:
|
||||
I2Cscan();
|
||||
state = HELP;
|
||||
break;
|
||||
case CONT:
|
||||
I2Cscan();
|
||||
delay(1000);
|
||||
break;
|
||||
case HELP:
|
||||
displayHelp();
|
||||
state = STOP;
|
||||
break;
|
||||
case STOP:
|
||||
break;
|
||||
default: // ignore all non commands
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void reset()
|
||||
{
|
||||
setSpeed('9');
|
||||
selectedWirePort = 0;
|
||||
addressStart = 8;
|
||||
addressEnd = 119;
|
||||
|
||||
delayFlag = false;
|
||||
printAll = true;
|
||||
header = true;
|
||||
disableIRQ = false;
|
||||
|
||||
state = STOP;
|
||||
|
||||
displayHelp();
|
||||
}
|
||||
|
||||
|
||||
void setAddress()
|
||||
{
|
||||
if (addressStart == 0)
|
||||
{
|
||||
addressStart = 8;
|
||||
addressEnd = 119;
|
||||
}
|
||||
else
|
||||
{
|
||||
addressStart = 0;
|
||||
addressEnd = 127;
|
||||
}
|
||||
Serial.print(F("<address Range = "));
|
||||
Serial.print(addressStart);
|
||||
Serial.print(F(".."));
|
||||
Serial.print(addressEnd);
|
||||
Serial.println(F(">"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void setSpeed(char sp)
|
||||
{
|
||||
switch (sp)
|
||||
{
|
||||
case '1':
|
||||
speed[0] = 100;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '2':
|
||||
speed[0] = 200;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '4':
|
||||
speed[0] = 400;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '8':
|
||||
speed[0] = 800;
|
||||
speeds = 1;
|
||||
break;
|
||||
case '9': // limited to 400 KHz
|
||||
speeds = 8;
|
||||
for (int i = 1; i <= speeds; i++) speed[i - 1] = i * 50;
|
||||
break;
|
||||
case '0': // limited to 800 KHz
|
||||
speeds = 8;
|
||||
for (int i = 1; i <= speeds; i++) speed[i - 1] = i * 100;
|
||||
break;
|
||||
|
||||
// new in 0.1.10 - experimental
|
||||
case 'M':
|
||||
speed[0] = 1000;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'N':
|
||||
speed[0] = 3400;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'O':
|
||||
speed[0] = 5000;
|
||||
speeds = 1;
|
||||
break;
|
||||
case 'P':
|
||||
speed[0] = 100;
|
||||
speed[1] = 400;
|
||||
speed[2] = 1000;
|
||||
speed[3] = 3400;
|
||||
speed[4] = 5000;
|
||||
speeds = 5;
|
||||
break;
|
||||
}
|
||||
Serial.print("<speeds =");
|
||||
for (int i = 0; i < speeds; i++)
|
||||
{
|
||||
Serial.print(' ');
|
||||
Serial.print(speed[i]);
|
||||
}
|
||||
Serial.println(" >");
|
||||
}
|
||||
|
||||
|
||||
char getCommand()
|
||||
{
|
||||
char c = '\0';
|
||||
if (Serial.available())
|
||||
{
|
||||
c = Serial.read();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void displayHelp()
|
||||
{
|
||||
Serial.print(F("\nArduino MultiSpeed I2C Scanner - "));
|
||||
Serial.println(version);
|
||||
Serial.println();
|
||||
Serial.print(F("I2C ports: "));
|
||||
Serial.print(wirePortCount);
|
||||
Serial.print(F(" Current: Wire"));
|
||||
Serial.println(selectedWirePort);
|
||||
Serial.println(F("\t@ = toggle Wire - Wire1 .. Wire5 [e.g. TEENSY or Arduino Due]"));
|
||||
|
||||
Serial.println(F("Scan mode:"));
|
||||
Serial.println(F("\ts = single scan"));
|
||||
Serial.println(F("\tc = continuous scan - 1 second delay"));
|
||||
Serial.println(F("\tq = quit continuous scan"));
|
||||
Serial.println(F("\td = toggle latency delay between successful tests. 0 - 5 ms"));
|
||||
Serial.println(F("\ti = toggle enable/disable interrupts"));
|
||||
|
||||
Serial.println(F("Output:"));
|
||||
Serial.println(F("\tp = toggle printAll - printFound."));
|
||||
Serial.println(F("\th = toggle header - noHeader."));
|
||||
Serial.println(F("\ta = toggle address range, 0..127 - 8..119 (default)"));
|
||||
|
||||
Serial.println(F("Speeds:"));
|
||||
Serial.println(F("\t0 = 100..800 KHz - step 100 (warning - can block!!)"));
|
||||
Serial.println(F("\t1 = 100 KHz"));
|
||||
Serial.println(F("\t2 = 200 KHz"));
|
||||
Serial.println(F("\t4 = 400 KHz"));
|
||||
Serial.println(F("\t9 = 50..400 KHz - step 50 < DEFAULT >"));
|
||||
Serial.println();
|
||||
Serial.println(F("\t!! HIGH SPEEDS - WARNING - can block - not applicable for UNO"));
|
||||
Serial.println(F("\t8 = 800 KHz"));
|
||||
Serial.println(F("\tM = 1000 KHz"));
|
||||
Serial.println(F("\tN = 3400 KHz"));
|
||||
Serial.println(F("\tO = 5000 KHz"));
|
||||
Serial.println(F("\tP = 100 400 1000 3400 5000 KHz (standards)"));
|
||||
Serial.println(F("\n\tr = reset to startup defaults."));
|
||||
Serial.println(F("\t? = help - this page"));
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
|
||||
void I2Cscan()
|
||||
{
|
||||
startScan = millis();
|
||||
uint8_t count = 0;
|
||||
|
||||
if (disableIRQ)
|
||||
{
|
||||
noInterrupts();
|
||||
}
|
||||
|
||||
if (header)
|
||||
{
|
||||
Serial.print(F("TIME\tDEC\tHEX\t"));
|
||||
for (uint8_t s = 0; s < speeds; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(speed[s]);
|
||||
}
|
||||
Serial.println(F("\t[KHz]"));
|
||||
for (uint8_t s = 0; s < speeds + 5; s++)
|
||||
{
|
||||
Serial.print(F("--------"));
|
||||
}
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
for (uint8_t address = addressStart; address <= addressEnd; address++)
|
||||
{
|
||||
bool printLine = printAll;
|
||||
bool found[speeds];
|
||||
bool fnd = false;
|
||||
|
||||
for (uint8_t s = 0; s < speeds ; s++)
|
||||
{
|
||||
yield(); // keep ESP happy
|
||||
|
||||
#if ARDUINO < 158 && defined (TWBR)
|
||||
uint16_t PREV_TWBR = TWBR;
|
||||
TWBR = (F_CPU / (speed[s] * 1000) - 16) / 2;
|
||||
if (TWBR < 2)
|
||||
{
|
||||
Serial.println("ERROR: not supported speed");
|
||||
TWBR = PREV_TWBR;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
wire->setClock(speed[s] * 1000UL);
|
||||
#endif
|
||||
wire->beginTransmission (address);
|
||||
found[s] = (wire->endTransmission () == 0);
|
||||
fnd |= found[s];
|
||||
// give device 5 millis
|
||||
if (fnd && delayFlag) delay(RESTORE_LATENCY);
|
||||
}
|
||||
|
||||
if (fnd) count++;
|
||||
printLine |= fnd;
|
||||
|
||||
if (printLine)
|
||||
{
|
||||
Serial.print(millis());
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(address, DEC);
|
||||
Serial.print(F("\t0x"));
|
||||
if (address < 0x10) Serial.print(0, HEX);
|
||||
Serial.print(address, HEX);
|
||||
Serial.print(F("\t"));
|
||||
|
||||
for (uint8_t s = 0; s < speeds ; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(found[s] ? F("V") : F("."));
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// FOOTER
|
||||
if (header)
|
||||
{
|
||||
for (uint8_t s = 0; s < speeds + 5; s++)
|
||||
{
|
||||
Serial.print(F("--------"));
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
Serial.print(F("TIME\tDEC\tHEX\t"));
|
||||
for (uint8_t s = 0; s < speeds; s++)
|
||||
{
|
||||
Serial.print(F("\t"));
|
||||
Serial.print(speed[s]);
|
||||
}
|
||||
Serial.println(F("\t[KHz]"));
|
||||
}
|
||||
*/
|
||||
|
||||
stopScan = millis();
|
||||
if (header)
|
||||
{
|
||||
Serial.println();
|
||||
Serial.print(count);
|
||||
Serial.print(F(" devices found in "));
|
||||
Serial.print(stopScan - startScan);
|
||||
Serial.println(F(" milliseconds."));
|
||||
}
|
||||
|
||||
interrupts();
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
22
ESPMegaPRO-firmware/src/input_demo.cpp.disabled
Normal file
22
ESPMegaPRO-firmware/src/input_demo.cpp.disabled
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <Arduino.h>
|
||||
#include <PCF8574.h>
|
||||
|
||||
PCF8574 inputbank1(0x21);
|
||||
|
||||
void setup() {
|
||||
Wire.begin(14,33);
|
||||
Serial.begin(9600);
|
||||
boolean connected = inputbank1.begin();
|
||||
Serial.println("connection state = "+String(connected));
|
||||
inputbank1.write(0, HIGH);
|
||||
delay(50);
|
||||
inputbank1.write(1, HIGH);
|
||||
delay(50);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for(int i=2;i<8;i++) {
|
||||
Serial.println(inputbank1.read(i));
|
||||
delay(100);
|
||||
}
|
||||
}
|
17
ESPMegaPRO-firmware/src/input_test.cpp.disabled
Normal file
17
ESPMegaPRO-firmware/src/input_test.cpp.disabled
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "ESPMegaPRO.h"
|
||||
|
||||
void setup() {
|
||||
ESPMega_begin();
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if(ESPMega_digitalRead(11)) {
|
||||
ESPMega_digitalWrite(8, HIGH);
|
||||
Serial.println("11HIGH");
|
||||
} else {
|
||||
ESPMega_digitalWrite(8, LOW);
|
||||
Serial.println("11LOW");
|
||||
}
|
||||
ESPMega_loop();
|
||||
}
|
57
ESPMegaPRO-firmware/src/iot_framdemo.cpp
Normal file
57
ESPMegaPRO-firmware/src/iot_framdemo.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <ESPMegaPRO_OOP.hpp>
|
||||
#include <InternalDisplay.hpp>
|
||||
#include <ETH.h>
|
||||
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void input_change_callback(uint8_t pin, uint8_t value) {
|
||||
Serial.print("Input change callback: ");
|
||||
Serial.print(pin);
|
||||
Serial.print(" ");
|
||||
Serial.println(value);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
espmega.begin();
|
||||
espmega.enableIotModule();
|
||||
ETH.begin();
|
||||
espmega.iot.bindEthernetInterface(Ð);
|
||||
NetworkConfig config = {
|
||||
.ip = {192, 168, 0, 11},
|
||||
.gateway = {192, 168, 0, 1},
|
||||
.subnet = {255, 255, 255, 0},
|
||||
.dns1 = {192, 168, 0, 1},
|
||||
.dns2 = {192, 168, 0, 1},
|
||||
.useStaticIp = true,
|
||||
.useWifi = false,
|
||||
.wifiUseAuth = false,
|
||||
};
|
||||
strcpy(config.ssid, "ssid");
|
||||
strcpy(config.password, "password");
|
||||
strcpy(config.hostname, "espmega");
|
||||
Serial.println("Setting network config");
|
||||
espmega.iot.setNetworkConfig(config);
|
||||
Serial.println("Connecting to network");
|
||||
espmega.iot.connectNetwork();
|
||||
Serial.println("Begin MQTT Modules");
|
||||
MqttConfig mqtt_config = {
|
||||
.mqtt_port = 1883,
|
||||
.mqtt_useauth = false
|
||||
};
|
||||
Serial.println("Setting MQTT Server");
|
||||
strcpy(mqtt_config.mqtt_server, "192.168.0.26");
|
||||
strcpy(mqtt_config.base_topic, "/espmegaoop");
|
||||
Serial.println("Loading MQTT Config Struct to IoT Module");
|
||||
espmega.iot.setMqttConfig(mqtt_config);
|
||||
Serial.println("Connecting to MQTT");
|
||||
espmega.iot.connectToMqtt();
|
||||
Serial.println("Registering cards");
|
||||
espmega.iot.registerCard(0);
|
||||
espmega.iot.registerCard(1);
|
||||
Serial.println("Initialization Routine Complete");
|
||||
espmega.inputs.registerCallback(input_change_callback);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
espmega.loop();
|
||||
}
|
38
ESPMegaPRO-firmware/src/iotdemo.cpp.disabled
Normal file
38
ESPMegaPRO-firmware/src/iotdemo.cpp.disabled
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <ESPMegaPRO_OOP.hpp>
|
||||
#include <ETH.h>
|
||||
// Instantiate ESPMega
|
||||
ESPMegaPRO espmega = ESPMegaPRO();
|
||||
|
||||
void mqtt_callback(char *topic, char *payload)
|
||||
{
|
||||
Serial.print("MQTT Callback: ");
|
||||
Serial.print(topic);
|
||||
Serial.print(" ");
|
||||
Serial.println(payload);
|
||||
}
|
||||
|
||||
IPAddress ip(192, 168, 0, 11);
|
||||
IPAddress gateway(192, 168, 0, 1);
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Initialize ESPMega
|
||||
espmega.begin();
|
||||
espmega.enableIotModule();
|
||||
ETH.begin();
|
||||
ETH.config(ip, gateway, subnet);
|
||||
espmega.iot.setBaseTopic("/testmegaoop");
|
||||
espmega.iot.connectToMqtt("espmega", "192.168.0.26", 1883);
|
||||
espmega.iot.publishRelative("test", "test");
|
||||
espmega.iot.subscribeRelative("test");
|
||||
espmega.iot.registerMqttCallback(mqtt_callback);
|
||||
espmega.iot.registerCard(1);
|
||||
espmega.iot.publishCard(1);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Call ESPMega loop
|
||||
espmega.loop();
|
||||
}
|
27
ESPMegaPRO-firmware/src/ntupload.cpp.disabled
Normal file
27
ESPMegaPRO-firmware/src/ntupload.cpp.disabled
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
|
||||
void lcd_send_stop_bit();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
lcd_send_stop_bit();
|
||||
Serial.print("rest");
|
||||
lcd_send_stop_bit();
|
||||
Serial.print("connect");
|
||||
lcd_send_stop_bit();
|
||||
delay(1000);
|
||||
Serial.print("whmi-wri 1024,115200,res0");
|
||||
lcd_send_stop_bit();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
while(Serial.available()) {
|
||||
Serial.write(Serial.read());
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_send_stop_bit() {
|
||||
Serial.write(0xFF);
|
||||
Serial.write(0xFF);
|
||||
Serial.write(0xFF);
|
||||
}
|
28
ESPMegaPRO-firmware/src/pwm_demo.cpp.disabled
Normal file
28
ESPMegaPRO-firmware/src/pwm_demo.cpp.disabled
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <Arduino.h>
|
||||
#include "Wire.h"
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
|
||||
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x5F);
|
||||
|
||||
void setup() {
|
||||
Wire.begin(14,33);
|
||||
pwm.begin();
|
||||
pwm.setPin(2, 500);
|
||||
pwm.setPin(3, 1000);
|
||||
pwm.setPin(4, 1500);
|
||||
pwm.setPin(5, 2000);
|
||||
pwm.setPin(6, 2500);
|
||||
pwm.setPin(7, 3000);
|
||||
pwm.setPin(8, 3500);
|
||||
pwm.setPin(9, 4000);
|
||||
pwm.setPin(10, 500);
|
||||
pwm.setPin(11, 1000);
|
||||
pwm.setPin(12, 1500);
|
||||
pwm.setPin(13, 2000);
|
||||
pwm.setPin(14, 2500);
|
||||
pwm.setPin(15, 3000);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
30
ESPMegaPRO-firmware/src/rtc_demo.cpp.disabled
Normal file
30
ESPMegaPRO-firmware/src/rtc_demo.cpp.disabled
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <ESPMegaPRO.h>
|
||||
#include <ETH.h>
|
||||
|
||||
uint8_t utc_offset = 7;
|
||||
|
||||
IPAddress IP(192, 168, 0, 241);
|
||||
IPAddress SUBNET(255, 255, 255, 0);
|
||||
IPAddress GATEWAY(192, 168, 0, 1);
|
||||
IPAddress DNS(10, 192, 1, 1);
|
||||
|
||||
void setup()
|
||||
{
|
||||
ESPMega_begin();
|
||||
Serial.begin(115200);
|
||||
ETH.begin();
|
||||
ETH.config(IP, GATEWAY, SUBNET, DNS, DNS);
|
||||
delay(5000);
|
||||
char ntp[19];
|
||||
DNS.toString().toCharArray(ntp, 19);
|
||||
ESPMega_configNTP(utc_offset * 3600, 0, ntp);
|
||||
ESPMega_updateTimeFromNTP();
|
||||
//ESPMega_setTime(0,10,52,9,11,2008);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
rtctime_t time = ESPMega_getTime();
|
||||
Serial.printf("RTC: %02d:%02d:%02d %02d/%02d/%04d\n", time.hours, time.minutes, time.seconds, time.day, time.month, time.year);
|
||||
delay(1000);
|
||||
}
|
11
ESPMegaPRO-firmware/test/README
Normal file
11
ESPMegaPRO-firmware/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
Loading…
Add table
Add a link
Reference in a new issue