@ -1,2 +1,3 @@ | |||||
*.gz.h -diff -merge | *.gz.h -diff -merge | ||||
*.gz.h linguist-generated=true | *.gz.h linguist-generated=true | ||||
*.ini text eol=lf |
@ -1,15 +1,13 @@ | |||||
.clang_complete | |||||
core_version.h | |||||
custom.h | |||||
.DS_Store | |||||
.gcc-flags.json | |||||
.pioenvs | |||||
.piolibdeps | |||||
.python-version | |||||
.travis.yml | |||||
.vscode | |||||
.vscode/.browse.c_cpp.db* | |||||
.vscode/c_cpp_properties.json | |||||
.vscode/launch.json | |||||
.pioenvs | |||||
.piolibdeps | |||||
.clang_complete | |||||
core_version.h | |||||
custom.h | |||||
.DS_Store | |||||
.gcc-flags.json | |||||
.python-version | |||||
.travis.yml | |||||
.vscode | |||||
.vscode/.browse.c_cpp.db* | |||||
.vscode/c_cpp_properties.json | |||||
.vscode/launch.json | |||||
.pio | |||||
libraries/ |
@ -1,19 +0,0 @@ | |||||
/* Flash Split for 1M chips, no SPIFFS, 1 sector for EEPROM */ | |||||
/* sketch 999KB */ | |||||
/* eeprom 4KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xf9ff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x402FB000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x402FB000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -1,19 +0,0 @@ | |||||
/* Flash Split for 1M chips, no SPIFFS, 2 sectors for EEPROM */ | |||||
/* sketch 995KB */ | |||||
/* eeprom 8KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xf8ff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x402FA000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x402FA000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -1,19 +0,0 @@ | |||||
/* Flash Split for 512K chips, no SPIFFS, 1 sector for EEPROM */ | |||||
/* sketch 487KB */ | |||||
/* eeprom 4KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0x79ff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x4027B000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x4027B000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -0,0 +1,97 @@ | |||||
/* | |||||
* | |||||
* Created: 29.03.2018 | |||||
* | |||||
* Authors: | |||||
* | |||||
* Assembled from the code released on Stackoverflow by: | |||||
* Dennis (instructable.com/member/nqtronix) | https://stackoverflow.com/questions/23032002/c-c-how-to-get-integer-unix-timestamp-of-build-time-not-string | |||||
* and | |||||
* Alexis Wilke | https://stackoverflow.com/questions/10538444/do-you-know-of-a-c-macro-to-compute-unix-time-and-date | |||||
* | |||||
* Assembled by Jean Rabault | |||||
* | |||||
* UNIX_TIMESTAMP gives the UNIX timestamp (unsigned long integer of seconds since 1st Jan 1970) of compilation from macros using the compiler defined __TIME__ macro. | |||||
* This should include Gregorian calendar leap days, in particular the 29ths of February, 100 and 400 years modulo leaps. | |||||
* | |||||
* Careful: __TIME__ is the local time of the computer, NOT the UTC time in general! | |||||
* | |||||
*/ | |||||
#ifndef COMPILE_TIME_H_ | |||||
#define COMPILE_TIME_H_ | |||||
// Some definitions for calculation | |||||
#define SEC_PER_MIN 60UL | |||||
#define SEC_PER_HOUR 3600UL | |||||
#define SEC_PER_DAY 86400UL | |||||
#define SEC_PER_YEAR (SEC_PER_DAY*365) | |||||
// extracts 1..4 characters from a string and interprets it as a decimal value | |||||
#define CONV_STR2DEC_1(str, i) (str[i]>'0'?str[i]-'0':0) | |||||
#define CONV_STR2DEC_2(str, i) (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0') | |||||
#define CONV_STR2DEC_3(str, i) (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0') | |||||
#define CONV_STR2DEC_4(str, i) (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0') | |||||
// Custom "glue logic" to convert the month name to a usable number | |||||
#define GET_MONTH(str, i) (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 : \ | |||||
str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 : \ | |||||
str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 : \ | |||||
str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 : \ | |||||
str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 : \ | |||||
str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 : \ | |||||
str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 : \ | |||||
str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 : \ | |||||
str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 : \ | |||||
str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 : \ | |||||
str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 : \ | |||||
str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0) | |||||
// extract the information from the time string given by __TIME__ and __DATE__ | |||||
#define __TIME_SECOND__ (CONV_STR2DEC_2(__TIME__, 6)) | |||||
#define __TIME_MINUTE__ (CONV_STR2DEC_2(__TIME__, 3)) | |||||
#define __TIME_HOUR__ (CONV_STR2DEC_2(__TIME__, 0)) | |||||
#define __TIME_DAY__ (CONV_STR2DEC_2(__DATE__, 4)) | |||||
#define __TIME_MONTH__ (GET_MONTH(__DATE__, 0)) | |||||
#define __TIME_YEAR__ (CONV_STR2DEC_4(__DATE__, 7)) | |||||
// Days in February | |||||
#define _UNIX_TIMESTAMP_FDAY(year) \ | |||||
(((year) % 400) == 0UL ? 29UL : \ | |||||
(((year) % 100) == 0UL ? 28UL : \ | |||||
(((year) % 4) == 0UL ? 29UL : \ | |||||
28UL))) | |||||
// Days in the year | |||||
#define _UNIX_TIMESTAMP_YDAY(year, month, day) \ | |||||
( \ | |||||
/* January */ day \ | |||||
/* February */ + (month >= 2 ? 31UL : 0UL) \ | |||||
/* March */ + (month >= 3 ? _UNIX_TIMESTAMP_FDAY(year) : 0UL) \ | |||||
/* April */ + (month >= 4 ? 31UL : 0UL) \ | |||||
/* May */ + (month >= 5 ? 30UL : 0UL) \ | |||||
/* June */ + (month >= 6 ? 31UL : 0UL) \ | |||||
/* July */ + (month >= 7 ? 30UL : 0UL) \ | |||||
/* August */ + (month >= 8 ? 31UL : 0UL) \ | |||||
/* September */+ (month >= 9 ? 31UL : 0UL) \ | |||||
/* October */ + (month >= 10 ? 30UL : 0UL) \ | |||||
/* November */ + (month >= 11 ? 31UL : 0UL) \ | |||||
/* December */ + (month >= 12 ? 30UL : 0UL) \ | |||||
) | |||||
// get the UNIX timestamp from a digits representation | |||||
#define _UNIX_TIMESTAMP(year, month, day, hour, minute, second) \ | |||||
( /* time */ second \ | |||||
+ minute * SEC_PER_MIN \ | |||||
+ hour * SEC_PER_HOUR \ | |||||
+ /* year day (month + day) */ (_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * SEC_PER_DAY \ | |||||
+ /* year */ (year - 1970UL) * SEC_PER_YEAR \ | |||||
+ ((year - 1969UL) / 4UL) * SEC_PER_DAY \ | |||||
- ((year - 1901UL) / 100UL) * SEC_PER_DAY \ | |||||
+ ((year - 1601UL) / 400UL) * SEC_PER_DAY \ | |||||
) | |||||
// the UNIX timestamp | |||||
#define __UNIX_TIMESTAMP__ (_UNIX_TIMESTAMP(__TIME_YEAR__, __TIME_MONTH__, __TIME_DAY__, __TIME_HOUR__, __TIME_MINUTE__, __TIME_SECOND__)) | |||||
#endif |
@ -0,0 +1,47 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// printf-like debug methods | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
void debugSendImpl(const char*); | |||||
void _debugSend(const char * format, va_list args) { | |||||
char temp[64]; | |||||
int len = ets_vsnprintf(temp, sizeof(temp), format, args); | |||||
if (len < 64) { debugSendImpl(temp); return; } | |||||
auto buffer = new char[len + 1]; | |||||
ets_vsnprintf(buffer, len + 1, format, args); | |||||
debugSendImpl(buffer); | |||||
delete[] buffer; | |||||
} | |||||
void debugSend(const char* format, ...) { | |||||
va_list args; | |||||
va_start(args, format); | |||||
_debugSend(format, args); | |||||
va_end(args); | |||||
} | |||||
void debugSend_P(PGM_P format_P, ...) { | |||||
char format[strlen_P(format_P) + 1]; | |||||
memcpy_P(format, format_P, sizeof(format)); | |||||
va_list args; | |||||
va_start(args, format_P); | |||||
_debugSend(format, args); | |||||
va_end(args); | |||||
} |
@ -0,0 +1,228 @@ | |||||
/* ---------------------------- Original copyright ----------------------------- | |||||
* | |||||
* Encoder Library, for measuring quadrature encoded signals | |||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html | |||||
* Copyright (c) 2011,2013 PJRC.COM, LLC - Paul Stoffregen <paul@pjrc.com> | |||||
* | |||||
* Version 1.2 - fix -2 bug in C-only code | |||||
* Version 1.1 - expand to support boards with up to 60 interrupts | |||||
* Version 1.0 - initial release | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice shall be included in | |||||
* all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
* | |||||
* ----------------------------------------------------------------------------- | |||||
* | |||||
* Encoder.h, updated for ESP8266 use in ESPurna. Other hardware is not supported. | |||||
* | |||||
* - Added ESP-specific attributes to ISR handlers to place them in IRAM. | |||||
* - Reduced per-encoder structure sizes - only 5 Encoders can be used on ESP8266, | |||||
* and we can directly reference pin number instead of storing both register and bitmask | |||||
* | |||||
*/ | |||||
#pragma once | |||||
// _______ _______ | |||||
// Pin1 ______| |_______| |______ Pin1 | |||||
// negative <--- _______ _______ __ --> positive | |||||
// Pin2 __| |_______| |_______| Pin2 | |||||
// new new old old | |||||
// pin2 pin1 pin2 pin1 Result | |||||
// ---- ---- ---- ---- ------ | |||||
// 0 0 0 0 no movement | |||||
// 0 0 0 1 +1 | |||||
// 0 0 1 0 -1 | |||||
// 0 0 1 1 +2 (assume pin1 edges only) | |||||
// 0 1 0 0 -1 | |||||
// 0 1 0 1 no movement | |||||
// 0 1 1 0 -2 (assume pin1 edges only) | |||||
// 0 1 1 1 +1 | |||||
// 1 0 0 0 +1 | |||||
// 1 0 0 1 -2 (assume pin1 edges only) | |||||
// 1 0 1 0 no movement | |||||
// 1 0 1 1 -1 | |||||
// 1 1 0 0 +2 (assume pin1 edges only) | |||||
// 1 1 0 1 -1 | |||||
// 1 1 1 0 +1 | |||||
// 1 1 1 1 no movement | |||||
namespace EncoderLibrary { | |||||
typedef struct { | |||||
uint8_t pin1; | |||||
uint8_t pin2; | |||||
uint8_t state; | |||||
int32_t position; | |||||
} encoder_values_t; | |||||
constexpr const unsigned char ENCODERS_MAXIMUM {5u}; | |||||
encoder_values_t * EncoderValues[ENCODERS_MAXIMUM] = {nullptr}; | |||||
uint8_t _encoderFindStorage() { | |||||
for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) { | |||||
if (EncoderValues[i] == nullptr) { | |||||
return i; | |||||
} | |||||
} | |||||
return ENCODERS_MAXIMUM; | |||||
} | |||||
void _encoderCleanStorage(uint8_t pin1, uint8_t pin2) { | |||||
for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) { | |||||
if (EncoderValues[i] == nullptr) continue; | |||||
if (((EncoderValues[i])->pin1 == pin1) && ((EncoderValues[i])->pin2 == pin2)) { | |||||
EncoderValues[i] = nullptr; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
// update() is not meant to be called from outside Encoder, | |||||
// but it is public to allow static interrupt routines. | |||||
void ICACHE_RAM_ATTR update(encoder_values_t *target) { | |||||
uint8_t p1val = GPIP(target->pin1); | |||||
uint8_t p2val = GPIP(target->pin2); | |||||
uint8_t state = target->state & 3; | |||||
if (p1val) state |= 4; | |||||
if (p2val) state |= 8; | |||||
target->state = (state >> 2); | |||||
switch (state) { | |||||
case 1: case 7: case 8: case 14: | |||||
target->position++; | |||||
return; | |||||
case 2: case 4: case 11: case 13: | |||||
target->position--; | |||||
return; | |||||
case 3: case 12: | |||||
target->position += 2; | |||||
return; | |||||
case 6: case 9: | |||||
target->position -= 2; | |||||
return; | |||||
} | |||||
} | |||||
// 2 pins per encoder, 1 isr per encoder | |||||
void ICACHE_RAM_ATTR isr0() { update(EncoderValues[0]); } | |||||
void ICACHE_RAM_ATTR isr1() { update(EncoderValues[1]); } | |||||
void ICACHE_RAM_ATTR isr2() { update(EncoderValues[2]); } | |||||
void ICACHE_RAM_ATTR isr3() { update(EncoderValues[3]); } | |||||
void ICACHE_RAM_ATTR isr4() { update(EncoderValues[4]); } | |||||
constexpr void (*_isr_funcs[5])() = { | |||||
isr0, isr1, isr2, isr3, isr4 | |||||
}; | |||||
class Encoder { | |||||
private: | |||||
encoder_values_t values; | |||||
public: | |||||
Encoder(uint8_t pin1, uint8_t pin2) { | |||||
values.pin1 = pin1; | |||||
values.pin2 = pin2; | |||||
pinMode(values.pin1, INPUT_PULLUP); | |||||
pinMode(values.pin2, INPUT_PULLUP); | |||||
values.position = 0; | |||||
// allow time for a passive R-C filter to charge | |||||
// through the pullup resistors, before reading | |||||
// the initial state | |||||
delayMicroseconds(2000); | |||||
uint8_t current = 0; | |||||
if (GPIP(values.pin1)) { | |||||
current |= 1; | |||||
} | |||||
if (GPIP(values.pin2)) { | |||||
current |= 2; | |||||
} | |||||
values.state = current; | |||||
attach(); | |||||
} | |||||
~Encoder() { | |||||
detach(); | |||||
} | |||||
uint8_t pin1() { | |||||
return values.pin1; | |||||
} | |||||
uint8_t pin2() { | |||||
return values.pin2; | |||||
} | |||||
int32_t read() { | |||||
noInterrupts(); | |||||
update(&values); | |||||
int32_t ret = values.position; | |||||
interrupts(); | |||||
return ret; | |||||
} | |||||
void write(int32_t position) { | |||||
noInterrupts(); | |||||
values.position = position; | |||||
interrupts(); | |||||
} | |||||
bool attach() { | |||||
uint8_t index = _encoderFindStorage(); | |||||
if (index >= ENCODERS_MAXIMUM) return false; | |||||
EncoderValues[index] = &values; | |||||
attachInterrupt(values.pin1, _isr_funcs[index], CHANGE); | |||||
attachInterrupt(values.pin2, _isr_funcs[index], CHANGE); | |||||
return true; | |||||
} | |||||
void detach() { | |||||
noInterrupts(); | |||||
_encoderCleanStorage(values.pin1, values.pin2); | |||||
detachInterrupt(values.pin1); | |||||
detachInterrupt(values.pin2); | |||||
interrupts(); | |||||
} | |||||
}; | |||||
} | |||||
using EncoderLibrary::Encoder; |
@ -0,0 +1,112 @@ | |||||
/* | |||||
Show extended heap stats when EspClass::getHeapStats() is available | |||||
*/ | |||||
#pragma once | |||||
#include <type_traits> | |||||
struct heap_stats_t { | |||||
uint32_t available; | |||||
uint16_t usable; | |||||
uint8_t frag_pct; | |||||
}; | |||||
namespace EspClass_has_getHeapStats { | |||||
struct _detector { | |||||
template<typename T, typename = decltype( | |||||
std::declval<T>().getHeapStats(0,0,0))> | |||||
static std::true_type detect(int); | |||||
template<typename> | |||||
static std::false_type detect(...); | |||||
}; | |||||
template <typename T> | |||||
struct detector : public _detector { | |||||
using result = decltype( | |||||
std::declval<detector>().template detect<T>(0)); | |||||
}; | |||||
template <typename T> | |||||
struct typed_check : public detector<T>::result { | |||||
}; | |||||
typed_check<EspClass> check{}; | |||||
}; | |||||
template <typename T> | |||||
void _getHeapStats(std::true_type&, T& instance, heap_stats_t& stats) { | |||||
instance.getHeapStats(&stats.available, &stats.usable, &stats.frag_pct); | |||||
} | |||||
template <typename T> | |||||
void _getHeapStats(std::false_type&, T& instance, heap_stats_t& stats) { | |||||
stats.available = instance.getFreeHeap(); | |||||
stats.usable = 0; | |||||
stats.frag_pct = 0; | |||||
} | |||||
void getHeapStats(heap_stats_t& stats) { | |||||
_getHeapStats(EspClass_has_getHeapStats::check, ESP, stats); | |||||
} | |||||
// WTF | |||||
// Calling ESP.getFreeHeap() is making the system crash on a specific | |||||
// AiLight bulb, but anywhere else it should work as expected | |||||
static bool _heap_value_wtf = false; | |||||
heap_stats_t getHeapStats() { | |||||
heap_stats_t stats; | |||||
if (_heap_value_wtf) { | |||||
stats.available = 9999; | |||||
stats.usable = 9999; | |||||
stats.frag_pct = 0; | |||||
return stats; | |||||
} | |||||
getHeapStats(stats); | |||||
return stats; | |||||
} | |||||
void wtfHeap(bool value) { | |||||
_heap_value_wtf = value; | |||||
} | |||||
unsigned int getFreeHeap() { | |||||
return ESP.getFreeHeap(); | |||||
} | |||||
static unsigned int _initial_heap_value = 0; | |||||
void setInitialFreeHeap() { | |||||
_initial_heap_value = getFreeHeap(); | |||||
} | |||||
unsigned int getInitialFreeHeap() { | |||||
if (0 == _initial_heap_value) { | |||||
setInitialFreeHeap(); | |||||
} | |||||
return _initial_heap_value; | |||||
} | |||||
void infoMemory(const char* name, const heap_stats_t& stats) { | |||||
infoMemory(name, getInitialFreeHeap(), stats.available); | |||||
} | |||||
void infoHeapStats(const char* name, const heap_stats_t& stats) { | |||||
DEBUG_MSG_P( | |||||
PSTR("[MAIN] %-6s: %5u contiguous bytes available (%u%% fragmentation)\n"), | |||||
name, | |||||
stats.usable, | |||||
stats.frag_pct | |||||
); | |||||
} | |||||
void infoHeapStats(bool show_frag_stats = true) { | |||||
const auto stats = getHeapStats(); | |||||
infoMemory("Heap", stats); | |||||
if (show_frag_stats && EspClass_has_getHeapStats::check) { | |||||
infoHeapStats("Heap", stats); | |||||
} | |||||
} |
@ -0,0 +1,247 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// WiFiClientSecure validation helpers | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE | |||||
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL | |||||
#include <WiFiClientSecureBearSSL.h> | |||||
#elif SECURE_CLIENT == SECURE_CLIENT_AXTLS | |||||
#include <WiFiClientSecureAxTLS.h> | |||||
#endif | |||||
namespace SecureClientHelpers { | |||||
using host_callback_f = std::function<String()>; | |||||
using check_callback_f = std::function<int()>; | |||||
using fp_callback_f = std::function<String()>; | |||||
using cert_callback_f = std::function<const char*()>; | |||||
using mfln_callback_f = std::function<uint16_t()>; | |||||
const char * _secureClientCheckAsString(int check) { | |||||
switch (check) { | |||||
case SECURE_CLIENT_CHECK_NONE: return "no validation"; | |||||
case SECURE_CLIENT_CHECK_FINGERPRINT: return "fingerprint validation"; | |||||
case SECURE_CLIENT_CHECK_CA: return "CA validation"; | |||||
default: return "unknown"; | |||||
} | |||||
} | |||||
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS | |||||
using SecureClientClass = axTLS::WiFiClientSecure; | |||||
struct SecureClientConfig { | |||||
SecureClientConfig(const char* tag, host_callback_f host_cb, check_callback_f check_cb, fp_callback_f fp_cb, bool debug = false) : | |||||
tag(tag), | |||||
on_host(host_cb), | |||||
on_check(check_cb), | |||||
on_fingerprint(fp_cb), | |||||
debug(debug) | |||||
{} | |||||
String tag; | |||||
host_callback_f on_host; | |||||
check_callback_f on_check; | |||||
fp_callback_f on_fingerprint; | |||||
bool debug; | |||||
}; | |||||
struct SecureClientChecks { | |||||
SecureClientChecks(SecureClientConfig& config) : | |||||
config(config) | |||||
{} | |||||
int getCheck() { | |||||
return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK); | |||||
} | |||||
bool beforeConnected(SecureClientClass& client) { | |||||
return true; | |||||
} | |||||
// Special condition for legacy client! | |||||
// Otherwise, we are required to connect twice. And it is deemed broken & deprecated anyways... | |||||
bool afterConnected(SecureClientClass& client) { | |||||
bool result = false; | |||||
int check = getCheck(); | |||||
if(config.debug) { | |||||
DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check)); | |||||
} | |||||
if (check == SECURE_CLIENT_CHECK_NONE) { | |||||
if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str()); | |||||
result = true; | |||||
} else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) { | |||||
if (config.on_fingerprint) { | |||||
char _buffer[60] = {0}; | |||||
if (config.on_fingerprint && config.on_host && sslFingerPrintChar(config.on_fingerprint().c_str(), _buffer)) { | |||||
result = client.verify(_buffer, config.on_host().c_str()); | |||||
} | |||||
if (!result) DEBUG_MSG_P(PSTR("[%s] Wrong fingerprint, cannot connect\n"), config.tag.c_str()); | |||||
} | |||||
} else if (check == SECURE_CLIENT_CHECK_CA) { | |||||
if (config.debug) DEBUG_MSG_P(PSTR("[%s] CA verification is not supported with axTLS client\n"), config.tag.c_str()); | |||||
} | |||||
return result; | |||||
} | |||||
SecureClientConfig& config; | |||||
bool debug; | |||||
}; | |||||
#endif // SECURE_CLIENT_AXTLS | |||||
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL | |||||
using SecureClientClass = BearSSL::WiFiClientSecure; | |||||
struct SecureClientConfig { | |||||
SecureClientConfig(const char* tag, check_callback_f check_cb, cert_callback_f cert_cb, fp_callback_f fp_cb, mfln_callback_f mfln_cb, bool debug = false) : | |||||
tag(tag), | |||||
on_check(check_cb), | |||||
on_certificate(cert_cb), | |||||
on_fingerprint(fp_cb), | |||||
on_mfln(mfln_cb), | |||||
debug(debug) | |||||
{} | |||||
String tag; | |||||
check_callback_f on_check; | |||||
cert_callback_f on_certificate; | |||||
fp_callback_f on_fingerprint; | |||||
mfln_callback_f on_mfln; | |||||
bool debug; | |||||
}; | |||||
struct SecureClientChecks { | |||||
SecureClientChecks(SecureClientConfig& config) : | |||||
config(config) | |||||
{} | |||||
int getCheck() { | |||||
return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK); | |||||
} | |||||
bool prepareMFLN(SecureClientClass& client) { | |||||
const uint16_t requested_mfln = (config.on_mfln) ? config.on_mfln() : (SECURE_CLIENT_MFLN); | |||||
bool result = false; | |||||
switch (requested_mfln) { | |||||
// default, do nothing | |||||
case 0: | |||||
result = true; | |||||
break; | |||||
// match valid sizes only | |||||
case 512: | |||||
case 1024: | |||||
case 2048: | |||||
case 4096: | |||||
{ | |||||
client.setBufferSizes(requested_mfln, requested_mfln); | |||||
result = true; | |||||
if (config.debug) { | |||||
DEBUG_MSG_P(PSTR("[%s] MFLN buffer size set to %u\n"), config.tag.c_str(), requested_mfln); | |||||
} | |||||
break; | |||||
} | |||||
default: | |||||
{ | |||||
if (config.debug) { | |||||
DEBUG_MSG_P(PSTR("[%s] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n"), config.tag.c_str()); | |||||
} | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
bool beforeConnected(SecureClientClass& client) { | |||||
int check = getCheck(); | |||||
bool settime = (check == SECURE_CLIENT_CHECK_CA); | |||||
if(config.debug) { | |||||
DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check)); | |||||
} | |||||
if (!ntpSynced() && settime) { | |||||
if (config.debug) DEBUG_MSG_P(PSTR("[%s] Time not synced! Cannot use CA validation\n"), config.tag.c_str()); | |||||
return false; | |||||
} | |||||
prepareMFLN(client); | |||||
if (check == SECURE_CLIENT_CHECK_NONE) { | |||||
if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str()); | |||||
client.setInsecure(); | |||||
} else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) { | |||||
uint8_t _buffer[20] = {0}; | |||||
if (config.on_fingerprint && sslFingerPrintArray(config.on_fingerprint().c_str(), _buffer)) { | |||||
client.setFingerprint(_buffer); | |||||
} | |||||
} else if (check == SECURE_CLIENT_CHECK_CA) { | |||||
client.setX509Time(ntpLocal2UTC(now())); | |||||
if (!certs.getCount()) { | |||||
if (config.on_certificate) certs.append(config.on_certificate()); | |||||
} | |||||
client.setTrustAnchors(&certs); | |||||
} | |||||
return true; | |||||
} | |||||
bool afterConnected(SecureClientClass&) { | |||||
return true; | |||||
} | |||||
bool debug; | |||||
SecureClientConfig& config; | |||||
BearSSL::X509List certs; | |||||
}; | |||||
#endif // SECURE_CLIENT_BEARSSL | |||||
class SecureClient { | |||||
public: | |||||
SecureClient(SecureClientConfig& config) : | |||||
_config(config), | |||||
_checks(_config), | |||||
_client(std::make_unique<SecureClientClass>()) | |||||
{} | |||||
bool afterConnected() { | |||||
return _checks.afterConnected(get()); | |||||
} | |||||
bool beforeConnected() { | |||||
return _checks.beforeConnected(get()); | |||||
} | |||||
SecureClientClass& get() { | |||||
return *_client.get(); | |||||
} | |||||
private: | |||||
SecureClientConfig _config; | |||||
SecureClientChecks _checks; | |||||
std::unique_ptr<SecureClientClass> _client; | |||||
}; | |||||
}; | |||||
using SecureClientConfig = SecureClientHelpers::SecureClientConfig; | |||||
using SecureClientChecks = SecureClientHelpers::SecureClientChecks; | |||||
using SecureClient = SecureClientHelpers::SecureClient; | |||||
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE |
@ -0,0 +1,68 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// Parse char string as URL | |||||
// | |||||
// Adapted from HTTPClient::beginInternal() | |||||
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | |||||
// | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
struct URL { | |||||
String value; | |||||
String protocol; | |||||
String host; | |||||
String path; | |||||
uint16_t port; | |||||
URL(const char* url) { init(url); } | |||||
URL(const String& url) { init(url); } | |||||
void init(String url); | |||||
}; | |||||
void URL::init(String url) { | |||||
this->value = url; | |||||
// cut the protocol part | |||||
int index = url.indexOf("://"); | |||||
if (index > 0) { | |||||
this->protocol = url.substring(0, index); | |||||
url.remove(0, (index + 3)); | |||||
} | |||||
if (this->protocol == "http") { | |||||
this->port = 80; | |||||
} else if (this->protocol == "https") { | |||||
this->port = 443; | |||||
} | |||||
// cut the host part | |||||
String _host; | |||||
index = url.indexOf('/'); | |||||
if (index >= 0) { | |||||
_host = url.substring(0, index); | |||||
} else { | |||||
_host = url; | |||||
} | |||||
// store the remaining part as path | |||||
if (index >= 0) { | |||||
url.remove(0, index); | |||||
this->path = url; | |||||
} else { | |||||
this->path = "/"; | |||||
} | |||||
// separate host from port, when present | |||||
index = _host.indexOf(':'); | |||||
if (index >= 0) { | |||||
this->port = _host.substring(index + 1).toInt(); | |||||
this->host = _host.substring(0, index); | |||||
} else { | |||||
this->host = _host; | |||||
} | |||||
} |
@ -0,0 +1,26 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// Light | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
namespace Light { | |||||
constexpr const unsigned char VALUE_MIN = LIGHT_MIN_VALUE; | |||||
constexpr const unsigned char VALUE_MAX = LIGHT_MAX_VALUE; | |||||
constexpr const unsigned int BRIGHTNESS_MIN = LIGHT_MIN_BRIGHTNESS; | |||||
constexpr const unsigned int BRIGHTNESS_MAX = LIGHT_MAX_BRIGHTNESS; | |||||
// Default to the Philips Hue value that HA also use. | |||||
// https://developers.meethue.com/documentation/core-concepts | |||||
constexpr const unsigned int MIREDS_COLDWHITE = LIGHT_COLDWHITE_MIRED; | |||||
constexpr const unsigned int MIREDS_WARMWHITE = LIGHT_WARMWHITE_MIRED; | |||||
constexpr const unsigned int KELVIN_WARMWHITE = LIGHT_WARMWHITE_KELVIN; | |||||
constexpr const unsigned int KELVIN_COLDWHITE = LIGHT_COLDWHITE_KELVIN; | |||||
constexpr const unsigned int PWM_MIN = LIGHT_MIN_PWM; | |||||
constexpr const unsigned int PWM_MAX = LIGHT_MAX_PWM; | |||||
constexpr const unsigned int PWM_LIMIT = LIGHT_LIMIT_PWM; | |||||
} | |||||
@ -1,295 +0,0 @@ | |||||
/* | |||||
OTA MODULE | |||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#include "ArduinoOTA.h" | |||||
// ----------------------------------------------------------------------------- | |||||
// Arduino OTA | |||||
// ----------------------------------------------------------------------------- | |||||
void _otaConfigure() { | |||||
ArduinoOTA.setPort(OTA_PORT); | |||||
ArduinoOTA.setHostname(getSetting("hostname").c_str()); | |||||
#if USE_PASSWORD | |||||
ArduinoOTA.setPassword(getAdminPass().c_str()); | |||||
#endif | |||||
} | |||||
void _otaLoop() { | |||||
ArduinoOTA.handle(); | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Terminal OTA | |||||
// ----------------------------------------------------------------------------- | |||||
#if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT | |||||
#include <ESPAsyncTCP.h> | |||||
AsyncClient * _ota_client; | |||||
char * _ota_host; | |||||
char * _ota_url; | |||||
unsigned int _ota_port = 80; | |||||
unsigned long _ota_size = 0; | |||||
const char OTA_REQUEST_TEMPLATE[] PROGMEM = | |||||
"GET %s HTTP/1.1\r\n" | |||||
"Host: %s\r\n" | |||||
"User-Agent: ESPurna\r\n" | |||||
"Connection: close\r\n" | |||||
"Content-Type: application/x-www-form-urlencoded\r\n" | |||||
"Content-Length: 0\r\n\r\n\r\n"; | |||||
void _otaFrom(const char * host, unsigned int port, const char * url) { | |||||
if (_ota_host) free(_ota_host); | |||||
if (_ota_url) free(_ota_url); | |||||
_ota_host = strdup(host); | |||||
_ota_url = strdup(url); | |||||
_ota_port = port; | |||||
_ota_size = 0; | |||||
if (_ota_client == NULL) { | |||||
_ota_client = new AsyncClient(); | |||||
} | |||||
_ota_client->onDisconnect([](void *s, AsyncClient *c) { | |||||
DEBUG_MSG_P(PSTR("\n")); | |||||
if (Update.end(true)){ | |||||
DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size); | |||||
deferredReset(100, CUSTOM_RESET_OTA); | |||||
} else { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
eepromRotate(true); | |||||
} | |||||
DEBUG_MSG_P(PSTR("[OTA] Disconnected\n")); | |||||
_ota_client->free(); | |||||
delete _ota_client; | |||||
_ota_client = NULL; | |||||
free(_ota_host); | |||||
_ota_host = NULL; | |||||
free(_ota_url); | |||||
_ota_url = NULL; | |||||
}, 0); | |||||
_ota_client->onTimeout([](void *s, AsyncClient *c, uint32_t time) { | |||||
_ota_client->close(true); | |||||
}, 0); | |||||
_ota_client->onData([](void * arg, AsyncClient * c, void * data, size_t len) { | |||||
char * p = (char *) data; | |||||
if (_ota_size == 0) { | |||||
Update.runAsync(true); | |||||
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
} | |||||
p = strstr((char *)data, "\r\n\r\n") + 4; | |||||
len = len - (p - (char *) data); | |||||
} | |||||
if (!Update.hasError()) { | |||||
if (Update.write((uint8_t *) p, len) != len) { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
} | |||||
} | |||||
_ota_size += len; | |||||
DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size); | |||||
delay(0); | |||||
}, NULL); | |||||
_ota_client->onConnect([](void * arg, AsyncClient * client) { | |||||
#if ASYNC_TCP_SSL_ENABLED | |||||
if (443 == _ota_port) { | |||||
uint8_t fp[20] = {0}; | |||||
sslFingerPrintArray(getSetting("otafp", OTA_GITHUB_FP).c_str(), fp); | |||||
SSL * ssl = _ota_client->getSSL(); | |||||
if (ssl_match_fingerprint(ssl, fp) != SSL_OK) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Warning: certificate doesn't match\n")); | |||||
} | |||||
} | |||||
#endif | |||||
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade | |||||
eepromRotate(false); | |||||
DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url); | |||||
char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + strlen(_ota_url) + strlen(_ota_host)]; | |||||
snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url, _ota_host); | |||||
client->write(buffer); | |||||
}, NULL); | |||||
#if ASYNC_TCP_SSL_ENABLED | |||||
bool connected = _ota_client->connect(host, port, 443 == port); | |||||
#else | |||||
bool connected = _ota_client->connect(host, port); | |||||
#endif | |||||
if (!connected) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Connection failed\n")); | |||||
_ota_client->close(true); | |||||
} | |||||
} | |||||
void _otaFrom(String url) { | |||||
if (!url.startsWith("http://") && !url.startsWith("https://")) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n")); | |||||
return; | |||||
} | |||||
// Port from protocol | |||||
unsigned int port = 80; | |||||
if (url.startsWith("https://")) port = 443; | |||||
url = url.substring(url.indexOf("/") + 2); | |||||
// Get host | |||||
String host = url.substring(0, url.indexOf("/")); | |||||
// Explicit port | |||||
int p = host.indexOf(":"); | |||||
if (p > 0) { | |||||
port = host.substring(p + 1).toInt(); | |||||
host = host.substring(0, p); | |||||
} | |||||
// Get URL | |||||
String uri = url.substring(url.indexOf("/")); | |||||
_otaFrom(host.c_str(), port, uri.c_str()); | |||||
} | |||||
#endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT | |||||
#if TERMINAL_SUPPORT | |||||
void _otaInitCommands() { | |||||
terminalRegisterCommand(F("OTA"), [](Embedis* e) { | |||||
if (e->argc < 2) { | |||||
terminalError(F("Wrong arguments")); | |||||
} else { | |||||
terminalOK(); | |||||
String url = String(e->argv[1]); | |||||
_otaFrom(url); | |||||
} | |||||
}); | |||||
} | |||||
#endif // TERMINAL_SUPPORT | |||||
#if OTA_MQTT_SUPPORT | |||||
void _otaMQTTCallback(unsigned int type, const char * topic, const char * payload) { | |||||
if (type == MQTT_CONNECT_EVENT) { | |||||
mqttSubscribe(MQTT_TOPIC_OTA); | |||||
} | |||||
if (type == MQTT_MESSAGE_EVENT) { | |||||
// Match topic | |||||
String t = mqttMagnitude((char *) topic); | |||||
if (t.equals(MQTT_TOPIC_OTA)) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload); | |||||
_otaFrom(payload); | |||||
} | |||||
} | |||||
} | |||||
#endif // OTA_MQTT_SUPPORT | |||||
// ----------------------------------------------------------------------------- | |||||
void otaSetup() { | |||||
_otaConfigure(); | |||||
#if TERMINAL_SUPPORT | |||||
_otaInitCommands(); | |||||
#endif | |||||
#if OTA_MQTT_SUPPORT | |||||
mqttRegister(_otaMQTTCallback); | |||||
#endif | |||||
// Main callbacks | |||||
espurnaRegisterLoop(_otaLoop); | |||||
espurnaRegisterReload(_otaConfigure); | |||||
// ------------------------------------------------------------------------- | |||||
ArduinoOTA.onStart([]() { | |||||
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade | |||||
eepromRotate(false); | |||||
DEBUG_MSG_P(PSTR("[OTA] Start\n")); | |||||
#if WEB_SUPPORT | |||||
wsSend_P(PSTR("{\"message\": 2}")); | |||||
#endif | |||||
}); | |||||
ArduinoOTA.onEnd([]() { | |||||
DEBUG_MSG_P(PSTR("\n")); | |||||
DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n")); | |||||
#if WEB_SUPPORT | |||||
wsSend_P(PSTR("{\"action\": \"reload\"}")); | |||||
#endif | |||||
deferredReset(100, CUSTOM_RESET_OTA); | |||||
}); | |||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |||||
static unsigned int _progOld; | |||||
unsigned int _prog = (progress / (total / 100)); | |||||
if (_prog != _progOld) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%\r"), _prog); | |||||
_progOld = _prog; | |||||
} | |||||
}); | |||||
ArduinoOTA.onError([](ota_error_t error) { | |||||
#if DEBUG_SUPPORT | |||||
DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error); | |||||
if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n")); | |||||
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n")); | |||||
else if (error == OTA_CONNECT_ERROR) DEBUG_MSG_P(PSTR("Connect Failed\n")); | |||||
else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG_P(PSTR("Receive Failed\n")); | |||||
else if (error == OTA_END_ERROR) DEBUG_MSG_P(PSTR("End Failed\n")); | |||||
#endif | |||||
eepromRotate(true); | |||||
}); | |||||
ArduinoOTA.begin(); | |||||
} |
@ -0,0 +1,101 @@ | |||||
/* | |||||
ARDUINO OTA MODULE | |||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if OTA_ARDUINOOTA_SUPPORT | |||||
// TODO: allocate ArduinoOTAClass on-demand, stop using global instance | |||||
void _arduinoOtaConfigure() { | |||||
ArduinoOTA.setPort(OTA_PORT); | |||||
ArduinoOTA.setHostname(getSetting("hostname").c_str()); | |||||
#if USE_PASSWORD | |||||
ArduinoOTA.setPassword(getAdminPass().c_str()); | |||||
#endif | |||||
ArduinoOTA.begin(); | |||||
} | |||||
void _arduinoOtaLoop() { | |||||
ArduinoOTA.handle(); | |||||
} | |||||
void _arduinoOtaOnStart() { | |||||
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade | |||||
eepromRotate(false); | |||||
// Because ArduinoOTA is synchronous, force backup right now instead of waiting for the next loop() | |||||
eepromBackup(0); | |||||
DEBUG_MSG_P(PSTR("[OTA] Start\n")); | |||||
#if WEB_SUPPORT | |||||
wsSend_P(PSTR("{\"message\": 2}")); | |||||
#endif | |||||
} | |||||
void _arduinoOtaOnEnd() { | |||||
DEBUG_MSG_P(PSTR("\n")); | |||||
DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n")); | |||||
#if WEB_SUPPORT | |||||
wsSend_P(PSTR("{\"action\": \"reload\"}")); | |||||
#endif | |||||
deferredReset(100, CUSTOM_RESET_OTA); | |||||
} | |||||
void _arduinoOtaOnProgress(unsigned int progress, unsigned int total) { | |||||
// Removed to avoid websocket ping back during upgrade (see #1574) | |||||
// TODO: implement as separate from debugging message | |||||
#if WEB_SUPPORT | |||||
if (wsConnected()) return; | |||||
#endif | |||||
static unsigned int _progOld; | |||||
unsigned int _prog = (progress / (total / 100)); | |||||
if (_prog != _progOld) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%\r"), _prog); | |||||
_progOld = _prog; | |||||
} | |||||
} | |||||
void _arduinoOtaOnError(ota_error_t error) { | |||||
#if DEBUG_SUPPORT | |||||
DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error); | |||||
if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n")); | |||||
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n")); | |||||
else if (error == OTA_CONNECT_ERROR) DEBUG_MSG_P(PSTR("Connect Failed\n")); | |||||
else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG_P(PSTR("Receive Failed\n")); | |||||
else if (error == OTA_END_ERROR) DEBUG_MSG_P(PSTR("End Failed\n")); | |||||
#endif | |||||
eepromRotate(true); | |||||
} | |||||
void arduinoOtaSetup() { | |||||
espurnaRegisterLoop(_arduinoOtaLoop); | |||||
espurnaRegisterReload(_arduinoOtaConfigure); | |||||
ArduinoOTA.onStart(_arduinoOtaOnStart); | |||||
ArduinoOTA.onEnd(_arduinoOtaOnEnd); | |||||
ArduinoOTA.onError(_arduinoOtaOnError); | |||||
ArduinoOTA.onProgress(_arduinoOtaOnProgress); | |||||
_arduinoOtaConfigure(); | |||||
} | |||||
#endif // OTA_ARDUINOOTA_SUPPORT |
@ -0,0 +1,227 @@ | |||||
/* | |||||
ASYNC CLIENT OTA MODULE | |||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if OTA_CLIENT == OTA_CLIENT_ASYNCTCP | |||||
// ----------------------------------------------------------------------------- | |||||
// Terminal OTA command | |||||
// ----------------------------------------------------------------------------- | |||||
#if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT | |||||
#include <ESPAsyncTCP.h> | |||||
#include "libs/URL.h" | |||||
std::unique_ptr<AsyncClient> _ota_client = nullptr; | |||||
unsigned long _ota_size = 0; | |||||
bool _ota_connected = false; | |||||
std::unique_ptr<URL> _ota_url = nullptr; | |||||
const char OTA_REQUEST_TEMPLATE[] PROGMEM = | |||||
"GET %s HTTP/1.1\r\n" | |||||
"Host: %s\r\n" | |||||
"User-Agent: ESPurna\r\n" | |||||
"Connection: close\r\n" | |||||
"Content-Type: application/x-www-form-urlencoded\r\n" | |||||
"Content-Length: 0\r\n\r\n\r\n"; | |||||
void _otaClientOnDisconnect(void *s, AsyncClient *c) { | |||||
DEBUG_MSG_P(PSTR("\n")); | |||||
if (Update.end(true)){ | |||||
DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size); | |||||
deferredReset(100, CUSTOM_RESET_OTA); | |||||
} else { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
eepromRotate(true); | |||||
} | |||||
DEBUG_MSG_P(PSTR("[OTA] Disconnected\n")); | |||||
_ota_connected = false; | |||||
_ota_url = nullptr; | |||||
_ota_client = nullptr; | |||||
} | |||||
void _otaClientOnTimeout(void *s, AsyncClient *c, uint32_t time) { | |||||
_ota_connected = false; | |||||
_ota_url = nullptr; | |||||
_ota_client->close(true); | |||||
} | |||||
void _otaClientOnData(void * arg, AsyncClient * c, void * data, size_t len) { | |||||
char * p = (char *) data; | |||||
if (_ota_size == 0) { | |||||
Update.runAsync(true); | |||||
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
c->close(true); | |||||
return; | |||||
} | |||||
p = strstr((char *)data, "\r\n\r\n") + 4; | |||||
len = len - (p - (char *) data); | |||||
} | |||||
if (!Update.hasError()) { | |||||
if (Update.write((uint8_t *) p, len) != len) { | |||||
#ifdef DEBUG_PORT | |||||
Update.printError(DEBUG_PORT); | |||||
#endif | |||||
c->close(true); | |||||
return; | |||||
} | |||||
} | |||||
_ota_size += len; | |||||
DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size); | |||||
delay(0); | |||||
} | |||||
void _otaClientOnConnect(void *arg, AsyncClient *client) { | |||||
#if ASYNC_TCP_SSL_ENABLED | |||||
int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt(); | |||||
if ((check == SECURE_CLIENT_CHECK_FINGERPRINT) && (443 == _ota_url->port)) { | |||||
uint8_t fp[20] = {0}; | |||||
sslFingerPrintArray(getSetting("otafp", OTA_FINGERPRINT).c_str(), fp); | |||||
SSL * ssl = _ota_client->getSSL(); | |||||
if (ssl_match_fingerprint(ssl, fp) != SSL_OK) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Warning: certificate fingerpint doesn't match\n")); | |||||
client->close(true); | |||||
return; | |||||
} | |||||
} | |||||
#endif | |||||
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade | |||||
eepromRotate(false); | |||||
DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url->path.c_str()); | |||||
char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + _ota_url->path.length() + _ota_url->host.length()]; | |||||
snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url->path.c_str(), _ota_url->host.c_str()); | |||||
client->write(buffer); | |||||
} | |||||
void _otaClientFrom(const String& url) { | |||||
if (_ota_connected) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Already connected\n")); | |||||
return; | |||||
} | |||||
_ota_size = 0; | |||||
if (_ota_url) _ota_url = nullptr; | |||||
_ota_url = std::make_unique<URL>(url); | |||||
/* | |||||
DEBUG_MSG_P(PSTR("[OTA] proto:%s host:%s port:%u path:%s\n"), | |||||
_ota_url->protocol.c_str(), | |||||
_ota_url->host.c_str(), | |||||
_ota_url->port, | |||||
_ota_url->path.c_str() | |||||
); | |||||
*/ | |||||
// we only support HTTP | |||||
if ((!_ota_url->protocol.equals("http")) && (!_ota_url->protocol.equals("https"))) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n")); | |||||
_ota_url = nullptr; | |||||
return; | |||||
} | |||||
if (!_ota_client) { | |||||
_ota_client = std::make_unique<AsyncClient>(); | |||||
} | |||||
_ota_client->onDisconnect(_otaClientOnDisconnect, nullptr); | |||||
_ota_client->onTimeout(_otaClientOnTimeout, nullptr); | |||||
_ota_client->onData(_otaClientOnData, nullptr); | |||||
_ota_client->onConnect(_otaClientOnConnect, nullptr); | |||||
#if ASYNC_TCP_SSL_ENABLED | |||||
_ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port, 443 == _ota_url->port); | |||||
#else | |||||
_ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port); | |||||
#endif | |||||
if (!_ota_connected) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Connection failed\n")); | |||||
_ota_url = nullptr; | |||||
_ota_client->close(true); | |||||
} | |||||
} | |||||
#endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT | |||||
#if TERMINAL_SUPPORT | |||||
void _otaClientInitCommands() { | |||||
terminalRegisterCommand(F("OTA"), [](Embedis* e) { | |||||
if (e->argc < 2) { | |||||
terminalError(F("OTA <url>")); | |||||
} else { | |||||
_otaClientFrom(String(e->argv[1])); | |||||
terminalOK(); | |||||
} | |||||
}); | |||||
} | |||||
#endif // TERMINAL_SUPPORT | |||||
#if OTA_MQTT_SUPPORT | |||||
void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) { | |||||
if (type == MQTT_CONNECT_EVENT) { | |||||
mqttSubscribe(MQTT_TOPIC_OTA); | |||||
} | |||||
if (type == MQTT_MESSAGE_EVENT) { | |||||
String t = mqttMagnitude((char *) topic); | |||||
if (t.equals(MQTT_TOPIC_OTA)) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload); | |||||
_otaClientFrom(payload); | |||||
} | |||||
} | |||||
} | |||||
#endif // OTA_MQTT_SUPPORT | |||||
// ----------------------------------------------------------------------------- | |||||
void otaClientSetup() { | |||||
#if TERMINAL_SUPPORT | |||||
_otaClientInitCommands(); | |||||
#endif | |||||
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT) | |||||
mqttRegister(_otaClientMqttCallback); | |||||
#endif | |||||
} | |||||
#endif // OTA_CLIENT == OTA_CLIENT_ASYNCTCP |
@ -0,0 +1,264 @@ | |||||
/* | |||||
HTTP(s) OTA MODULE | |||||
Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com> | |||||
*/ | |||||
// ----------------------------------------------------------------------------- | |||||
// OTA by using Core's HTTP(s) updater | |||||
// ----------------------------------------------------------------------------- | |||||
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE | |||||
#include <memory> | |||||
#include <ESP8266HTTPClient.h> | |||||
#include <ESP8266httpUpdate.h> | |||||
#include "libs/URL.h" | |||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE | |||||
#if OTA_SECURE_CLIENT_INCLUDE_CA | |||||
#include "static/ota_client_trusted_root_ca.h" | |||||
#else | |||||
#include "static/digicert_evroot_pem.h" | |||||
#define _ota_client_trusted_root_ca _ssl_digicert_ev_root_ca | |||||
#endif | |||||
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE | |||||
void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& fp = "") { | |||||
UNUSED(client); | |||||
UNUSED(fp); | |||||
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade | |||||
eepromRotate(false); | |||||
DEBUG_MSG_P(PSTR("[OTA] Downloading %s ...\n"), url.c_str()); | |||||
// TODO: support currentVersion (string arg after 'url') | |||||
// NOTE: ESPhttpUpdate.update(..., fp) will **always** fail with empty fingerprint | |||||
// NOTE: It is possible to support BearSSL with 2.4.2 by using uint8_t[20] instead of String for fingerprint argument | |||||
ESPhttpUpdate.rebootOnUpdate(false); | |||||
t_httpUpdate_return result = HTTP_UPDATE_NO_UPDATES; | |||||
// We expect both .update(url, "", String_fp) and .update(url) to survice until axTLS is removed from the Core | |||||
#if (SECURE_CLIENT == SECURE_CLIENT_AXTLS) | |||||
if (url.startsWith("https://")) { | |||||
result = ESPhttpUpdate.update(url, "", fp); | |||||
} else { | |||||
result = ESPhttpUpdate.update(url); | |||||
} | |||||
#elif OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE | |||||
result = ESPhttpUpdate.update(url); | |||||
#else | |||||
result = ESPhttpUpdate.update(*client, url); | |||||
#endif | |||||
switch (result) { | |||||
case HTTP_UPDATE_FAILED: | |||||
DEBUG_MSG_P(PSTR("[OTA] Update failed (error %d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); | |||||
eepromRotate(true); | |||||
break; | |||||
case HTTP_UPDATE_NO_UPDATES: | |||||
DEBUG_MSG_P(PSTR("[OTA] No updates")); | |||||
eepromRotate(true); | |||||
break; | |||||
case HTTP_UPDATE_OK: | |||||
DEBUG_MSG_P(PSTR("[OTA] Done, restarting...")); | |||||
deferredReset(500, CUSTOM_RESET_OTA); // wait a bit more than usual | |||||
break; | |||||
} | |||||
} | |||||
#if OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE | |||||
void _otaClientFromHttp(const String& url) { | |||||
_otaClientRunUpdater(nullptr, url, ""); | |||||
} | |||||
#else | |||||
void _otaClientFromHttp(const String& url) { | |||||
auto client = std::make_unique<WiFiClient>(); | |||||
_otaClientRunUpdater(client.get(), url, ""); | |||||
} | |||||
#endif | |||||
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL | |||||
void _otaClientFromHttps(const String& url) { | |||||
int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt(); | |||||
bool settime = (check == SECURE_CLIENT_CHECK_CA); | |||||
if (!ntpSynced() && settime) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Time not synced!\n")); | |||||
return; | |||||
} | |||||
// unique_ptr self-destructs after exiting function scope | |||||
// create WiFiClient on heap to use less stack space | |||||
auto client = std::make_unique<BearSSL::WiFiClientSecure>(); | |||||
if (check == SECURE_CLIENT_CHECK_NONE) { | |||||
DEBUG_MSG_P(PSTR("[OTA] !!! Connection will not be validated !!!\n")); | |||||
client->setInsecure(); | |||||
} | |||||
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) { | |||||
String fp_string = getSetting("otafp", OTA_FINGERPRINT); | |||||
if (!fp_string.length()) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Requested fingerprint auth, but 'otafp' is not set\n")); | |||||
return; | |||||
} | |||||
uint8_t fp_bytes[20] = {0}; | |||||
sslFingerPrintArray(fp_string.c_str(), fp_bytes); | |||||
client->setFingerprint(fp_bytes); | |||||
} | |||||
BearSSL::X509List *ca = nullptr; | |||||
if (check == SECURE_CLIENT_CHECK_CA) { | |||||
ca = new BearSSL::X509List(_ota_client_trusted_root_ca); | |||||
// because we do not support libc methods of getting time, force client to use ntpclientlib's current time | |||||
// XXX: local2utc method use is detrimental when DST happening. now() should be utc | |||||
client->setX509Time(ntpLocal2UTC(now())); | |||||
client->setTrustAnchors(ca); | |||||
} | |||||
// TODO: RX and TX buffer sizes must be equal? | |||||
const uint16_t requested_mfln = getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN).toInt(); | |||||
switch (requested_mfln) { | |||||
// default, do nothing | |||||
case 0: | |||||
break; | |||||
// match valid sizes only | |||||
case 512: | |||||
case 1024: | |||||
case 2048: | |||||
case 4096: | |||||
{ | |||||
client->setBufferSizes(requested_mfln, requested_mfln); | |||||
break; | |||||
} | |||||
default: | |||||
DEBUG_MSG_P(PSTR("[OTA] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n")); | |||||
} | |||||
_otaClientRunUpdater(client.get(), url); | |||||
} | |||||
#endif // SECURE_CLIENT_BEARSSL | |||||
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS | |||||
void _otaClientFromHttps(const String& url) { | |||||
const int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt(); | |||||
String fp_string; | |||||
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) { | |||||
fp_string = getSetting("otafp", OTA_FINGERPRINT); | |||||
if (!fp_string.length() || !sslCheckFingerPrint(fp_string.c_str())) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Wrong fingerprint\n")); | |||||
return; | |||||
} | |||||
} | |||||
_otaClientRunUpdater(nullptr, url, fp_string); | |||||
} | |||||
#endif // SECURE_CLIENT_AXTLS | |||||
void _otaClientFrom(const String& url) { | |||||
if (url.startsWith("http://")) { | |||||
_otaClientFromHttp(url); | |||||
return; | |||||
} | |||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE | |||||
if (url.startsWith("https://")) { | |||||
_otaClientFromHttps(url); | |||||
return; | |||||
} | |||||
#endif | |||||
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n")); | |||||
} | |||||
#if TERMINAL_SUPPORT | |||||
void _otaClientInitCommands() { | |||||
terminalRegisterCommand(F("OTA"), [](Embedis* e) { | |||||
if (e->argc < 2) { | |||||
terminalError(F("OTA <url>")); | |||||
} else { | |||||
_otaClientFrom(String(e->argv[1])); | |||||
terminalOK(); | |||||
} | |||||
}); | |||||
} | |||||
#endif // TERMINAL_SUPPORT | |||||
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT) | |||||
bool _ota_do_update = false; | |||||
String _ota_url; | |||||
void _otaClientLoop() { | |||||
if (_ota_do_update) { | |||||
_otaClientFrom(_ota_url); | |||||
_ota_do_update = false; | |||||
_ota_url = ""; | |||||
} | |||||
} | |||||
void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) { | |||||
if (type == MQTT_CONNECT_EVENT) { | |||||
mqttSubscribe(MQTT_TOPIC_OTA); | |||||
} | |||||
if (type == MQTT_MESSAGE_EVENT) { | |||||
String t = mqttMagnitude((char *) topic); | |||||
if (t.equals(MQTT_TOPIC_OTA)) { | |||||
DEBUG_MSG_P(PSTR("[OTA] Queuing from URL: %s\n"), payload); | |||||
_ota_do_update = true; | |||||
_ota_url = payload; | |||||
} | |||||
} | |||||
} | |||||
#endif // MQTT_SUPPORT | |||||
// ----------------------------------------------------------------------------- | |||||
void otaClientSetup() { | |||||
#if TERMINAL_SUPPORT | |||||
_otaClientInitCommands(); | |||||
#endif | |||||
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT) | |||||
mqttRegister(_otaClientMqttCallback); | |||||
espurnaRegisterLoop(_otaClientLoop); | |||||
#endif | |||||
} | |||||
#endif // OTA_CLIENT == OTA_CLIENT_HTTPUPDATE |
@ -0,0 +1,249 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// ADE7853 Sensor over I2C | |||||
// Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// Implemented by Antonio López <tonilopezmr at gmail dot com> | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && ADE7953_SUPPORT | |||||
#pragma once | |||||
#undef I2C_SUPPORT | |||||
#define I2C_SUPPORT 1 // Explicitly request I2C support. | |||||
#include "Arduino.h" | |||||
#include "I2CSensor.h" | |||||
#include <Wire.h> | |||||
// ----------------------------------------------------------------------------- | |||||
// ADE7953 - Energy (Shelly 2.5) | |||||
// | |||||
// Based on datasheet from https://www.analog.com/en/products/ade7953.html | |||||
// Based on Tasmota code https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/xnrg_07_ade7953.ino | |||||
// | |||||
// I2C Address: 0x38 | |||||
// ----------------------------------------------------------------------------- | |||||
#define ADE7953_PREF 1540 | |||||
#define ADE7953_UREF 26000 | |||||
#define ADE7953_IREF 10000 | |||||
#define ADE7953_ALL_RELAYS 0 | |||||
#define ADE7953_RELAY_1 1 | |||||
#define ADE7953_RELAY_2 2 | |||||
#define ADE7953_VOLTAGE 1 | |||||
#define ADE7953_TOTAL_DEVICES 3 | |||||
class ADE7953Sensor : public I2CSensor { | |||||
protected: | |||||
struct reading_t { | |||||
float current = 0.0; | |||||
float power = 0.0; | |||||
float energy = 0.0; | |||||
}; | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
ADE7953Sensor(): I2CSensor() { | |||||
_sensor_id = SENSOR_ADE7953_ID; | |||||
_readings.resize(ADE7953_TOTAL_DEVICES); | |||||
_energy_offsets.resize(ADE7953_TOTAL_DEVICES); | |||||
_count = _readings.size() * ADE7953_TOTAL_DEVICES + ADE7953_VOLTAGE; //10 | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensors API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
if (!_dirty) return; | |||||
_init(); | |||||
_dirty = !_ready; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[25]; | |||||
snprintf(buffer, sizeof(buffer), "ADE7953 @ I2C (0x%02X)", _address); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_VOLTAGE; | |||||
index = index % ADE7953_TOTAL_DEVICES; | |||||
if (index == 0) return MAGNITUDE_ENERGY; | |||||
if (index == 1) return MAGNITUDE_CURRENT; | |||||
if (index == 2) return MAGNITUDE_POWER_ACTIVE; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Pre-read hook (usually to populate registers with up-to-date data) | |||||
void pre() { | |||||
uint32_t active_power1 = 0; | |||||
uint32_t active_power2 = 0; | |||||
uint32_t current_rms = 0; | |||||
uint32_t current_rms1 = 0; | |||||
uint32_t current_rms2 = 0; | |||||
uint32_t voltage_rms = 0; | |||||
voltage_rms = read(_address, 0x31C); // Both relays | |||||
current_rms1 = read(_address, 0x31B); // Relay 1 | |||||
if (current_rms1 < 2000) { // No load threshold (20mA) | |||||
current_rms1 = 0; | |||||
active_power1 = 0; | |||||
} else { | |||||
active_power1 = (int32_t)read(_address, 0x313) * -1; // Relay 1 | |||||
active_power1 = (active_power1 > 0) ? active_power1 : 0; | |||||
} | |||||
current_rms2 = read(_address, 0x31A); // Relay 2 | |||||
if (current_rms2 < 2000) { // No load threshold (20mA) | |||||
current_rms2 = 0; | |||||
active_power2 = 0; | |||||
} else { | |||||
active_power2 = (int32_t)read(_address, 0x312); // Relay 2 | |||||
active_power2 = (active_power2 > 0) ? active_power2 : 0; | |||||
} | |||||
_voltage = (float) voltage_rms / ADE7953_UREF; | |||||
storeReading( | |||||
ADE7953_ALL_RELAYS, | |||||
(float)(current_rms1 + current_rms2) / (ADE7953_IREF * 10), | |||||
(float)(active_power1 + active_power2) / (ADE7953_PREF / 10) | |||||
); | |||||
storeReading( | |||||
ADE7953_RELAY_1, | |||||
(float) current_rms1 / (ADE7953_IREF * 10), | |||||
(float) active_power1 / (ADE7953_PREF / 10) | |||||
); | |||||
storeReading( | |||||
ADE7953_RELAY_2, | |||||
(float)current_rms2 / (ADE7953_IREF * 10), | |||||
(float)active_power2 / (ADE7953_PREF / 10) | |||||
); | |||||
} | |||||
inline void storeReading(unsigned int relay, float current, float power) { | |||||
auto& reading_ref = _readings.at(relay); | |||||
reading_ref.current = current; | |||||
reading_ref.power = power; | |||||
static unsigned long last = 0; | |||||
if (last > 0) { | |||||
reading_ref.energy += (power * (millis() - last) / 1000); | |||||
} | |||||
last = millis(); | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) return _voltage; | |||||
int relay = (index - 1) / ADE7953_TOTAL_DEVICES; | |||||
index = index % ADE7953_TOTAL_DEVICES; | |||||
if (index == 0) return _energy_offsets[relay] + _readings[relay].energy; | |||||
if (index == 1) return _readings[relay].current; | |||||
if (index == 2) return _readings[relay].power; | |||||
return 0; | |||||
} | |||||
unsigned int getTotalDevices() { | |||||
return ADE7953_TOTAL_DEVICES; | |||||
} | |||||
void resetEnergy(int relay, double value = 0) { | |||||
_energy_offsets[relay] = value; | |||||
} | |||||
protected: | |||||
void _init() { | |||||
nice_delay(100); // Need 100mS to init ADE7953 | |||||
write(_address, 0x102, 0x0004); // Locking the communication interface (Clear bit COMM_LOCK), Enable HPF | |||||
write(_address, 0x0FE, 0x00AD); // Unlock register 0x120 | |||||
write(_address, 0x120, 0x0030); // Configure optimum setting | |||||
_ready = true; | |||||
} | |||||
#if 0 | |||||
static int reg_size(uint16_t reg) { | |||||
int size = 0; | |||||
switch ((reg >> 8) & 0x0F) { | |||||
case 0x03: | |||||
size++; | |||||
case 0x02: | |||||
size++; | |||||
case 0x01: | |||||
size++; | |||||
case 0x00: | |||||
case 0x07: | |||||
case 0x08: | |||||
size++; | |||||
} | |||||
return size; | |||||
} | |||||
#else | |||||
// Optimized version of the function above, -80 bytes of code | |||||
// Use the known property of register addresses to calculate their size | |||||
static const int reg_size(const uint16_t reg) { | |||||
const uint8_t mask = ((reg >> 8) & 0b1111); | |||||
if (!mask || (mask & 0b1100)) { | |||||
return 1; | |||||
} else if (mask & 0b0011) { | |||||
return mask + 1; | |||||
} | |||||
return 0; | |||||
} | |||||
#endif | |||||
void write(unsigned char address, uint16_t reg, uint32_t val) { | |||||
int size = reg_size(reg); | |||||
if (size) { | |||||
Wire.beginTransmission(address); | |||||
Wire.write((reg >> 8) & 0xFF); | |||||
Wire.write(reg & 0xFF); | |||||
while (size--) { | |||||
Wire.write((val >> (8 * size)) & 0xFF); // Write data, MSB first | |||||
} | |||||
Wire.endTransmission(); | |||||
delayMicroseconds(5); // Bus-free time minimum 4.7us | |||||
} | |||||
} | |||||
static uint32_t read(int address, uint16_t reg) { | |||||
uint32_t response = 0; | |||||
const int size = reg_size(reg); | |||||
if (size) { | |||||
Wire.beginTransmission(address); | |||||
Wire.write((reg >> 8) & 0xFF); | |||||
Wire.write(reg & 0xFF); | |||||
Wire.endTransmission(0); | |||||
Wire.requestFrom(address, size); | |||||
if (size <= Wire.available()) { | |||||
for (int i = 0; i < size; i++) { | |||||
response = response << 8 | Wire.read(); // receive DATA (MSB first) | |||||
} | |||||
} | |||||
} | |||||
return response; | |||||
} | |||||
std::vector<reading_t> _readings; | |||||
float _voltage = 0; | |||||
std::vector<double> _energy_offsets; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && ADE7953_SUPPORT |
@ -0,0 +1,42 @@ | |||||
// https://github.com root issuer | |||||
// Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA | |||||
// Validity | |||||
// Not Before: Oct 22 12:00:00 2013 GMT | |||||
// Not After : Oct 22 12:00:00 2028 GMT | |||||
// Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 Extended Validation Server CA | |||||
#pragma once | |||||
#include <pgmspace.h> | |||||
const char PROGMEM _ssl_digicert_ev_root_ca[] = R"EOF( | |||||
-----BEGIN CERTIFICATE----- | |||||
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs | |||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 | |||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j | |||||
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL | |||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 | |||||
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW | |||||
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC | |||||
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY | |||||
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/ | |||||
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy | |||||
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh | |||||
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k | |||||
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB | |||||
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF | |||||
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp | |||||
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy | |||||
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2 | |||||
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j | |||||
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW | |||||
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh | |||||
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg | |||||
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa | |||||
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs | |||||
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1 | |||||
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn | |||||
8TUoE6smftX3eg== | |||||
-----END CERTIFICATE----- | |||||
)EOF"; |
@ -0,0 +1,94 @@ | |||||
// ISRG Root X1 (self-signed) | |||||
// from https://letsencrypt.org/certs/isrgrootx1.pem.txt | |||||
// Note: LetsEncrypt will only start using this root certificate to sign | |||||
// certificates after July 8, 2020. Any certificate issued before this date | |||||
// uses the X3 intermediate certificate down below. | |||||
// See: https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html | |||||
// Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1 | |||||
// Validity | |||||
// Not Before: Jun 4 11:04:38 2015 GMT | |||||
// Not After : Jun 4 11:04:38 2035 GMT | |||||
// Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1 | |||||
#pragma once | |||||
#include <pgmspace.h> | |||||
const char PROGMEM _ssl_letsencrypt_isrg_x1_ca[] = R"EOF( | |||||
-----BEGIN CERTIFICATE----- | |||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw | |||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh | |||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 | |||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu | |||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY | |||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc | |||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ | |||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U | |||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW | |||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH | |||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC | |||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv | |||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn | |||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn | |||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw | |||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI | |||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV | |||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq | |||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL | |||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ | |||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK | |||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 | |||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur | |||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC | |||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc | |||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq | |||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA | |||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d | |||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= | |||||
-----END CERTIFICATE----- | |||||
)EOF"; | |||||
// Let’s Encrypt Authority X3 (Signed by ISRG Root X1) | |||||
// from https://letsencrypt.org/certs/letsencryptauthorityx3.pem.txt | |||||
// Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 | |||||
// Validity | |||||
// Not Before: Oct 6 15:43:55 2016 GMT | |||||
// Not After : Oct 6 15:43:55 2021 GMT | |||||
const char PROGMEM _ssl_letsencrypt_isrg_x3_ca[] = R"EOF( | |||||
-----BEGIN CERTIFICATE----- | |||||
MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw | |||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh | |||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1 | |||||
WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg | |||||
RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi | |||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX | |||||
NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf | |||||
89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl | |||||
Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc | |||||
Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz | |||||
uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB | |||||
AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU | |||||
BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB | |||||
FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo | |||||
SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js | |||||
LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF | |||||
BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG | |||||
AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD | |||||
VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB | |||||
ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx | |||||
A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM | |||||
UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2 | |||||
DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1 | |||||
eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu | |||||
OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw | |||||
p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY | |||||
2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0 | |||||
ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR | |||||
PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b | |||||
rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt | |||||
-----END CERTIFICATE----- | |||||
)EOF"; |
@ -0,0 +1,90 @@ | |||||
from __future__ import print_function | |||||
Import("env") | |||||
import os | |||||
import sys | |||||
TRAVIS = os.environ.get("TRAVIS") | |||||
PIO_PLATFORM = env.PioPlatform() | |||||
CONFIG = env.GetProjectConfig() | |||||
class ExtraScriptError(Exception): | |||||
pass | |||||
# Most portable way, without depending on platformio internals | |||||
def subprocess_libdeps(lib_deps, storage=None, silent=True): | |||||
import subprocess | |||||
args = [env.subst("$PYTHONEXE"), "-mplatformio", "lib"] | |||||
if not storage: | |||||
args.append("-g") | |||||
else: | |||||
args.extend(["-d", storage]) | |||||
args.append("install") | |||||
if silent: | |||||
args.append("-s") | |||||
args.extend(lib_deps) | |||||
subprocess.check_call(args) | |||||
# Avoid spawning pio lib every time, hook into the LibraryManager API (sort-of internal) | |||||
def library_manager_libdeps(lib_deps, storage=None): | |||||
from platformio.managers.lib import LibraryManager | |||||
from platformio.project.helpers import get_project_global_lib_dir | |||||
if not storage: | |||||
manager = LibraryManager(get_project_global_lib_dir()) | |||||
else: | |||||
manager = LibraryManager(storage) | |||||
for lib in lib_deps: | |||||
if manager.get_package_dir(*manager.parse_pkg_uri(lib)): | |||||
continue | |||||
print("installing: {}".format(lib), file=sys.stderr) | |||||
manager.install(lib) | |||||
def get_shared_libdeps_dir(section, name): | |||||
if not CONFIG.has_option(section, name): | |||||
raise ExtraScriptError("{}.{} is required to be set".format(section, name)) | |||||
opt = CONFIG.get(section, name) | |||||
if not opt in env.GetProjectOption("lib_extra_dirs"): | |||||
raise ExtraScriptError( | |||||
"lib_extra_dirs must contain {}.{}".format(section, name) | |||||
) | |||||
return os.path.join(env["PROJECT_DIR"], opt) | |||||
def ensure_platform_updated(): | |||||
try: | |||||
if PIO_PLATFORM.are_outdated_packages(): | |||||
print("updating platform packages", file=sys.stderr) | |||||
PIO_PLATFORM.update_packages() | |||||
except Exception: | |||||
print("Warning: no connection, cannot check for outdated packages", file=sys.stderr) | |||||
# latest toolchain is still optional with PIO (TODO: recheck after 2.6.0!) | |||||
# also updates arduino core git to the latest master commit | |||||
if TRAVIS and (env.GetProjectOption("platform") == CONFIG.get("common", "arduino_core_git")): | |||||
ensure_platform_updated() | |||||
# to speed-up build process, install libraries in either global or local shared storage | |||||
if os.environ.get("ESPURNA_PIO_SHARED_LIBRARIES"): | |||||
if TRAVIS: | |||||
storage = None | |||||
print("using global library storage", file=sys.stderr) | |||||
else: | |||||
storage = get_shared_libdeps_dir("common", "shared_libdeps_dir") | |||||
print("using shared library storage: ", storage, file=sys.stderr) | |||||
subprocess_libdeps(env.GetProjectOption("lib_deps"), storage) |
@ -1,160 +0,0 @@ | |||||
/** | |||||
* jQuery Wheel Color Picker | |||||
* Base Stylesheet | |||||
* | |||||
* http://www.jar2.net/projects/jquery-wheelcolorpicker | |||||
* | |||||
* Copyright © 2011-2016 Fajar Chandra. All rights reserved. | |||||
* Released under MIT License. | |||||
* http://www.opensource.org/licenses/mit-license.php | |||||
* | |||||
* Note: Width, height, left, and top properties are handled by the | |||||
* plugin. These values might change on the fly. | |||||
*/ | |||||
.jQWCP-wWidget { | |||||
position: absolute; | |||||
width: 250px; | |||||
height: 180px; | |||||
background: #eee; | |||||
box-shadow: 1px 1px 4px rgba(0,0,0,.5); | |||||
border-radius: 4px; | |||||
border: solid 1px #aaa; | |||||
padding: 10px; | |||||
z-index: 1001; | |||||
} | |||||
.jQWCP-wWidget.jQWCP-block { | |||||
position: relative; | |||||
border-color: #aaa; | |||||
box-shadow: inset 1px 1px 1px #ccc; | |||||
} | |||||
.jQWCP-wWheel { | |||||
background-repeat: no-repeat; | |||||
background-position: center; | |||||
background-size: contain; | |||||
position: relative; | |||||
float: left; | |||||
width: 180px; | |||||
height: 180px; | |||||
-webkit-border-radius: 90px; | |||||
-moz-border-radius: 50%; | |||||
border-radius: 50%; | |||||
border: solid 1px #aaa; | |||||
margin: -1px; | |||||
margin-right: 10px; | |||||
transition: border .15s; | |||||
cursor: crosshair; | |||||
} | |||||
.jQWCP-wWheel:hover { | |||||
border-color: #666; | |||||
} | |||||
.jQWCP-wWheelOverlay { | |||||
position: absolute; | |||||
top: 0; | |||||
left: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
background: #000; | |||||
opacity: 0; | |||||
-webkit-border-radius: 90px; | |||||
-moz-border-radius: 50%; | |||||
border-radius: 50%; | |||||
} | |||||
.jQWCP-wWheelCursor { | |||||
width: 8px; | |||||
height: 8px; | |||||
position: absolute; | |||||
top: 50%; | |||||
left: 50%; | |||||
margin: -6px -6px; | |||||
cursor: crosshair; | |||||
border: solid 2px #fff; | |||||
box-shadow: 1px 1px 2px #000; | |||||
border-radius: 50%; | |||||
} | |||||
.jQWCP-slider-wrapper, | |||||
.jQWCP-wPreview { | |||||
position: relative; | |||||
width: 20px; | |||||
height: 180px; | |||||
float: left; | |||||
margin-right: 10px; | |||||
} | |||||
.jQWCP-wWheel:last-child, | |||||
.jQWCP-slider-wrapper:last-child, | |||||
.jQWCP-wPreview:last-child { | |||||
margin-right: 0; | |||||
} | |||||
.jQWCP-slider, | |||||
.jQWCP-wPreviewBox { | |||||
position: absolute; | |||||
width: 100%; | |||||
height: 100%; | |||||
left: 0; | |||||
top: 0; | |||||
box-sizing: border-box; | |||||
border: solid 1px #aaa; | |||||
margin: -1px; | |||||
-moz-border-radius: 4px; | |||||
border-radius: 4px; | |||||
transition: border .15s; | |||||
} | |||||
.jQWCP-slider { | |||||
cursor: crosshair; | |||||
} | |||||
.jQWCP-slider-wrapper:hover .jQWCP-slider { | |||||
border-color: #666; | |||||
} | |||||
.jQWCP-scursor { | |||||
position: absolute; | |||||
left: 0; | |||||
top: 0; | |||||
right: 0; | |||||
height: 6px; | |||||
margin: -5px -1px -5px -3px; | |||||
cursor: crosshair; | |||||
border: solid 2px #fff; | |||||
box-shadow: 1px 1px 2px #000; | |||||
border-radius: 4px; | |||||
} | |||||
.jQWCP-wAlphaSlider, | |||||
.jQWCP-wPreviewBox { | |||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVAQEB/f39eaJUuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYRBDgK9dKdMgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAARSURBVAjXY/jPwIAVYRf9DwB+vw/x6vMT1wAAAABJRU5ErkJggg==') center center; | |||||
} | |||||
.jQWCP-overlay { | |||||
position: fixed; | |||||
top: 0; | |||||
left: 0; | |||||
bottom: 0; | |||||
right: 0; | |||||
z-index: 1000; | |||||
} | |||||
/*********************/ | |||||
/* Mobile layout */ | |||||
.jQWCP-mobile.jQWCP-wWidget { | |||||
position: fixed; | |||||
bottom: 0; | |||||
left: 0 !important; | |||||
top: auto !important; | |||||
width: 100%; | |||||
height: 75%; | |||||
max-height: 240px; | |||||
box-sizing: border-box; | |||||
border-radius: 0; | |||||
} |
@ -0,0 +1,161 @@ | |||||
/** | |||||
* Wheel Color Picker for jQuery | |||||
* Base Stylesheet | |||||
* | |||||
* https://raffer.one/projects/jquery-wheelcolorpicker | |||||
* | |||||
* Copyright © 2011-2019 Fajar Chandra. All rights reserved. | |||||
* Released under MIT License. | |||||
* http://www.opensource.org/licenses/mit-license.php | |||||
* | |||||
* Note: Width, height, left, and top properties are handled by the | |||||
* plugin. These values might change on the fly. | |||||
*/ | |||||
.jQWCP-wWidget { | |||||
position: absolute; | |||||
width: 250px; | |||||
height: 180px; | |||||
background: #eee; | |||||
box-shadow: 1px 1px 4px rgba(0,0,0,.5); | |||||
border-radius: 4px; | |||||
border: solid 1px #aaa; | |||||
padding: 10px; | |||||
z-index: 1001; | |||||
touch-action: none; | |||||
} | |||||
.jQWCP-wWidget.jQWCP-block { | |||||
position: relative; | |||||
border-color: #aaa; | |||||
box-shadow: inset 1px 1px 1px #ccc; | |||||
} | |||||
.jQWCP-wWheel { | |||||
background-repeat: no-repeat; | |||||
background-position: center; | |||||
background-size: contain; | |||||
position: relative; | |||||
float: left; | |||||
width: 180px; | |||||
height: 180px; | |||||
-webkit-border-radius: 90px; | |||||
-moz-border-radius: 50%; | |||||
border-radius: 50%; | |||||
border: solid 1px #aaa; | |||||
margin: -1px; | |||||
margin-right: 10px; | |||||
transition: border .15s; | |||||
cursor: crosshair; | |||||
} | |||||
.jQWCP-wWheel:hover { | |||||
border-color: #666; | |||||
} | |||||
.jQWCP-wWheelOverlay { | |||||
position: absolute; | |||||
top: 0; | |||||
left: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
background: #000; | |||||
opacity: 0; | |||||
-webkit-border-radius: 90px; | |||||
-moz-border-radius: 50%; | |||||
border-radius: 50%; | |||||
} | |||||
.jQWCP-wWheelCursor { | |||||
width: 8px; | |||||
height: 8px; | |||||
position: absolute; | |||||
top: 50%; | |||||
left: 50%; | |||||
margin: -6px -6px; | |||||
cursor: crosshair; | |||||
border: solid 2px #fff; | |||||
box-shadow: 1px 1px 2px #000; | |||||
border-radius: 50%; | |||||
} | |||||
.jQWCP-slider-wrapper, | |||||
.jQWCP-wPreview { | |||||
position: relative; | |||||
width: 20px; | |||||
height: 180px; | |||||
float: left; | |||||
margin-right: 10px; | |||||
} | |||||
.jQWCP-wWheel:last-child, | |||||
.jQWCP-slider-wrapper:last-child, | |||||
.jQWCP-wPreview:last-child { | |||||
margin-right: 0; | |||||
} | |||||
.jQWCP-slider, | |||||
.jQWCP-wPreviewBox { | |||||
position: absolute; | |||||
width: 100%; | |||||
height: 100%; | |||||
left: 0; | |||||
top: 0; | |||||
box-sizing: border-box; | |||||
border: solid 1px #aaa; | |||||
margin: -1px; | |||||
-moz-border-radius: 4px; | |||||
border-radius: 4px; | |||||
transition: border .15s; | |||||
} | |||||
.jQWCP-slider { | |||||
cursor: crosshair; | |||||
} | |||||
.jQWCP-slider-wrapper:hover .jQWCP-slider { | |||||
border-color: #666; | |||||
} | |||||
.jQWCP-scursor { | |||||
position: absolute; | |||||
left: 0; | |||||
top: 0; | |||||
right: 0; | |||||
height: 6px; | |||||
margin: -5px -1px -5px -3px; | |||||
cursor: crosshair; | |||||
border: solid 2px #fff; | |||||
box-shadow: 1px 1px 2px #000; | |||||
border-radius: 4px; | |||||
} | |||||
.jQWCP-wAlphaSlider, | |||||
.jQWCP-wPreviewBox { | |||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVAQEB/f39eaJUuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYRBDgK9dKdMgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAARSURBVAjXY/jPwIAVYRf9DwB+vw/x6vMT1wAAAABJRU5ErkJggg==') center center; | |||||
} | |||||
.jQWCP-overlay { | |||||
position: fixed; | |||||
top: 0; | |||||
left: 0; | |||||
bottom: 0; | |||||
right: 0; | |||||
z-index: 1000; | |||||
} | |||||
/*********************/ | |||||
/* Mobile layout */ | |||||
.jQWCP-mobile.jQWCP-wWidget { | |||||
position: fixed; | |||||
bottom: 0; | |||||
left: 0 !important; | |||||
top: auto !important; | |||||
width: 100%; | |||||
height: 75%; | |||||
max-height: 240px; | |||||
box-sizing: border-box; | |||||
border-radius: 0; | |||||
} |
@ -0,0 +1 @@ | |||||
Shared lib_deps storage, see code/extra_script_libdeps.py |
@ -0,0 +1,39 @@ | |||||
#version=2.3.0 | |||||
menu.float_support=scanf and printf float support | |||||
generic.menu.FlashSize.1M1S=1M (1 EEPROM Sector, no SPIFFS) | |||||
generic.menu.FlashSize.1M1S.build.flash_size=1M | |||||
generic.menu.FlashSize.1M1S.build.flash_size_bytes=0x100000 | |||||
generic.menu.FlashSize.1M1S.build.flash_ld=eagle.flash.1m0m1s.ld | |||||
generic.menu.FlashSize.1M1S.build.spiffs_pagesize=256 | |||||
generic.menu.FlashSize.1M1S.upload.maximum_size=1023984 | |||||
generic.menu.FlashSize.1M1S.build.rfcal_addr=0xFC000 | |||||
generic.menu.FlashSize.2M4S=2M (4 EEPROM Sectors, 1M SPIFFS) | |||||
generic.menu.FlashSize.2M4S.build.flash_size=2M | |||||
generic.menu.FlashSize.2M4S.build.flash_size_bytes=0x200000 | |||||
generic.menu.FlashSize.2M4S.build.flash_ld=eagle.flash.2m1m4s.ld | |||||
generic.menu.FlashSize.2M4S.build.spiffs_pagesize=256 | |||||
generic.menu.FlashSize.2M4S.upload.maximum_size=1044464 | |||||
generic.menu.FlashSize.2M4S.build.rfcal_addr=0x1FC000 | |||||
generic.menu.FlashSize.4M1M4S=4M (4 EEPROM Sectors, 1M SPIFFS) | |||||
generic.menu.FlashSize.4M1M4S.build.flash_size=4M | |||||
generic.menu.FlashSize.4M1M4S.build.flash_size_bytes=0x400000 | |||||
generic.menu.FlashSize.4M1M4S.build.flash_ld=eagle.flash.4m1m4s.ld | |||||
generic.menu.FlashSize.4M1M4S.build.spiffs_pagesize=256 | |||||
generic.menu.FlashSize.4M1M4S.upload.maximum_size=1044464 | |||||
generic.menu.FlashSize.4M1M4S.build.rfcal_addr=0x3FC000 | |||||
generic.menu.FlashSize.4M3M4S=4M (4 EEPROM Sectors, 3M SPIFFS) | |||||
generic.menu.FlashSize.4M3M4S.build.flash_size=4M | |||||
generic.menu.FlashSize.4M3M4S.build.flash_size_bytes=0x400000 | |||||
generic.menu.FlashSize.4M3M4S.build.flash_ld=eagle.flash.4m3m4s.ld | |||||
generic.menu.FlashSize.4M3M4S.build.spiffs_pagesize=256 | |||||
generic.menu.FlashSize.4M3M4S.upload.maximum_size=1044464 | |||||
generic.menu.FlashSize.4M3M4S.build.rfcal_addr=0x3FC000 | |||||
generic.menu.float_support.disabled=Disabled (Recommended) | |||||
generic.menu.float_support.disabled.build.float= | |||||
generic.menu.float_support.enabled=Enabled | |||||
generic.menu.float_support.enabled.build.float=-u _printf_float -u _scanf_float | |||||
generic.compiler.cpp.extra_flags=-DNO_GLOBAL_EEPROM -DMQTT_MAX_PACKET_SIZE=1024 |
@ -0,0 +1,24 @@ | |||||
# boards.local.txt for ESPurna | |||||
Additional flash layouts to support multiple EEPROM sectors | |||||
### Installation | |||||
Place boards.local.txt into Arduino hardware directory, in the same directory as the boards.txt file. Depending on platform and ESP8266 installation method, it is one of: | |||||
- Linux (boards manager): `~/.arduino15/packages/esp8266/hardware/esp8266/<version>` | |||||
- Linux (git): `~/Arduino/hardware/esp8266com/esp8266/` | |||||
- Windows (boards manager): `%LOCALAPPDATA%\Arduino15\packages\esp8266\hardware\esp8266\<version>` | |||||
- Windows (git): `~\Documents\Arduino\hardware\esp8266com\esp8266\` | |||||
- macOS (boards manager): `~/Library/Arduino15/packages/esp2866/hardware/esp8266/<version>` | |||||
- macOS (git): `<application-directory>/Arduino.app/Contents/Java/hardware/esp8266com/esp8266` | |||||
Use `2.3.0/boards.local.txt` for Core version 2.3.0 | |||||
Use `latest/boards.local.txt` for all the others | |||||
### Arduino documentation | |||||
https://arduino-esp8266.readthedocs.io/en/latest/installing.html | |||||
https://www.arduino.cc/en/Hacking/Preferences | |||||
https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification#boardslocaltxt |
@ -0,0 +1,39 @@ | |||||
#version=latest | |||||
menu.float_support=scanf and printf float support | |||||
generic.menu.eesz.1M1S=1M (1 EEPROM Sector, no SPIFFS) | |||||
generic.menu.eesz.1M1S.build.flash_size=1M | |||||
generic.menu.eesz.1M1S.build.flash_size_bytes=0x100000 | |||||
generic.menu.eesz.1M1S.build.flash_ld=eagle.flash.1m0m1s.ld | |||||
generic.menu.eesz.1M1S.build.spiffs_pagesize=256 | |||||
generic.menu.eesz.1M1S.upload.maximum_size=1023984 | |||||
generic.menu.eesz.1M1S.build.rfcal_addr=0xFC000 | |||||
generic.menu.eesz.2M4S=2M (4 EEPROM Sectors, 1M SPIFFS) | |||||
generic.menu.eesz.2M4S.build.flash_size=2M | |||||
generic.menu.eesz.2M4S.build.flash_size_bytes=0x200000 | |||||
generic.menu.eesz.2M4S.build.flash_ld=eagle.flash.2m1m4s.ld | |||||
generic.menu.eesz.2M4S.build.spiffs_pagesize=256 | |||||
generic.menu.eesz.2M4S.upload.maximum_size=1044464 | |||||
generic.menu.eesz.2M4S.build.rfcal_addr=0x1FC000 | |||||
generic.menu.eesz.4M1M4S=4M (4 EEPROM Sectors, 1M SPIFFS) | |||||
generic.menu.eesz.4M1M4S.build.flash_size=4M | |||||
generic.menu.eesz.4M1M4S.build.flash_size_bytes=0x400000 | |||||
generic.menu.eesz.4M1M4S.build.flash_ld=eagle.flash.4m1m4s.ld | |||||
generic.menu.eesz.4M1M4S.build.spiffs_pagesize=256 | |||||
generic.menu.eesz.4M1M4S.upload.maximum_size=1044464 | |||||
generic.menu.eesz.4M1M4S.build.rfcal_addr=0x3FC000 | |||||
generic.menu.eesz.4M3M4S=4M (4 EEPROM Sectors, 3M SPIFFS) | |||||
generic.menu.eesz.4M3M4S.build.flash_size=4M | |||||
generic.menu.eesz.4M3M4S.build.flash_size_bytes=0x400000 | |||||
generic.menu.eesz.4M3M4S.build.flash_ld=eagle.flash.4m3m4s.ld | |||||
generic.menu.eesz.4M3M4S.build.spiffs_pagesize=256 | |||||
generic.menu.eesz.4M3M4S.upload.maximum_size=1044464 | |||||
generic.menu.eesz.4M3M4S.build.rfcal_addr=0x3FC000 | |||||
generic.menu.float_support.disabled=Disabled (Recommended) | |||||
generic.menu.float_support.disabled.build.float= | |||||
generic.menu.float_support.enabled=Enabled | |||||
generic.menu.float_support.enabled.build.float=-u _printf_float -u _scanf_float | |||||
generic.compiler.cpp.extra_flags=-DNO_GLOBAL_EEPROM -DMQTT_MAX_PACKET_SIZE=1024 |
@ -0,0 +1,187 @@ | |||||
#!/usr/bin/env python | |||||
# adapted boards.txt.py from esp8266/Arduino | |||||
# - single board definition, ldscripts | |||||
# - portable boards.local.txt for 2.3.0 and up | |||||
import os | |||||
import argparse | |||||
import sys | |||||
import collections | |||||
# TODO: drop after platform.io supports python 3 | |||||
if sys.version < (3, 2): | |||||
import string | |||||
_format = string.Formatter().vformat | |||||
def format_map(tmpl, f_map): | |||||
return _format(tmpl, None, f_map) | |||||
else: | |||||
def format_map(tmpl, f_map): | |||||
return tmpl.format_map(f_map) | |||||
class VersionedSubstitution(collections.MutableMapping): | |||||
def __init__(self, substitutions, targets, *args, **kwargs): | |||||
self._targets = targets | |||||
self._version = None | |||||
self._store = substitutions.copy() | |||||
self.update(dict(*args, **kwargs)) | |||||
def set_version(self, version): | |||||
self._version = version | |||||
def __getitem__(self, key): | |||||
if self._version in self._targets: | |||||
return self._store[key] | |||||
return key | |||||
def __setitem__(self, key, value): | |||||
self._store[key] = value | |||||
def __delitem__(self, key): | |||||
del self._store[key] | |||||
def __iter__(self): | |||||
return iter(self._store) | |||||
def __len__(self): | |||||
return len(self._store) | |||||
BOARDS_LOCAL = { | |||||
"global": collections.OrderedDict( | |||||
[("menu.float_support", "scanf and printf float support")] | |||||
), | |||||
"flash_size": collections.OrderedDict( | |||||
[ | |||||
(".menu.{eesz}.1M1S", "1M (1 EEPROM Sector, no SPIFFS)"), | |||||
(".menu.{eesz}.1M1S.build.flash_size", "1M"), | |||||
(".menu.{eesz}.1M1S.build.flash_size_bytes", "0x100000"), | |||||
(".menu.{eesz}.1M1S.build.flash_ld", "eagle.flash.1m0m1s.ld"), | |||||
(".menu.{eesz}.1M1S.build.spiffs_pagesize", "256"), | |||||
(".menu.{eesz}.1M1S.upload.maximum_size", "1023984"), | |||||
(".menu.{eesz}.1M1S.build.rfcal_addr", "0xFC000"), | |||||
(".menu.{eesz}.2M4S", "2M (4 EEPROM Sectors, 1M SPIFFS)"), | |||||
(".menu.{eesz}.2M4S.build.flash_size", "2M"), | |||||
(".menu.{eesz}.2M4S.build.flash_size_bytes", "0x200000"), | |||||
(".menu.{eesz}.2M4S.build.flash_ld", "eagle.flash.2m1m4s.ld"), | |||||
(".menu.{eesz}.2M4S.build.spiffs_pagesize", "256"), | |||||
(".menu.{eesz}.2M4S.upload.maximum_size", "1044464"), | |||||
(".menu.{eesz}.2M4S.build.rfcal_addr", "0x1FC000"), | |||||
(".menu.{eesz}.4M1M4S", "4M (4 EEPROM Sectors, 1M SPIFFS)"), | |||||
(".menu.{eesz}.4M1M4S.build.flash_size", "4M"), | |||||
(".menu.{eesz}.4M1M4S.build.flash_size_bytes", "0x400000"), | |||||
(".menu.{eesz}.4M1M4S.build.flash_ld", "eagle.flash.4m1m4s.ld"), | |||||
(".menu.{eesz}.4M1M4S.build.spiffs_pagesize", "256"), | |||||
(".menu.{eesz}.4M1M4S.upload.maximum_size", "1044464"), | |||||
(".menu.{eesz}.4M1M4S.build.rfcal_addr", "0x3FC000"), | |||||
(".menu.{eesz}.4M3M4S", "4M (4 EEPROM Sectors, 3M SPIFFS)"), | |||||
(".menu.{eesz}.4M3M4S.build.flash_size", "4M"), | |||||
(".menu.{eesz}.4M3M4S.build.flash_size_bytes", "0x400000"), | |||||
(".menu.{eesz}.4M3M4S.build.flash_ld", "eagle.flash.4m3m4s.ld"), | |||||
(".menu.{eesz}.4M3M4S.build.spiffs_pagesize", "256"), | |||||
(".menu.{eesz}.4M3M4S.upload.maximum_size", "1044464"), | |||||
(".menu.{eesz}.4M3M4S.build.rfcal_addr", "0x3FC000"), | |||||
] | |||||
), | |||||
"float_support": collections.OrderedDict( | |||||
[ | |||||
(".menu.float_support.disabled", "Disabled (Recommended)"), | |||||
(".menu.float_support.disabled.build.float", ""), | |||||
(".menu.float_support.enabled", "Enabled"), | |||||
( | |||||
".menu.float_support.enabled.build.float", | |||||
"-u _printf_float -u _scanf_float", | |||||
), | |||||
] | |||||
), | |||||
} | |||||
BOARD = "generic" | |||||
MENUS = ["flash_size", "float_support"] | |||||
CORE_VERSIONS = ["2.3.0", "latest"] | |||||
EXTRA_FLAGS = [ | |||||
(".compiler.cpp.extra_flags", "-DNO_GLOBAL_EEPROM -DMQTT_MAX_PACKET_SIZE=1024") | |||||
] | |||||
SUBSTITUTIONS = VersionedSubstitution( | |||||
dict(eesz="FlashSize", wipe="FlashErase", baud="UploadSpeed", vt="VTable"), | |||||
["2.3.0"], | |||||
) | |||||
def generate_boards_txt(args, sub=SUBSTITUTIONS): | |||||
versions = args.versions | |||||
if args.version: | |||||
versions = [args.version] | |||||
for version in versions: | |||||
sub.set_version(version) | |||||
result = ["#version={}\n\n".format(version)] | |||||
result.extend("{}={}\n".format(k, v) for k, v in BOARDS_LOCAL["global"].items()) | |||||
result.append("\n") | |||||
# print("{} unused:".format(version, set(BOARDS_LOCAL.keys()) - set(MENUS[version]))) | |||||
# continue | |||||
for menu in MENUS: | |||||
section = [] | |||||
for k, v in BOARDS_LOCAL[menu].items(): | |||||
k = format_map(k, sub) | |||||
section.append(BOARD + "=".join([k, v])) | |||||
result.append("\n".join(section)) | |||||
result.append("\n\n") | |||||
if EXTRA_FLAGS: | |||||
result.extend(("{}{}={}".format(BOARD, k, v)) for k, v in EXTRA_FLAGS) | |||||
f_path = os.path.join(args.directory, version, "boards.local.txt") | |||||
f_dir, _ = os.path.split(f_path) | |||||
if not os.path.exists(f_dir): | |||||
os.makedirs(f_dir) | |||||
with open(f_path, "w") as f: | |||||
for part in result: | |||||
f.write(part) | |||||
f.write("\n") | |||||
def print_versions(args): | |||||
for version in CORE_VERSIONS: | |||||
print("- {}".format(version)) | |||||
if __name__ == "__main__": | |||||
parser = argparse.ArgumentParser() | |||||
parser.add_argument( | |||||
"-d", | |||||
"--directory", | |||||
default=os.path.join( | |||||
os.path.dirname(os.path.realpath(__file__)), "arduino_ide" | |||||
), | |||||
) | |||||
subparsers = parser.add_subparsers(title="commands") | |||||
parser_versions = subparsers.add_parser("versions", help="list supported versions") | |||||
parser_versions.set_defaults(command=print_versions) | |||||
parser_generate = subparsers.add_parser("generate", help="") | |||||
parser_generate.add_argument("version", nargs="?") | |||||
parser_generate.set_defaults(command=generate_boards_txt, versions=CORE_VERSIONS) | |||||
args = parser.parse_args() | |||||
args.command(args) |
@ -0,0 +1,8 @@ | |||||
# ESP8266 linker scripts with additional EEPROM sectors | |||||
### Installation | |||||
Depending on ESP8266 version, use files from either `Pre 2.5.0` or `latest` | |||||
Reference [../arduino_ide/README.md](../arduino_ide/README.md) about ESP8266 package location | |||||
Copy all \*.ld files into the `<esp8266-package>/tools/sdk/ld` directory |
@ -0,0 +1,30 @@ | |||||
/* | |||||
sketch: 999KB | |||||
fs: 0KB | |||||
eeprom: 4KB | |||||
*/ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xf9ff0 | |||||
} | |||||
/* | |||||
Provide both _SPIFFS_ and _FS_ to be compatible with 2.3.0...2.6.0+ and | |||||
any library that is using old _SPIFFS_... | |||||
*/ | |||||
PROVIDE ( _SPIFFS_start = 0x402fb000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x402fb000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
PROVIDE ( _FS_start = _SPIFFS_start ); | |||||
PROVIDE ( _FS_end = _SPIFFS_end ); | |||||
PROVIDE ( _FS_page = _SPIFFS_page ); | |||||
PROVIDE ( _FS_block = _SPIFFS_block ); | |||||
INCLUDE "local.eagle.app.v6.common.ld" |
@ -0,0 +1,30 @@ | |||||
/* | |||||
sketch: 995KB | |||||
fs: 0KB | |||||
eeprom: 8KB | |||||
*/ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xf8ff0 | |||||
} | |||||
/* | |||||
Provide both _SPIFFS_ and _FS_ to be compatible with 2.3.0...2.6.0+ and | |||||
any library that is using old _SPIFFS_... | |||||
*/ | |||||
PROVIDE ( _SPIFFS_start = 0x402fa000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x402fa000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
PROVIDE ( _FS_start = _SPIFFS_start ); | |||||
PROVIDE ( _FS_end = _SPIFFS_end ); | |||||
PROVIDE ( _FS_page = _SPIFFS_page ); | |||||
PROVIDE ( _FS_block = _SPIFFS_block ); | |||||
INCLUDE "local.eagle.app.v6.common.ld" |
@ -0,0 +1,30 @@ | |||||
/* | |||||
sketch: 1019KB | |||||
fs: 992KB | |||||
eeprom: 16KB | |||||
*/ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xfeff0 | |||||
} | |||||
/* | |||||
Provide both _SPIFFS_ and _FS_ to be compatible with 2.3.0...2.6.0+ and | |||||
any library that is using old _SPIFFS_... | |||||
*/ | |||||
PROVIDE ( _SPIFFS_start = 0x40300000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x403f8000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x100 ); | |||||
PROVIDE ( _SPIFFS_block = 0x2000 ); | |||||
PROVIDE ( _FS_start = _SPIFFS_start ); | |||||
PROVIDE ( _FS_end = _SPIFFS_end ); | |||||
PROVIDE ( _FS_page = _SPIFFS_page ); | |||||
PROVIDE ( _FS_block = _SPIFFS_block ); | |||||
INCLUDE "local.eagle.app.v6.common.ld" |