mirror of https://github.com/lumapu/ahoy.git
lumapu
3 years ago
19 changed files with 1641 additions and 0 deletions
@ -0,0 +1,158 @@ |
|||||
|
/*
|
||||
|
CircularBuffer - An Arduino circular buffering library for arbitrary types. |
||||
|
|
||||
|
Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl |
||||
|
|
||||
|
This library is free software; you can redistribute it and/or |
||||
|
modify it under the terms of the GNU Lesser General Public |
||||
|
License as published by the Free Software Foundation; either |
||||
|
version 2.1 of the License, or (at your option) any later version. |
||||
|
|
||||
|
This library is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
|
Lesser General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU Lesser General Public |
||||
|
License along with this library; if not, write to the Free Software |
||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
*/ |
||||
|
|
||||
|
#ifndef CircularBuffer_h |
||||
|
#define CircularBuffer_h |
||||
|
|
||||
|
#ifdef ESP8266 |
||||
|
#define DISABLE_IRQ noInterrupts() |
||||
|
#define RESTORE_IRQ interrupts() |
||||
|
#else |
||||
|
#define DISABLE_IRQ \ |
||||
|
uint8_t sreg = SREG; \ |
||||
|
cli(); |
||||
|
|
||||
|
#define RESTORE_IRQ \ |
||||
|
SREG = sreg; |
||||
|
#endif |
||||
|
|
||||
|
template <class T> class CircularBuffer |
||||
|
{ |
||||
|
public: |
||||
|
/** Constructor
|
||||
|
* @param buffer Preallocated buffer of at least size records. |
||||
|
* @param size Number of records available in the buffer. |
||||
|
*/ |
||||
|
CircularBuffer(T* buffer, const uint8_t size ) |
||||
|
: m_size(size), m_buff(buffer) |
||||
|
{ |
||||
|
clear(); |
||||
|
} |
||||
|
|
||||
|
/** Clear all entries in the circular buffer. */ |
||||
|
void clear(void) |
||||
|
{ |
||||
|
m_front = 0; |
||||
|
m_fill = 0; |
||||
|
} |
||||
|
|
||||
|
/** Test if the circular buffer is empty */ |
||||
|
inline bool empty(void) const |
||||
|
{ |
||||
|
return !m_fill; |
||||
|
} |
||||
|
|
||||
|
/** Return the number of records stored in the buffer */ |
||||
|
inline uint8_t available(void) const |
||||
|
{ |
||||
|
return m_fill; |
||||
|
} |
||||
|
|
||||
|
/** Test if the circular buffer is full */ |
||||
|
inline bool full(void) const |
||||
|
{ |
||||
|
return m_fill == m_size; |
||||
|
} |
||||
|
|
||||
|
/** Aquire record on front of the buffer, for writing.
|
||||
|
* After filling the record, it has to be pushed to actually |
||||
|
* add it to the buffer. |
||||
|
* @return Pointer to record, or NULL when buffer is full. |
||||
|
*/ |
||||
|
T* getFront(void) const |
||||
|
{ |
||||
|
DISABLE_IRQ; |
||||
|
T* f = NULL; |
||||
|
if (!full()) |
||||
|
f = get(m_front); |
||||
|
RESTORE_IRQ; |
||||
|
return f; |
||||
|
} |
||||
|
|
||||
|
/** Push record to front of the buffer
|
||||
|
* @param record Record to push. If record was aquired previously (using getFront) its |
||||
|
* data will not be copied as it is already present in the buffer. |
||||
|
* @return True, when record was pushed successfully. |
||||
|
*/ |
||||
|
bool pushFront(T* record) |
||||
|
{ |
||||
|
bool ok = false; |
||||
|
DISABLE_IRQ; |
||||
|
if (!full()) |
||||
|
{ |
||||
|
T* f = get(m_front); |
||||
|
if (f != record) |
||||
|
*f = *record; |
||||
|
m_front = (m_front+1) % m_size; |
||||
|
m_fill++; |
||||
|
ok = true; |
||||
|
} |
||||
|
RESTORE_IRQ; |
||||
|
return ok; |
||||
|
} |
||||
|
|
||||
|
/** Aquire record on back of the buffer, for reading.
|
||||
|
* After reading the record, it has to be pop'ed to actually |
||||
|
* remove it from the buffer. |
||||
|
* @return Pointer to record, or NULL when buffer is empty. |
||||
|
*/ |
||||
|
T* getBack(void) const |
||||
|
{ |
||||
|
T* b = NULL; |
||||
|
DISABLE_IRQ; |
||||
|
if (!empty()) |
||||
|
b = get(back()); |
||||
|
RESTORE_IRQ; |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
/** Remove record from back of the buffer.
|
||||
|
* @return True, when record was pop'ed successfully. |
||||
|
*/ |
||||
|
bool popBack(void) |
||||
|
{ |
||||
|
bool ok = false; |
||||
|
DISABLE_IRQ; |
||||
|
if (!empty()) |
||||
|
{ |
||||
|
m_fill--; |
||||
|
ok = true; |
||||
|
} |
||||
|
RESTORE_IRQ; |
||||
|
return ok; |
||||
|
} |
||||
|
|
||||
|
protected: |
||||
|
inline T * get(const uint8_t idx) const |
||||
|
{ |
||||
|
return &(m_buff[idx]); |
||||
|
} |
||||
|
inline uint8_t back(void) const |
||||
|
{ |
||||
|
return (m_front - m_fill + m_size) % m_size; |
||||
|
} |
||||
|
|
||||
|
const uint8_t m_size; // Total number of records that can be stored in the buffer.
|
||||
|
T* const m_buff; // Ptr to buffer holding all records.
|
||||
|
volatile uint8_t m_front; // Index of front element (not pushed yet).
|
||||
|
volatile uint8_t m_fill; // Amount of records currently pushed.
|
||||
|
}; |
||||
|
|
||||
|
#endif // CircularBuffer_h
|
@ -0,0 +1,45 @@ |
|||||
|
## OVERVIEW |
||||
|
|
||||
|
This code was tested on a ESP8266 - ESP-07 module. Many parts of the code are based on 'Hubi's code, which can be found here: <https://www.mikrocontroller.net/topic/525778?page=3#7033371> |
||||
|
|
||||
|
The NRF24L01+ radio module is connected to the standard SPI pins. Additional there are 3 pins, which can be set individual: |
||||
|
|
||||
|
- IRQ - Pin 4 |
||||
|
- CE - Pin 5 |
||||
|
- CS - Pin 15 |
||||
|
|
||||
|
|
||||
|
## Compile |
||||
|
|
||||
|
This code can be compiled using Arduino. The settings were: |
||||
|
|
||||
|
- Board: Generic ESP8266 Module |
||||
|
- Flash-Size: 1MB (FS: none, OTA: 502kB) |
||||
|
|
||||
|
|
||||
|
## Flash ESP with firmware |
||||
|
|
||||
|
1. flash the ESP with the compiled firmware using the UART pins or any preinstalled firmware with OTA capabilities |
||||
|
2. repower the ESP |
||||
|
3. the ESP will start as access point (AP) if there is no network config stored in its eeprom |
||||
|
4. connect to the AP, you will be forwarded to the setup page |
||||
|
5. configure your WiFi settings, save, repower |
||||
|
6. check your router for the IP address of the module |
||||
|
|
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
Connect the ESP to power and to your serial console. The webinterface is currently only used for OTA and config. |
||||
|
The serial console will print all information which is send and received. |
||||
|
|
||||
|
|
||||
|
## Known Issues |
||||
|
|
||||
|
- only command 0x81 is received |
||||
|
|
||||
|
|
||||
|
## USED LIBRARIES |
||||
|
|
||||
|
- `Time` |
||||
|
- `RF24` |
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
#include "app.h" |
||||
|
|
||||
|
app myApp; |
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void setup() { |
||||
|
pinMode(RF24_IRQ_PIN, INPUT_PULLUP); |
||||
|
attachInterrupt(digitalPinToInterrupt(RF24_IRQ_PIN), handleIntr, FALLING); |
||||
|
|
||||
|
// AP name, password, timeout
|
||||
|
myApp.setup("ESP AHOY", "esp_8266", 15); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void loop() { |
||||
|
myApp.loop(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
ICACHE_RAM_ATTR void handleIntr(void) { |
||||
|
myApp.handleIntr(); |
||||
|
} |
||||
|
|
@ -0,0 +1,265 @@ |
|||||
|
#include "app.h" |
||||
|
|
||||
|
#include "html/h/index_html.h" |
||||
|
extern String setup_html; |
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
app::app() : Main() { |
||||
|
uint8_t wrAddr[6]; |
||||
|
mRadio = new RF24(RF24_CE_PIN, RF24_CS_PIN); |
||||
|
|
||||
|
mRadio->begin(); |
||||
|
mRadio->setAutoAck(false); |
||||
|
mRadio->setRetries(0, 0); |
||||
|
|
||||
|
mHoymiles = new hoymiles(); |
||||
|
mEep->read(ADDR_HOY_ADDR, mHoymiles->mAddrBytes, HOY_ADDR_LEN); |
||||
|
mHoymiles->serial2RadioId(); |
||||
|
|
||||
|
mBufCtrl = new CircularBuffer(mBuffer, PACKET_BUFFER_SIZE); |
||||
|
|
||||
|
mSendCnt = 0; |
||||
|
mSendTicker = new Ticker(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
app::~app(void) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::setup(const char *ssid, const char *pwd, uint32_t timeout) { |
||||
|
Main::setup(ssid, pwd, timeout); |
||||
|
|
||||
|
mWeb->on("/", std::bind(&app::showIndex, this)); |
||||
|
mWeb->on("/setup", std::bind(&app::showSetup, this)); |
||||
|
mWeb->on("/save ", std::bind(&app::showSave, this)); |
||||
|
|
||||
|
initRadio(); |
||||
|
|
||||
|
mSendTicker->attach_ms(1000, std::bind(&app::sendTicker, this)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::loop(void) { |
||||
|
Main::loop(); |
||||
|
|
||||
|
if(!mBufCtrl->empty()) { |
||||
|
uint8_t len, rptCnt; |
||||
|
NRF24_packet_t *p = mBufCtrl->getBack(); |
||||
|
|
||||
|
//mHoymiles->dumpBuf("RAW", p->packet, PACKET_BUFFER_SIZE);
|
||||
|
|
||||
|
if(mHoymiles->checkCrc(p->packet, &len, &rptCnt)) { |
||||
|
// process buffer only on first occurrence
|
||||
|
if((0 != len) && (0 == rptCnt)) { |
||||
|
mHoymiles->dumpBuf("Payload", p->packet, len); |
||||
|
// @TODO: do analysis here
|
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
if(p->packetsLost != 0) { |
||||
|
Serial.println("Lost packets: " + String(p->packetsLost)); |
||||
|
} |
||||
|
} |
||||
|
mBufCtrl->popBack(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::handleIntr(void) { |
||||
|
uint8_t lostCnt = 0, pipe, len; |
||||
|
NRF24_packet_t *p; |
||||
|
|
||||
|
DISABLE_IRQ; |
||||
|
|
||||
|
while(mRadio->available(&pipe)) { |
||||
|
if(!mBufCtrl->full()) { |
||||
|
p = mBufCtrl->getFront(); |
||||
|
memset(p->packet, 0xcc, MAX_RF_PAYLOAD_SIZE); |
||||
|
p->timestamp = micros(); // Micros does not increase in interrupt, but it can be used.
|
||||
|
p->packetsLost = lostCnt; |
||||
|
len = mRadio->getPayloadSize(); |
||||
|
if(len > MAX_RF_PAYLOAD_SIZE) |
||||
|
len = MAX_RF_PAYLOAD_SIZE; |
||||
|
|
||||
|
mRadio->read(p->packet, len); |
||||
|
mBufCtrl->pushFront(p); |
||||
|
lostCnt = 0; |
||||
|
} |
||||
|
else { |
||||
|
bool tx_ok, tx_fail, rx_ready; |
||||
|
if(lostCnt < 255) |
||||
|
lostCnt++; |
||||
|
mRadio->whatHappened(tx_ok, tx_fail, rx_ready); // reset interrupt status
|
||||
|
mRadio->flush_rx(); // drop the packet
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
RESTORE_IRQ; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::initRadio(void) { |
||||
|
mRadio->setChannel(DEFAULT_RECV_CHANNEL); |
||||
|
mRadio->setDataRate(RF24_250KBPS); |
||||
|
mRadio->disableCRC(); |
||||
|
mRadio->setAutoAck(false); |
||||
|
mRadio->setPayloadSize(MAX_RF_PAYLOAD_SIZE); |
||||
|
mRadio->setAddressWidth(5); |
||||
|
mRadio->openReadingPipe(1, DTU_RADIO_ID); |
||||
|
|
||||
|
// enable only receiving interrupts
|
||||
|
mRadio->maskIRQ(true, true, false); |
||||
|
|
||||
|
// Use lo PA level, as a higher level will disturb CH340 serial usb adapter
|
||||
|
mRadio->setPALevel(RF24_PA_MAX); |
||||
|
mRadio->startListening(); |
||||
|
|
||||
|
Serial.println("Radio Config:"); |
||||
|
mRadio->printPrettyDetails(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::sendPacket(uint8_t buf[], uint8_t len) { |
||||
|
DISABLE_IRQ; |
||||
|
|
||||
|
mRadio->stopListening(); |
||||
|
|
||||
|
#ifdef CHANNEL_HOP |
||||
|
mRadio->setChannel(mHoymiles->getNxtChannel()); |
||||
|
#else |
||||
|
mRadio->setChannel(mHoymiles->getDefaultChannel()); |
||||
|
#endif |
||||
|
|
||||
|
mRadio->openWritingPipe(mHoymiles->mRadioId); |
||||
|
mRadio->setCRCLength(RF24_CRC_16); |
||||
|
mRadio->enableDynamicPayloads(); |
||||
|
mRadio->setAutoAck(true); |
||||
|
mRadio->setRetries(3, 15); |
||||
|
|
||||
|
mRadio->write(buf, len); |
||||
|
|
||||
|
// Try to avoid zero payload acks (has no effect)
|
||||
|
mRadio->openWritingPipe(DUMMY_RADIO_ID); |
||||
|
|
||||
|
mRadio->setAutoAck(false); |
||||
|
mRadio->setRetries(0, 0); |
||||
|
mRadio->disableDynamicPayloads(); |
||||
|
mRadio->setCRCLength(RF24_CRC_DISABLED); |
||||
|
|
||||
|
mRadio->setChannel(DEFAULT_RECV_CHANNEL); |
||||
|
mRadio->startListening(); |
||||
|
|
||||
|
RESTORE_IRQ; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::sendTicker(void) { |
||||
|
uint8_t size = 0; |
||||
|
if((mSendCnt % 6) == 0) |
||||
|
size = mHoymiles->getTimePacket(mSendBuf, mTimestamp); |
||||
|
else if((mSendCnt % 6) == 5) |
||||
|
size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x81); |
||||
|
else if((mSendCnt % 6) == 4) |
||||
|
size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x80); |
||||
|
else if((mSendCnt % 6) == 3) |
||||
|
size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x83); |
||||
|
else if((mSendCnt % 6) == 2) |
||||
|
size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x82); |
||||
|
else if((mSendCnt % 6) == 1) |
||||
|
size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x84); |
||||
|
|
||||
|
Serial.println("sent packet: #" + String(mSendCnt)); |
||||
|
dumpBuf(mSendBuf, size); |
||||
|
sendPacket(mSendBuf, size); |
||||
|
|
||||
|
mSendCnt++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::showIndex(void) { |
||||
|
String html = index_html; |
||||
|
html.replace("{DEVICE}", mDeviceName); |
||||
|
html.replace("{VERSION}", mVersion); |
||||
|
mWeb->send(200, "text/html", html); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::showSetup(void) { |
||||
|
// overrides same method in main.cpp
|
||||
|
|
||||
|
String html = setup_html; |
||||
|
html.replace("{SSID}", mStationSsid); |
||||
|
// PWD will be left at the default value (for protection)
|
||||
|
// -> the PWD will only be changed if it does not match the placeholder "{PWD}"
|
||||
|
|
||||
|
char addr[20] = {0}; |
||||
|
sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X", mHoymiles->mAddrBytes[0], mHoymiles->mAddrBytes[1], mHoymiles->mAddrBytes[2], mHoymiles->mAddrBytes[3], mHoymiles->mAddrBytes[4], mHoymiles->mAddrBytes[5]); |
||||
|
html.replace("{HOY_ADDR}", String(addr)); |
||||
|
|
||||
|
html.replace("{DEVICE}", String(mDeviceName)); |
||||
|
html.replace("{VERSION}", String(mVersion)); |
||||
|
|
||||
|
mWeb->send(200, "text/html", html); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::showSave(void) { |
||||
|
saveValues(true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::saveValues(bool webSend = true) { |
||||
|
Main::saveValues(false); // general configuration
|
||||
|
|
||||
|
if(mWeb->args() > 0) { |
||||
|
char *p; |
||||
|
char addr[20] = {0}; |
||||
|
uint8_t i = 0; |
||||
|
|
||||
|
memset(mHoymiles->mAddrBytes, 0, 6); |
||||
|
mWeb->arg("hoy_addr").toCharArray(addr, 20); |
||||
|
|
||||
|
p = strtok(addr, ":"); |
||||
|
while(NULL != p) { |
||||
|
mHoymiles->mAddrBytes[i++] = strtol(p, NULL, 16); |
||||
|
p = strtok(NULL, ":"); |
||||
|
} |
||||
|
|
||||
|
mEep->write(ADDR_HOY_ADDR, mHoymiles->mAddrBytes, HOY_ADDR_LEN); |
||||
|
|
||||
|
if((mWeb->arg("reboot") == "on")) |
||||
|
showReboot(); |
||||
|
else { |
||||
|
mWeb->send(200, "text/html", "<!doctype html><html><head><title>Setup saved</title><meta http-equiv=\"refresh\" content=\"0; URL=/setup\"></head><body>" |
||||
|
"<p>saved</p></body></html>"); |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
mWeb->send(200, "text/html", "<!doctype html><html><head><title>Error</title><meta http-equiv=\"refresh\" content=\"3; URL=/setup\"></head><body>" |
||||
|
"<p>Error while saving</p></body></html>"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void app::dumpBuf(uint8_t buf[], uint8_t len) { |
||||
|
for(uint8_t i = 0; i < len; i ++) { |
||||
|
if((i % 8 == 0) && (i != 0)) |
||||
|
Serial.println(); |
||||
|
Serial.print(String(buf[i], HEX) + " "); |
||||
|
} |
||||
|
Serial.println(); |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
#ifndef __APP_H__ |
||||
|
#define __APP_H__ |
||||
|
|
||||
|
#include <RF24.h> |
||||
|
#include <RF24_config.h> |
||||
|
|
||||
|
#include "defines.h" |
||||
|
#include "main.h" |
||||
|
|
||||
|
#include "CircularBuffer.h" |
||||
|
#include "hoymiles.h" |
||||
|
|
||||
|
|
||||
|
class app : public Main { |
||||
|
public: |
||||
|
app(); |
||||
|
~app(); |
||||
|
|
||||
|
void setup(const char *ssid, const char *pwd, uint32_t timeout); |
||||
|
void loop(void); |
||||
|
void handleIntr(void); |
||||
|
|
||||
|
private: |
||||
|
void initRadio(void); |
||||
|
void sendPacket(uint8_t data[], uint8_t length); |
||||
|
|
||||
|
void sendTicker(void); |
||||
|
|
||||
|
void showIndex(void); |
||||
|
void showSetup(void); |
||||
|
void showSave(void); |
||||
|
|
||||
|
void saveValues(bool webSend); |
||||
|
void dumpBuf(uint8_t buf[], uint8_t len); |
||||
|
|
||||
|
uint8_t mState; |
||||
|
bool mKeyPressed; |
||||
|
|
||||
|
RF24 *mRadio; |
||||
|
hoymiles *mHoymiles; |
||||
|
CircularBuffer<NRF24_packet_t> *mBufCtrl; |
||||
|
NRF24_packet_t mBuffer[PACKET_BUFFER_SIZE]; |
||||
|
|
||||
|
|
||||
|
Ticker *mSendTicker; |
||||
|
uint32_t mSendCnt; |
||||
|
uint8_t mSendBuf[MAX_RF_PAYLOAD_SIZE]; |
||||
|
}; |
||||
|
|
||||
|
#endif /*__APP_H__*/ |
@ -0,0 +1,37 @@ |
|||||
|
#ifndef __DEFINES_H__ |
||||
|
#define __DEFINES_H__ |
||||
|
|
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// PINOUT
|
||||
|
//-------------------------------------
|
||||
|
#define RF24_IRQ_PIN 4 |
||||
|
#define RF24_CE_PIN 5 |
||||
|
#define RF24_CS_PIN 15 |
||||
|
|
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// VERSION
|
||||
|
//-------------------------------------
|
||||
|
#define VERSION_MAJOR 0 |
||||
|
#define VERSION_MINOR 1 |
||||
|
#define VERSION_PATCH 7 |
||||
|
|
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// EEPROM
|
||||
|
//-------------------------------------
|
||||
|
#define SSID_LEN 32 |
||||
|
#define PWD_LEN 64 |
||||
|
#define DEVNAME_LEN 32 |
||||
|
|
||||
|
#define HOY_ADDR_LEN 6 |
||||
|
|
||||
|
|
||||
|
#define ADDR_SSID 0 // start address
|
||||
|
#define ADDR_PWD ADDR_SSID + SSID_LEN |
||||
|
#define ADDR_DEVNAME ADDR_PWD + PWD_LEN |
||||
|
|
||||
|
#define ADDR_HOY_ADDR ADDR_DEVNAME + DEVNAME_LEN |
||||
|
|
||||
|
#endif /*__DEFINES_H__*/ |
@ -0,0 +1,130 @@ |
|||||
|
#include "eep.h" |
||||
|
#include <EEPROM.h> |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
eep::eep() { |
||||
|
EEPROM.begin(500); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
eep::~eep() { |
||||
|
EEPROM.end(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, char *str, uint8_t length) { |
||||
|
for(uint8_t i = 0; i < length; i ++) { |
||||
|
*(str++) = (char)EEPROM.read(addr++); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, float *value) { |
||||
|
uint8_t *p = (uint8_t*)value; |
||||
|
for(uint8_t i = 0; i < 4; i ++) { |
||||
|
*(p++) = (uint8_t)EEPROM.read(addr++); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, bool *value) { |
||||
|
uint8_t intVal = 0x00; |
||||
|
intVal = EEPROM.read(addr++); |
||||
|
*value = (intVal == 0x01); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, uint8_t *value) { |
||||
|
*value = (EEPROM.read(addr++)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, uint8_t data[], uint8_t length) { |
||||
|
for(uint8_t i = 0; i < length; i ++) { |
||||
|
*(data++) = EEPROM.read(addr++); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, uint16_t *value) { |
||||
|
*value = (EEPROM.read(addr++) << 8); |
||||
|
*value |= (EEPROM.read(addr++)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::read(uint32_t addr, uint32_t *value) { |
||||
|
*value = (EEPROM.read(addr++) << 24); |
||||
|
*value |= (EEPROM.read(addr++) << 16); |
||||
|
*value |= (EEPROM.read(addr++) << 8); |
||||
|
*value |= (EEPROM.read(addr++)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, const char *str, uint8_t length) { |
||||
|
for(uint8_t i = 0; i < length; i ++) { |
||||
|
EEPROM.write(addr++, str[i]); |
||||
|
} |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, uint8_t data[], uint8_t length) { |
||||
|
for(uint8_t i = 0; i < length; i ++) { |
||||
|
EEPROM.write(addr++, data[i]); |
||||
|
} |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, float value) { |
||||
|
uint8_t *p = (uint8_t*)&value; |
||||
|
for(uint8_t i = 0; i < 4; i ++) { |
||||
|
EEPROM.write(addr++, p[i]); |
||||
|
} |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, bool value) { |
||||
|
uint8_t intVal = (value) ? 0x01 : 0x00; |
||||
|
EEPROM.write(addr++, intVal); |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, uint8_t value) { |
||||
|
EEPROM.write(addr++, value); |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, uint16_t value) { |
||||
|
EEPROM.write(addr++, (value >> 8) & 0xff); |
||||
|
EEPROM.write(addr++, (value ) & 0xff); |
||||
|
EEPROM.commit(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void eep::write(uint32_t addr, uint32_t value) { |
||||
|
EEPROM.write(addr++, (value >> 24) & 0xff); |
||||
|
EEPROM.write(addr++, (value >> 16) & 0xff); |
||||
|
EEPROM.write(addr++, (value >> 8) & 0xff); |
||||
|
EEPROM.write(addr++, (value ) & 0xff); |
||||
|
EEPROM.commit(); |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
#ifndef __EEP_H__ |
||||
|
#define __EEP_H__ |
||||
|
|
||||
|
#include "Arduino.h" |
||||
|
|
||||
|
class eep { |
||||
|
public: |
||||
|
eep(); |
||||
|
~eep(); |
||||
|
|
||||
|
void read(uint32_t addr, char *str, uint8_t length); |
||||
|
void read(uint32_t addr, float *value); |
||||
|
void read(uint32_t addr, bool *value); |
||||
|
void read(uint32_t addr, uint8_t *value); |
||||
|
void read(uint32_t addr, uint8_t data[], uint8_t length); |
||||
|
void read(uint32_t addr, uint16_t *value); |
||||
|
void read(uint32_t addr, uint32_t *value); |
||||
|
void write(uint32_t addr, const char *str, uint8_t length); |
||||
|
void write(uint32_t addr, uint8_t data[], uint8_t length); |
||||
|
void write(uint32_t addr, float value); |
||||
|
void write(uint32_t addr, bool value); |
||||
|
void write(uint32_t addr, uint8_t value); |
||||
|
void write(uint32_t addr, uint16_t value); |
||||
|
void write(uint32_t addr, uint32_t value); |
||||
|
|
||||
|
private: |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
#endif /*__EEP_H__*/ |
@ -0,0 +1,228 @@ |
|||||
|
#ifndef __HOYMILES_H__ |
||||
|
#define __HOYMILES_H__ |
||||
|
|
||||
|
#include <RF24.h> |
||||
|
#include <RF24_config.h> |
||||
|
|
||||
|
#define CHANNEL_HOP // switch between channels or use static channel to send
|
||||
|
|
||||
|
#define luint64_t long long unsigned int |
||||
|
|
||||
|
#define DEFAULT_RECV_CHANNEL 3 |
||||
|
#define MAX_RF_PAYLOAD_SIZE 32 |
||||
|
#define DTU_RADIO_ID ((uint64_t)0x1234567801ULL) |
||||
|
#define DUMMY_RADIO_ID ((uint64_t)0xDEADBEEF01ULL) |
||||
|
|
||||
|
#define PACKET_BUFFER_SIZE 30 |
||||
|
#define CRC8_INIT 0x00 |
||||
|
#define CRC8_POLY 0x01 |
||||
|
|
||||
|
#define CRC16_MODBUS_POLYNOM 0xA001 |
||||
|
#define CRC16_NRF24_POLYNOM 0x1021 |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
// MACROS
|
||||
|
#define CP_U32_LittleEndian(buf, v) ({ \ |
||||
|
uint8_t *b = buf; \ |
||||
|
b[0] = ((v >> 24) & 0xff); \ |
||||
|
b[1] = ((v >> 16) & 0xff); \ |
||||
|
b[2] = ((v >> 8) & 0xff); \ |
||||
|
b[3] = ((v ) & 0xff); \ |
||||
|
}) |
||||
|
|
||||
|
#define CP_U32_BigEndian(buf, v) ({ \ |
||||
|
uint8_t *b = buf; \ |
||||
|
b[3] = ((v >> 24) & 0xff); \ |
||||
|
b[2] = ((v >> 16) & 0xff); \ |
||||
|
b[1] = ((v >> 8) & 0xff); \ |
||||
|
b[0] = ((v ) & 0xff); \ |
||||
|
}) |
||||
|
|
||||
|
#define BIT_CNT(x) ((x)<<3) |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
union uint64Bytes { |
||||
|
uint64_t ull; |
||||
|
uint8_t bytes[8]; |
||||
|
}; |
||||
|
|
||||
|
typedef struct { |
||||
|
uint32_t timestamp; |
||||
|
uint8_t packetsLost; |
||||
|
uint8_t packet[MAX_RF_PAYLOAD_SIZE]; |
||||
|
} NRF24_packet_t; |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
class hoymiles { |
||||
|
public: |
||||
|
hoymiles() { |
||||
|
serial2RadioId(); |
||||
|
calcDtuIdCrc(); |
||||
|
|
||||
|
mChannels = new uint8_t(4); |
||||
|
mChannels[0] = 23; |
||||
|
mChannels[1] = 40; |
||||
|
mChannels[2] = 61; |
||||
|
mChannels[3] = 75; |
||||
|
mChanIdx = 1; |
||||
|
|
||||
|
mLastCrc = 0x0000; |
||||
|
mRptCnt = 0; |
||||
|
} |
||||
|
|
||||
|
~hoymiles() {} |
||||
|
|
||||
|
uint8_t getDefaultChannel(void) { |
||||
|
return mChannels[1]; |
||||
|
} |
||||
|
|
||||
|
uint8_t getNxtChannel(void) { |
||||
|
if(++mChanIdx >= 4) |
||||
|
mChanIdx = 0; |
||||
|
return mChannels[mChanIdx]; |
||||
|
} |
||||
|
|
||||
|
void serial2RadioId(void) { |
||||
|
uint64Bytes id; |
||||
|
|
||||
|
id.ull = 0ULL; |
||||
|
id.bytes[4] = mAddrBytes[5]; |
||||
|
id.bytes[3] = mAddrBytes[4]; |
||||
|
id.bytes[2] = mAddrBytes[3]; |
||||
|
id.bytes[1] = mAddrBytes[2]; |
||||
|
id.bytes[0] = 0x01; |
||||
|
|
||||
|
mRadioId = id.ull; |
||||
|
} |
||||
|
|
||||
|
uint8_t getTimePacket(uint8_t buf[], uint32_t ts) { |
||||
|
memset(buf, 0, MAX_RF_PAYLOAD_SIZE); |
||||
|
|
||||
|
buf[0] = 0x15; |
||||
|
CP_U32_BigEndian(&buf[1], (mRadioId >> 8)); |
||||
|
CP_U32_BigEndian(&buf[5], (DTU_RADIO_ID >> 8)); |
||||
|
buf[9] = 0x00; |
||||
|
buf[10] = 0x0b; // cid
|
||||
|
buf[11] = 0x00; |
||||
|
CP_U32_LittleEndian(&buf[12], ts); |
||||
|
buf[19] = 0x05; |
||||
|
|
||||
|
uint16_t crc = crc16(&buf[10], 14); |
||||
|
buf[24] = (crc >> 8) & 0xff; |
||||
|
buf[25] = (crc ) & 0xff; |
||||
|
buf[26] = crc8(buf, 26); |
||||
|
|
||||
|
return 27; |
||||
|
} |
||||
|
|
||||
|
uint8_t getCmdPacket(uint8_t buf[], uint8_t mid, uint8_t cmd) { |
||||
|
buf[0] = mid; |
||||
|
CP_U32_BigEndian(&buf[1], (mRadioId >> 8)); |
||||
|
CP_U32_BigEndian(&buf[5], (DTU_RADIO_ID >> 8)); |
||||
|
buf[9] = cmd; |
||||
|
buf[10] = crc8(buf, 10); |
||||
|
|
||||
|
return 11; |
||||
|
} |
||||
|
|
||||
|
bool checkCrc(uint8_t buf[], uint8_t *len, uint8_t *rptCnt) { |
||||
|
*len = (buf[0] >> 2); |
||||
|
for (int16_t i = MAX_RF_PAYLOAD_SIZE - 1; i >= 0; i--) { |
||||
|
buf[i] = ((buf[i] >> 7) | ((i > 0) ? (buf[i-1] << 1) : 0x00)); |
||||
|
} |
||||
|
uint16_t crc = crc16nrf24(buf, BIT_CNT(*len + 2), 7, mDtuIdCrc); |
||||
|
|
||||
|
bool valid = (crc == ((buf[*len+2] << 8) | (buf[*len+3]))); |
||||
|
|
||||
|
if(valid) { |
||||
|
if(mLastCrc == crc) |
||||
|
*rptCnt = (++mRptCnt); |
||||
|
else { |
||||
|
mRptCnt = 0; |
||||
|
*rptCnt = 0; |
||||
|
mLastCrc = crc; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return valid; |
||||
|
} |
||||
|
|
||||
|
void dumpBuf(const char *info, uint8_t buf[], uint8_t len) { |
||||
|
Serial.print(String(info)); |
||||
|
for(uint8_t i = 0; i < len; i++) { |
||||
|
Serial.print(buf[i], HEX); |
||||
|
Serial.print(" "); |
||||
|
} |
||||
|
Serial.println(); |
||||
|
} |
||||
|
|
||||
|
uint8_t mAddrBytes[6]; |
||||
|
luint64_t mRadioId; |
||||
|
|
||||
|
private: |
||||
|
void calcDtuIdCrc(void) { |
||||
|
uint64_t addr = DTU_RADIO_ID; |
||||
|
uint8_t dtuAddr[5]; |
||||
|
for(int8_t i = 4; i >= 0; i--) { |
||||
|
dtuAddr[i] = addr; |
||||
|
addr >>= 8; |
||||
|
} |
||||
|
mDtuIdCrc = crc16nrf24(dtuAddr, BIT_CNT(5)); |
||||
|
} |
||||
|
|
||||
|
uint8_t crc8(uint8_t buf[], uint8_t len) { |
||||
|
uint8_t crc = CRC8_INIT; |
||||
|
for(uint8_t i = 0; i < len; i++) { |
||||
|
crc ^= buf[i]; |
||||
|
for(uint8_t b = 0; b < 8; b ++) { |
||||
|
crc = (crc << 1) ^ ((crc & 0x80) ? CRC8_POLY : 0x00); |
||||
|
} |
||||
|
} |
||||
|
return crc; |
||||
|
} |
||||
|
|
||||
|
uint16_t crc16(uint8_t buf[], uint8_t len) { |
||||
|
uint16_t crc = 0xffff; |
||||
|
uint8_t lsb; |
||||
|
|
||||
|
for(uint8_t i = 0; i < len; i++) { |
||||
|
crc = crc ^ buf[i]; |
||||
|
for(int8_t b = 7; b >= 0; b--) { |
||||
|
lsb = (crc & 0x0001); |
||||
|
if(lsb == 0x01) |
||||
|
crc--; |
||||
|
crc = crc >> 1; |
||||
|
if(lsb == 0x01) |
||||
|
crc = crc ^ CRC16_MODBUS_POLYNOM; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return crc; |
||||
|
} |
||||
|
|
||||
|
uint16_t crc16nrf24(uint8_t buf[], uint16_t lenBits, uint16_t startBit = 0, uint16_t crcIn = 0xffff) { |
||||
|
uint16_t crc = crcIn; |
||||
|
uint8_t idx, val = buf[(startBit >> 3)]; |
||||
|
|
||||
|
for(uint16_t bit = startBit; bit < lenBits; bit ++) { |
||||
|
idx = bit & 0x07; |
||||
|
if(0 == idx) |
||||
|
val = buf[(bit >> 3)]; |
||||
|
crc ^= 0x8000 & (val << (8 + idx)); |
||||
|
crc = (crc & 0x8000) ? ((crc << 1) ^ CRC16_NRF24_POLYNOM) : (crc << 1); |
||||
|
} |
||||
|
|
||||
|
return crc; |
||||
|
} |
||||
|
|
||||
|
uint8_t *mChannels; |
||||
|
uint8_t mChanIdx; |
||||
|
uint16_t mDtuIdCrc; |
||||
|
uint16_t mLastCrc; |
||||
|
uint8_t mRptCnt; |
||||
|
}; |
||||
|
|
||||
|
#endif /*__HOYMILES_H__*/ |
@ -0,0 +1,4 @@ |
|||||
|
..\tools\fileConv.exe index.html h\index_html.h index_html |
||||
|
..\tools\fileConv.exe setup.html h\setup_html.h setup_html |
||||
|
..\tools\fileConv.exe style.css h\style_css.h style_css |
||||
|
pause |
@ -0,0 +1 @@ |
|||||
|
String index_html = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\"> window.setInterval(\"getAjax('/uptime', 'uptime')\", 1000); window.setInterval(\"getAjax('/time', 'time')\", 1000); function getAjax(url, resid) { var http = null; http = new XMLHttpRequest(); if(http != null) { http.open(\"GET\", url, true); http.onreadystatechange = print; http.send(null); } function print() { if(http.readyState == 4) { document.getElementById(resid).innerHTML = http.responseText; } } } </script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/update\">Update</a><br/><br/><a href=\"/setup\">Setup</a><br/><a href=\"/reboot\">Reboot</a></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Time: </span><span id=\"time\"></span></p></div><div id=\"footer\"><p class=\"left\">© 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>"; |
@ -0,0 +1 @@ |
|||||
|
String setup_html = "<!doctype html><html><head><title>Setup - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><body><h1>Setup</h1><div id=\"setup\" class=\"content\"><div id=\"content\"><p> Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information. </p><form method=\"post\" action=\"/save\"><p class=\"des\">WiFi</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"ssid\" value=\"{SSID}\" required/><span class=\"floating_label\">SSID</span></div><div class=\"inputWrp\"><input type=\"password\" class=\"inputText\" name=\"pwd\" value=\"{PWD}\" required/><span class=\"floating_label\">PASSWORD</span></div><p class=\"des\">Device Host Name</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"device\" value=\"{DEVICE}\" required/><span class=\"floating_label\">DEVICE NAME</span></div><p class=\"des\">General</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"hoy_addr\" value=\"{HOY_ADDR}\" required/><span class=\"floating_label\">HOYMILES ADDRESS (eg. 11:22:33:44:55:66)</span></div><input type=\"checkbox\" class=\"cb\" name=\"reboot\"/><label for=\"reboot\">Reboot device after successful save</label><input type=\"submit\" value=\"save\" class=\"button\" /></form></div></div><div id=\"footer\"><p class=\"left\"><a href=\"/\">Home</a></p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY - {VERSION}</p></div></body></html>"; |
@ -0,0 +1 @@ |
|||||
|
String style_css = "h1 { margin: 0; padding: 20pt; font-size: 22pt; color: #fff; background-color: #006ec0; display: block; text-transform: uppercase; } html, body { font-family: Arial; margin: 0; padding: 0; } p { text-align: justify; font-size: 13pt; } .des { font-size: 14pt; color: #006ec0; padding-bottom: 0px !important; } .fw { width: 60px; display: block; float: left; } .color { width: 50px; height: 50px; border: 1px solid #ccc; } .range { width: 300px; } a:link, a:visited { text-decoration: none; font-size: 13pt; color: #006ec0; } a:hover, a:focus { color: #f00; } #content { padding: 15px 15px 60px 15px; } #footer { position: fixed; bottom: 0px; height: 45px; background-color: #006ec0; width: 100%; } #footer p { color: #fff; padding-left: 20px; padding-right: 20px; font-size: 10pt !important; } #footer a { color: #fff; } #footer a:hover { color: #f00; } div.content { background-color: #fff; padding-bottom: 65px; overflow: hidden; } span.warn { display: inline-block; padding-left: 20px; color: #ff9900; font-style: italic; } input { padding: 10px; font-size: 13pt; } input.button { background-color: #006ec0; color: #fff; border: 0px; float: right; text-transform: uppercase; } input.cb { margin-bottom: 20px; } label { font-size: 14pt; } .left { float: left; } .right { float: right; } .inputWrp { position: relative; } .inputWrp .inputText { height: 35px; width: 90%; margin-bottom: 20px; border: 1px solid #ccc; border-top: none; border-right: none; } .inputWrp .floating_label { position: absolute; pointer-events: none; top: 20px; left: 10px; transition: 0.2s ease all; } .inputWrp input:focus ~ .floating_label, .inputWrp input:not(:focus):valid ~ .floating_label { top: 0px; left: 20px; font-size: 10px; color: blue; opacity: 1; } "; |
@ -0,0 +1,45 @@ |
|||||
|
<!doctype html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>Index - {DEVICE}</title> |
||||
|
<link rel="stylesheet" type="text/css" href="style.css"/> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
<script type="text/javascript"> |
||||
|
window.setInterval("getAjax('/uptime', 'uptime')", 1000); |
||||
|
window.setInterval("getAjax('/time', 'time')", 1000); |
||||
|
|
||||
|
function getAjax(url, resid) { |
||||
|
var http = null; |
||||
|
http = new XMLHttpRequest(); |
||||
|
if(http != null) { |
||||
|
http.open("GET", url, true); |
||||
|
http.onreadystatechange = print; |
||||
|
http.send(null); |
||||
|
} |
||||
|
|
||||
|
function print() { |
||||
|
if(http.readyState == 4) { |
||||
|
document.getElementById(resid).innerHTML = http.responseText; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>AHOY - {DEVICE}</h1> |
||||
|
<div id="content" class="content"> |
||||
|
<p> |
||||
|
<a href="/update">Update</a><br/> |
||||
|
<br/> |
||||
|
<a href="/setup">Setup</a><br/> |
||||
|
<a href="/reboot">Reboot</a> |
||||
|
</p> |
||||
|
<p><span class="des">Uptime: </span><span id="uptime"></span></p> |
||||
|
<p><span class="des">Time: </span><span id="time"></span></p> |
||||
|
</div> |
||||
|
<div id="footer"> |
||||
|
<p class="left">© 2022</p> |
||||
|
<p class="right">AHOY :: {VERSION}</p> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,51 @@ |
|||||
|
<!doctype html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>Setup - {DEVICE}</title> |
||||
|
<link rel="stylesheet" type="text/css" href="style.css"/> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>Setup</h1> |
||||
|
<div id="setup" class="content"> |
||||
|
<div id="content"> |
||||
|
<p> |
||||
|
Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information. |
||||
|
</p> |
||||
|
<form method="post" action="/save"> |
||||
|
<p class="des">WiFi</p> |
||||
|
<div class="inputWrp"> |
||||
|
<input type="text" class="inputText" name="ssid" value="{SSID}" required/> |
||||
|
<span class="floating_label">SSID</span> |
||||
|
</div> |
||||
|
<div class="inputWrp"> |
||||
|
<input type="password" class="inputText" name="pwd" value="{PWD}" required/> |
||||
|
<span class="floating_label">PASSWORD</span> |
||||
|
</div> |
||||
|
<p class="des">Device Host Name</p> |
||||
|
<div class="inputWrp"> |
||||
|
<input type="text" class="inputText" name="device" value="{DEVICE}" required/> |
||||
|
<span class="floating_label">DEVICE NAME</span> |
||||
|
</div> |
||||
|
|
||||
|
<p class="des">General</p> |
||||
|
|
||||
|
<div class="inputWrp"> |
||||
|
<input type="text" class="inputText" name="hoy_addr" value="{HOY_ADDR}" required/> |
||||
|
<span class="floating_label">HOYMILES ADDRESS (eg. 11:22:33:44:55:66)</span> |
||||
|
</div> |
||||
|
<input type="checkbox" class="cb" name="reboot"/> |
||||
|
<label for="reboot">Reboot device after successful save</label> |
||||
|
|
||||
|
<input type="submit" value="save" class="button" /> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div id="footer"> |
||||
|
<p class="left"><a href="/">Home</a></p> |
||||
|
<p class="left"><a href="/update">Update Firmware</a></p> |
||||
|
<p class="right">AHOY - {VERSION}</p> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,151 @@ |
|||||
|
h1 { |
||||
|
margin: 0; |
||||
|
padding: 20pt; |
||||
|
font-size: 22pt; |
||||
|
color: #fff; |
||||
|
background-color: #006ec0; |
||||
|
display: block; |
||||
|
text-transform: uppercase; |
||||
|
} |
||||
|
|
||||
|
html, body { |
||||
|
font-family: Arial; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
p { |
||||
|
text-align: justify; |
||||
|
font-size: 13pt; |
||||
|
} |
||||
|
|
||||
|
.des { |
||||
|
font-size: 14pt; |
||||
|
color: #006ec0; |
||||
|
padding-bottom: 0px !important; |
||||
|
} |
||||
|
|
||||
|
.fw { |
||||
|
width: 60px; |
||||
|
display: block; |
||||
|
float: left; |
||||
|
} |
||||
|
|
||||
|
.color { |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
border: 1px solid #ccc; |
||||
|
} |
||||
|
|
||||
|
.range { |
||||
|
width: 300px; |
||||
|
} |
||||
|
|
||||
|
a:link, a:visited { |
||||
|
text-decoration: none; |
||||
|
font-size: 13pt; |
||||
|
color: #006ec0; |
||||
|
} |
||||
|
|
||||
|
a:hover, a:focus { |
||||
|
color: #f00; |
||||
|
} |
||||
|
|
||||
|
#content { |
||||
|
padding: 15px 15px 60px 15px; |
||||
|
} |
||||
|
|
||||
|
#footer { |
||||
|
position: fixed; |
||||
|
bottom: 0px; |
||||
|
height: 45px; |
||||
|
background-color: #006ec0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
#footer p { |
||||
|
color: #fff; |
||||
|
padding-left: 20px; |
||||
|
padding-right: 20px; |
||||
|
font-size: 10pt !important; |
||||
|
} |
||||
|
|
||||
|
#footer a { |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
#footer a:hover { |
||||
|
color: #f00; |
||||
|
} |
||||
|
|
||||
|
div.content { |
||||
|
background-color: #fff; |
||||
|
padding-bottom: 65px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
span.warn { |
||||
|
display: inline-block; |
||||
|
padding-left: 20px; |
||||
|
color: #ff9900; |
||||
|
font-style: italic; |
||||
|
} |
||||
|
|
||||
|
input { |
||||
|
padding: 10px; |
||||
|
font-size: 13pt; |
||||
|
} |
||||
|
|
||||
|
input.button { |
||||
|
background-color: #006ec0; |
||||
|
color: #fff; |
||||
|
border: 0px; |
||||
|
float: right; |
||||
|
text-transform: uppercase; |
||||
|
} |
||||
|
|
||||
|
input.cb { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
label { |
||||
|
font-size: 14pt; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
float: left; |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
float: right; |
||||
|
} |
||||
|
|
||||
|
.inputWrp { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.inputWrp .inputText { |
||||
|
height: 35px; |
||||
|
width: 90%; |
||||
|
margin-bottom: 20px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-top: none; |
||||
|
border-right: none; |
||||
|
} |
||||
|
|
||||
|
.inputWrp .floating_label { |
||||
|
position: absolute; |
||||
|
pointer-events: none; |
||||
|
top: 20px; |
||||
|
left: 10px; |
||||
|
transition: 0.2s ease all; |
||||
|
} |
||||
|
|
||||
|
.inputWrp input:focus ~ .floating_label, |
||||
|
.inputWrp input:not(:focus):valid ~ .floating_label { |
||||
|
top: 0px; |
||||
|
left: 20px; |
||||
|
font-size: 10px; |
||||
|
color: blue; |
||||
|
opacity: 1; |
||||
|
} |
@ -0,0 +1,340 @@ |
|||||
|
#include "main.h" |
||||
|
#include "version.h" |
||||
|
|
||||
|
#include "html/h/style_css.h" |
||||
|
#include "html/h/setup_html.h" |
||||
|
|
||||
|
Main::Main(void) { |
||||
|
mDns = new DNSServer(); |
||||
|
mWeb = new ESP8266WebServer(80); |
||||
|
mUpdater = new ESP8266HTTPUpdateServer(); |
||||
|
mUdp = new WiFiUDP(); |
||||
|
|
||||
|
mApActive = true; |
||||
|
|
||||
|
snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); |
||||
|
|
||||
|
memset(&mDeviceName, 0, DEVNAME_LEN); |
||||
|
|
||||
|
mEep = new eep(); |
||||
|
Serial.begin(115200); |
||||
|
|
||||
|
mUptimeSecs = 0; |
||||
|
mUptimeTicker = new Ticker(); |
||||
|
mUptimeTicker->attach(1, std::bind(&Main::uptimeTicker, this)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::setup(const char *ssid, const char *pwd, uint32_t timeout) { |
||||
|
bool startAp = mApActive; |
||||
|
|
||||
|
mWeb->on("/setup", std::bind(&Main::showSetup, this)); |
||||
|
mWeb->on("/save", std::bind(&Main::showSave, this)); |
||||
|
mWeb->on("/uptime", std::bind(&Main::showUptime, this)); |
||||
|
mWeb->on("/time", std::bind(&Main::showTime, this)); |
||||
|
mWeb->on("/style.css", std::bind(&Main::showCss, this)); |
||||
|
mWeb->on("/reboot", std::bind(&Main::showReboot, this)); |
||||
|
mWeb->onNotFound (std::bind(&Main::showNotFound, this)); |
||||
|
|
||||
|
startAp = getConfig(); |
||||
|
if(String(mDeviceName) != "") |
||||
|
WiFi.hostname(mDeviceName); |
||||
|
|
||||
|
if(false == startAp) |
||||
|
startAp = setupStation(timeout); |
||||
|
|
||||
|
if(true == startAp) { |
||||
|
if(strlen(pwd) < 8) |
||||
|
Serial.println("password must be at least 8 characters long"); |
||||
|
setupAp(ssid, pwd); |
||||
|
} |
||||
|
|
||||
|
mUpdater->setup(mWeb); |
||||
|
|
||||
|
mApActive = startAp; |
||||
|
|
||||
|
mTimestamp = getNtpTime(); |
||||
|
//Serial.println("[NTP]: " + getDateTimeStr(getNtpTime()));
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::loop(void) { |
||||
|
if(mApActive) |
||||
|
mDns->processNextRequest(); |
||||
|
mWeb->handleClient(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
bool Main::getConfig(void) { |
||||
|
bool mApActive = false; |
||||
|
|
||||
|
mEep->read(ADDR_SSID, mStationSsid, SSID_LEN); |
||||
|
mEep->read(ADDR_PWD, mStationPwd, PWD_LEN); |
||||
|
mEep->read(ADDR_DEVNAME, mDeviceName, DEVNAME_LEN); |
||||
|
|
||||
|
if(mStationSsid[0] == 0xff) { // empty memory
|
||||
|
mApActive = true; |
||||
|
memset(mStationSsid, 0, SSID_LEN); |
||||
|
} |
||||
|
if(mStationPwd[0] == 0xff) |
||||
|
memset(mStationPwd, 0, PWD_LEN); |
||||
|
if(mDeviceName[0] == 0xff) |
||||
|
memset(mDeviceName, 0, DEVNAME_LEN); |
||||
|
|
||||
|
return mApActive; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::setupAp(const char *ssid, const char *pwd) { |
||||
|
IPAddress apIp(192, 168, 1, 1); |
||||
|
|
||||
|
WiFi.mode(WIFI_AP); |
||||
|
WiFi.softAPConfig(apIp, apIp, IPAddress(255, 255, 255, 0)); |
||||
|
WiFi.softAP(ssid, pwd); |
||||
|
|
||||
|
mDns->start(mDnsPort, "*", apIp); |
||||
|
|
||||
|
mWeb->onNotFound([&]() { |
||||
|
showSetup(); |
||||
|
}); |
||||
|
mWeb->on("/", std::bind(&Main::showSetup, this)); |
||||
|
|
||||
|
mWeb->begin(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
bool Main::setupStation(uint32_t timeout) { |
||||
|
int32_t cnt = timeout * 10; |
||||
|
bool startAp = false; |
||||
|
|
||||
|
WiFi.mode(WIFI_STA); |
||||
|
WiFi.begin(mStationSsid, mStationPwd); |
||||
|
|
||||
|
delay(5000); |
||||
|
Serial.println("wait for network"); |
||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||
|
delay(100); |
||||
|
if(cnt % 100 == 0) |
||||
|
Serial.println("."); |
||||
|
else |
||||
|
Serial.print("."); |
||||
|
|
||||
|
if(timeout > 0) { // limit == 0 -> no limit
|
||||
|
if(--cnt <= 0) { |
||||
|
startAp = true; |
||||
|
WiFi.disconnect(); |
||||
|
delay(100); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
Serial.println("."); |
||||
|
|
||||
|
if(false == startAp) { |
||||
|
mWeb->begin(); |
||||
|
} |
||||
|
|
||||
|
delay(1000); |
||||
|
|
||||
|
return startAp; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showSetup(void) { |
||||
|
String html = setup_html; |
||||
|
html.replace("{SSID}", mStationSsid); |
||||
|
// PWD will be left at the default value (for protection)
|
||||
|
// -> the PWD will only be changed if it does not match the default "{PWD}"
|
||||
|
html.replace("{DEVICE}", String(mDeviceName)); |
||||
|
html.replace("{VERSION}", String(mVersion)); |
||||
|
|
||||
|
mWeb->send(200, "text/html", html); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showCss(void) { |
||||
|
mWeb->send(200, "text/css", style_css); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showSave(void) { |
||||
|
saveValues(true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::saveValues(bool webSend = true) { |
||||
|
if(mWeb->args() > 0) { |
||||
|
if(mWeb->arg("ssid") != "") { |
||||
|
memset(mStationSsid, 0, SSID_LEN); |
||||
|
mWeb->arg("ssid").toCharArray(mStationSsid, SSID_LEN); |
||||
|
mEep->write(ADDR_SSID, mStationSsid, SSID_LEN); |
||||
|
|
||||
|
if(mWeb->arg("pwd") != "{PWD}") { |
||||
|
memset(mStationPwd, 0, PWD_LEN); |
||||
|
mWeb->arg("pwd").toCharArray(mStationPwd, PWD_LEN); |
||||
|
mEep->write(ADDR_PWD, mStationPwd, PWD_LEN); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
memset(mDeviceName, 0, DEVNAME_LEN); |
||||
|
mWeb->arg("device").toCharArray(mDeviceName, DEVNAME_LEN); |
||||
|
mEep->write(ADDR_DEVNAME, mDeviceName, DEVNAME_LEN); |
||||
|
|
||||
|
|
||||
|
if(webSend) { |
||||
|
if(mWeb->arg("reboot") == "on") |
||||
|
showReboot(); |
||||
|
else |
||||
|
mWeb->send(200, "text/html", "<!doctype html><html><head><title>Setup saved</title><meta http-equiv=\"refresh\" content=\"0; URL=/setup\"></head><body>" |
||||
|
"<p>saved</p></body></html>"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showUptime(void) { |
||||
|
char time[20] = {0}; |
||||
|
|
||||
|
int upTimeSc = uint32_t((mUptimeSecs) % 60); |
||||
|
int upTimeMn = uint32_t((mUptimeSecs / (60)) % 60); |
||||
|
int upTimeHr = uint32_t((mUptimeSecs / (60 * 60)) % 24); |
||||
|
int upTimeDy = uint32_t((mUptimeSecs / (60 * 60 * 24)) % 365); |
||||
|
|
||||
|
snprintf(time, 20, "%d Tage, %02d:%02d:%02d", upTimeDy, upTimeHr, upTimeMn, upTimeSc); |
||||
|
|
||||
|
mWeb->send(200, "text/plain", String(time)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showTime(void) { |
||||
|
mWeb->send(200, "text/plain", getDateTimeStr(mTimestamp)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showNotFound(void) { |
||||
|
String msg = "File Not Found\n\n"; |
||||
|
msg += "URI: "; |
||||
|
msg += mWeb->uri(); |
||||
|
msg += "\nMethod: "; |
||||
|
msg += ( mWeb->method() == HTTP_GET ) ? "GET" : "POST"; |
||||
|
msg += "\nArguments: "; |
||||
|
msg += mWeb->args(); |
||||
|
msg += "\n"; |
||||
|
|
||||
|
for(uint8_t i = 0; i < mWeb->args(); i++ ) { |
||||
|
msg += " " + mWeb->argName(i) + ": " + mWeb->arg(i) + "\n"; |
||||
|
} |
||||
|
|
||||
|
mWeb->send(404, "text/plain", msg); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::showReboot(void) { |
||||
|
mWeb->send(200, "text/html", "<!doctype html><html><head><title>Rebooting ...</title><meta http-equiv=\"refresh\" content=\"10; URL=/\"></head><body>rebooting ... auto reload after 10s</body></html>"); |
||||
|
delay(1000); |
||||
|
ESP.restart(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::uptimeTicker(void) { |
||||
|
mUptimeSecs++; |
||||
|
mTimestamp++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
time_t Main::getNtpTime(void) { |
||||
|
time_t date = 0; |
||||
|
IPAddress timeServer; |
||||
|
uint8_t buf[NTP_PACKET_SIZE]; |
||||
|
uint8_t retry = 0; |
||||
|
|
||||
|
WiFi.hostByName (TIMESERVER_NAME, timeServer); |
||||
|
mUdp->begin(TIME_LOCAL_PORT); |
||||
|
|
||||
|
|
||||
|
sendNTPpacket(timeServer); |
||||
|
|
||||
|
while(retry++ < 5) { |
||||
|
int wait = 150; |
||||
|
while(--wait) { |
||||
|
if(NTP_PACKET_SIZE <= mUdp->parsePacket()) { |
||||
|
uint64_t secsSince1900; |
||||
|
mUdp->read(buf, NTP_PACKET_SIZE); |
||||
|
secsSince1900 = (buf[40] << 24); |
||||
|
secsSince1900 |= (buf[41] << 16); |
||||
|
secsSince1900 |= (buf[42] << 8); |
||||
|
secsSince1900 |= (buf[43] ); |
||||
|
|
||||
|
date = secsSince1900 - 2208988800UL; // UTC time
|
||||
|
date += (TIMEZONE + offsetDayLightSaving(date)) * 3600; |
||||
|
break; |
||||
|
} |
||||
|
else |
||||
|
delay(10); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
void Main::sendNTPpacket(IPAddress& address) { |
||||
|
uint8_t buf[NTP_PACKET_SIZE] = {0}; |
||||
|
|
||||
|
buf[0] = B11100011; // LI, Version, Mode
|
||||
|
buf[1] = 0; // Stratum
|
||||
|
buf[2] = 6; // Max Interval between messages in seconds
|
||||
|
buf[3] = 0xEC; // Clock Precision
|
||||
|
// bytes 4 - 11 are for Root Delay and Dispersion and were set to 0 by memset
|
||||
|
buf[12] = 49; // four-byte reference ID identifying
|
||||
|
buf[13] = 0x4E; |
||||
|
buf[14] = 49; |
||||
|
buf[15] = 52; |
||||
|
|
||||
|
mUdp->beginPacket(address, 123); // NTP request, port 123
|
||||
|
mUdp->write(buf, NTP_PACKET_SIZE); |
||||
|
mUdp->endPacket(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
String Main::getDateTimeStr(time_t t) { |
||||
|
char str[20] = {0}; |
||||
|
sprintf(str, "%04d-%02d-%02d+%02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t)); |
||||
|
return String(str); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
// calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
|
||||
|
// from: https://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
|
||||
|
time_t Main::offsetDayLightSaving (uint32_t local_t) { |
||||
|
int m = month (local_t); |
||||
|
if(m < 3 || m > 10) return 0; // no DSL in Jan, Feb, Nov, Dez
|
||||
|
if(m > 3 && m < 10) return 1; // DSL in Apr, May, Jun, Jul, Aug, Sep
|
||||
|
int y = year (local_t); |
||||
|
int h = hour (local_t); |
||||
|
int hToday = (h + 24 * day(local_t)); |
||||
|
if((m == 3 && hToday >= (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 4) % 7))) |
||||
|
|| (m == 10 && hToday < (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 1) % 7))) ) |
||||
|
return 1; |
||||
|
else |
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,79 @@ |
|||||
|
#ifndef __MAIN_H__ |
||||
|
#define __MAIN_H__ |
||||
|
|
||||
|
#include "Arduino.h" |
||||
|
|
||||
|
#include <ESP8266WiFi.h> |
||||
|
#include <DNSServer.h> |
||||
|
#include <ESP8266WebServer.h> |
||||
|
#include <Ticker.h> |
||||
|
|
||||
|
#include <ESP8266HTTPUpdateServer.h> |
||||
|
|
||||
|
// NTP
|
||||
|
#include <WiFiUdp.h> |
||||
|
#include <TimeLib.h> |
||||
|
|
||||
|
#include "eep.h" |
||||
|
#include "defines.h" |
||||
|
|
||||
|
|
||||
|
const byte mDnsPort = 53; |
||||
|
|
||||
|
/* TIMESERVER CONFIG */ |
||||
|
#define TIMESERVER_NAME "pool.ntp.org" |
||||
|
#define TIME_LOCAL_PORT 8888 |
||||
|
#define NTP_PACKET_SIZE 48 |
||||
|
#define TIMEZONE 1 // Central European time +1
|
||||
|
|
||||
|
class Main { |
||||
|
public: |
||||
|
Main(void); |
||||
|
virtual void setup(const char *ssid, const char *pwd, uint32_t timeout); |
||||
|
virtual void loop(); |
||||
|
String getDateTimeStr (time_t t); |
||||
|
|
||||
|
|
||||
|
protected: |
||||
|
void showReboot(void); |
||||
|
virtual void saveValues(bool webSend); |
||||
|
|
||||
|
char mStationSsid[SSID_LEN]; |
||||
|
char mStationPwd[PWD_LEN]; |
||||
|
bool mLinkLedActive; |
||||
|
bool mApActive; |
||||
|
ESP8266WebServer *mWeb; |
||||
|
char mVersion[9]; |
||||
|
char mDeviceName[DEVNAME_LEN]; |
||||
|
eep *mEep; |
||||
|
uint32_t mTimestamp; |
||||
|
|
||||
|
|
||||
|
private: |
||||
|
bool getConfig(void); |
||||
|
void setupAp(const char *ssid, const char *pwd); |
||||
|
bool setupStation(uint32_t timeout); |
||||
|
|
||||
|
void showNotFound(void); |
||||
|
virtual void showSetup(void); |
||||
|
virtual void showSave(void); |
||||
|
void showUptime(void); |
||||
|
void showTime(void); |
||||
|
void showCss(void); |
||||
|
void uptimeTicker(void); |
||||
|
|
||||
|
|
||||
|
time_t getNtpTime(void); |
||||
|
void sendNTPpacket(IPAddress& address); |
||||
|
time_t offsetDayLightSaving (uint32_t local_t); |
||||
|
|
||||
|
Ticker *mUptimeTicker; |
||||
|
uint32_t mUptimeSecs; |
||||
|
|
||||
|
DNSServer *mDns; |
||||
|
ESP8266HTTPUpdateServer *mUpdater; |
||||
|
|
||||
|
WiFiUDP *mUdp; // for time server
|
||||
|
}; |
||||
|
|
||||
|
#endif /*__MAIN_H__*/ |
Binary file not shown.
Loading…
Reference in new issue