Working with QR code config, very memory consuming
This commit is contained in:
parent
8bad5ebafa
commit
f611d2f5f8
13 changed files with 1659 additions and 70 deletions
1028
lib/qrcode/qrcodegen.c
Normal file
1028
lib/qrcode/qrcodegen.c
Normal file
File diff suppressed because it is too large
Load diff
385
lib/qrcode/qrcodegen.h
Normal file
385
lib/qrcode/qrcodegen.h
Normal file
|
@ -0,0 +1,385 @@
|
|||
/*
|
||||
* QR Code generator library (C)
|
||||
*
|
||||
* Copyright (c) Project Nayuki. (MIT License)
|
||||
* https://www.nayuki.io/page/qr-code-generator-library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* This library creates QR Code symbols, which is a type of two-dimension barcode.
|
||||
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
|
||||
* A QR Code structure is an immutable square grid of dark and light cells.
|
||||
* The library provides functions to create a QR Code from text or binary data.
|
||||
* The library covers the QR Code Model 2 specification, supporting all versions (sizes)
|
||||
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
|
||||
*
|
||||
* Ways to create a QR Code object:
|
||||
* - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
|
||||
* - Low level: Custom-make the list of segments and call
|
||||
* qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
|
||||
* (Note that all ways require supplying the desired error correction level and various byte buffers.)
|
||||
*/
|
||||
|
||||
|
||||
/*---- Enum and struct types----*/
|
||||
|
||||
/*
|
||||
* The error correction level in a QR Code symbol.
|
||||
*/
|
||||
enum qrcodegen_Ecc {
|
||||
// Must be declared in ascending order of error protection
|
||||
// so that an internal qrcodegen function works properly
|
||||
qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords
|
||||
qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords
|
||||
qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
|
||||
qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The mask pattern used in a QR Code symbol.
|
||||
*/
|
||||
enum qrcodegen_Mask {
|
||||
// A special value to tell the QR Code encoder to
|
||||
// automatically select an appropriate mask pattern
|
||||
qrcodegen_Mask_AUTO = -1,
|
||||
// The eight actual mask patterns
|
||||
qrcodegen_Mask_0 = 0,
|
||||
qrcodegen_Mask_1,
|
||||
qrcodegen_Mask_2,
|
||||
qrcodegen_Mask_3,
|
||||
qrcodegen_Mask_4,
|
||||
qrcodegen_Mask_5,
|
||||
qrcodegen_Mask_6,
|
||||
qrcodegen_Mask_7,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Describes how a segment's data bits are interpreted.
|
||||
*/
|
||||
enum qrcodegen_Mode {
|
||||
qrcodegen_Mode_NUMERIC = 0x1,
|
||||
qrcodegen_Mode_ALPHANUMERIC = 0x2,
|
||||
qrcodegen_Mode_BYTE = 0x4,
|
||||
qrcodegen_Mode_KANJI = 0x8,
|
||||
qrcodegen_Mode_ECI = 0x7,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A segment of character/binary/control data in a QR Code symbol.
|
||||
* The mid-level way to create a segment is to take the payload data
|
||||
* and call a factory function such as qrcodegen_makeNumeric().
|
||||
* The low-level way to create a segment is to custom-make the bit buffer
|
||||
* and initialize a qrcodegen_Segment struct with appropriate values.
|
||||
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
|
||||
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
|
||||
* Moreover, the maximum allowed bit length is 32767 because
|
||||
* the largest QR Code (version 40) has 31329 modules.
|
||||
*/
|
||||
struct qrcodegen_Segment {
|
||||
// The mode indicator of this segment.
|
||||
enum qrcodegen_Mode mode;
|
||||
|
||||
// The length of this segment's unencoded data. Measured in characters for
|
||||
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
|
||||
// Always zero or positive. Not the same as the data's bit length.
|
||||
int numChars;
|
||||
|
||||
// The data bits of this segment, packed in bitwise big endian.
|
||||
// Can be null if the bit length is zero.
|
||||
uint8_t *data;
|
||||
|
||||
// The number of valid data bits used in the buffer. Requires
|
||||
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
|
||||
// The character count (numChars) must agree with the mode and the bit buffer length.
|
||||
int bitLength;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---- Macro constants and functions ----*/
|
||||
|
||||
#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard
|
||||
#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard
|
||||
|
||||
// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
|
||||
// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
|
||||
// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
|
||||
// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
|
||||
#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
|
||||
|
||||
// The worst-case number of bytes needed to store one QR Code, up to and including
|
||||
// version 40. This value equals 3918, which is just under 4 kilobytes.
|
||||
// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
|
||||
#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
|
||||
|
||||
|
||||
|
||||
/*---- Functions (high level) to generate QR Codes ----*/
|
||||
|
||||
/*
|
||||
* Encodes the given text string to a QR Code, returning true if successful.
|
||||
* If the data is too long to fit in any version in the given range
|
||||
* at the given ECC level, then false is returned.
|
||||
*
|
||||
* The input text must be encoded in UTF-8 and contain no NULs.
|
||||
* Requires 1 <= minVersion <= maxVersion <= 40.
|
||||
*
|
||||
* The smallest possible QR Code version within the given range is automatically
|
||||
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
|
||||
* may be higher than the ecl argument if it can be done without increasing the
|
||||
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
|
||||
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
|
||||
*
|
||||
* About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
|
||||
* - Before calling the function:
|
||||
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
|
||||
* reading and writing; hence each array must have a length of at least len.
|
||||
* - The two ranges must not overlap (aliasing).
|
||||
* - The initial state of both ranges can be uninitialized
|
||||
* because the function always writes before reading.
|
||||
* - After the function returns:
|
||||
* - Both ranges have no guarantee on which elements are initialized and what values are stored.
|
||||
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
|
||||
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
|
||||
*
|
||||
* If successful, the resulting QR Code may use numeric,
|
||||
* alphanumeric, or byte mode to encode the text.
|
||||
*
|
||||
* In the most optimistic case, a QR Code at version 40 with low ECC
|
||||
* can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
|
||||
* up to 4296 characters, or any digit string up to 7089 characters.
|
||||
* These numbers represent the hard upper limit of the QR Code standard.
|
||||
*
|
||||
* Please consult the QR Code specification for information on
|
||||
* data capacities per version, ECC level, and text encoding mode.
|
||||
*/
|
||||
bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
|
||||
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
|
||||
|
||||
|
||||
/*
|
||||
* Encodes the given binary data to a QR Code, returning true if successful.
|
||||
* If the data is too long to fit in any version in the given range
|
||||
* at the given ECC level, then false is returned.
|
||||
*
|
||||
* Requires 1 <= minVersion <= maxVersion <= 40.
|
||||
*
|
||||
* The smallest possible QR Code version within the given range is automatically
|
||||
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
|
||||
* may be higher than the ecl argument if it can be done without increasing the
|
||||
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
|
||||
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
|
||||
*
|
||||
* About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
|
||||
* - Before calling the function:
|
||||
* - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow
|
||||
* reading and writing; hence each array must have a length of at least len.
|
||||
* - The two ranges must not overlap (aliasing).
|
||||
* - The input array range dataAndTemp[0 : dataLen] should normally be
|
||||
* valid UTF-8 text, but is not required by the QR Code standard.
|
||||
* - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len]
|
||||
* can be uninitialized because the function always writes before reading.
|
||||
* - After the function returns:
|
||||
* - Both ranges have no guarantee on which elements are initialized and what values are stored.
|
||||
* - dataAndTemp contains no useful data and should be treated as entirely uninitialized.
|
||||
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
|
||||
*
|
||||
* If successful, the resulting QR Code will use byte mode to encode the data.
|
||||
*
|
||||
* In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
|
||||
* sequence up to length 2953. This is the hard upper limit of the QR Code standard.
|
||||
*
|
||||
* Please consult the QR Code specification for information on
|
||||
* data capacities per version, ECC level, and text encoding mode.
|
||||
*/
|
||||
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
|
||||
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
|
||||
|
||||
|
||||
/*---- Functions (low level) to generate QR Codes ----*/
|
||||
|
||||
/*
|
||||
* Encodes the given segments to a QR Code, returning true if successful.
|
||||
* If the data is too long to fit in any version at the given ECC level,
|
||||
* then false is returned.
|
||||
*
|
||||
* The smallest possible QR Code version is automatically chosen for
|
||||
* the output. The ECC level of the result may be higher than the
|
||||
* ecl argument if it can be done without increasing the version.
|
||||
*
|
||||
* About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
|
||||
* - Before calling the function:
|
||||
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
|
||||
* reading and writing; hence each array must have a length of at least len.
|
||||
* - The two ranges must not overlap (aliasing).
|
||||
* - The initial state of both ranges can be uninitialized
|
||||
* because the function always writes before reading.
|
||||
* - The input array segs can contain segments whose data buffers overlap with tempBuffer.
|
||||
* - After the function returns:
|
||||
* - Both ranges have no guarantee on which elements are initialized and what values are stored.
|
||||
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
|
||||
* - Any segment whose data buffer overlaps with tempBuffer[0 : len]
|
||||
* must be treated as having invalid values in that array.
|
||||
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
|
||||
*
|
||||
* Please consult the QR Code specification for information on
|
||||
* data capacities per version, ECC level, and text encoding mode.
|
||||
*
|
||||
* This function allows the user to create a custom sequence of segments that switches
|
||||
* between modes (such as alphanumeric and byte) to encode text in less space.
|
||||
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
|
||||
*/
|
||||
bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
|
||||
enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
|
||||
|
||||
|
||||
/*
|
||||
* Encodes the given segments to a QR Code, returning true if successful.
|
||||
* If the data is too long to fit in any version in the given range
|
||||
* at the given ECC level, then false is returned.
|
||||
*
|
||||
* Requires 1 <= minVersion <= maxVersion <= 40.
|
||||
*
|
||||
* The smallest possible QR Code version within the given range is automatically
|
||||
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
|
||||
* may be higher than the ecl argument if it can be done without increasing the
|
||||
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
|
||||
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
|
||||
*
|
||||
* About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
|
||||
* - Before calling the function:
|
||||
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
|
||||
* reading and writing; hence each array must have a length of at least len.
|
||||
* - The two ranges must not overlap (aliasing).
|
||||
* - The initial state of both ranges can be uninitialized
|
||||
* because the function always writes before reading.
|
||||
* - The input array segs can contain segments whose data buffers overlap with tempBuffer.
|
||||
* - After the function returns:
|
||||
* - Both ranges have no guarantee on which elements are initialized and what values are stored.
|
||||
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
|
||||
* - Any segment whose data buffer overlaps with tempBuffer[0 : len]
|
||||
* must be treated as having invalid values in that array.
|
||||
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
|
||||
*
|
||||
* Please consult the QR Code specification for information on
|
||||
* data capacities per version, ECC level, and text encoding mode.
|
||||
*
|
||||
* This function allows the user to create a custom sequence of segments that switches
|
||||
* between modes (such as alphanumeric and byte) to encode text in less space.
|
||||
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
|
||||
*/
|
||||
bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
|
||||
int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]);
|
||||
|
||||
|
||||
/*
|
||||
* Tests whether the given string can be encoded as a segment in numeric mode.
|
||||
* A string is encodable iff each character is in the range 0 to 9.
|
||||
*/
|
||||
bool qrcodegen_isNumeric(const char *text);
|
||||
|
||||
|
||||
/*
|
||||
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
|
||||
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
|
||||
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
||||
*/
|
||||
bool qrcodegen_isAlphanumeric(const char *text);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment
|
||||
* containing the given number of characters using the given mode. Notes:
|
||||
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal
|
||||
* calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767).
|
||||
* - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
|
||||
* - It is okay for the user to allocate more bytes for the buffer than needed.
|
||||
* - For byte mode, numChars measures the number of bytes, not Unicode code points.
|
||||
* - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
|
||||
* An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
|
||||
*/
|
||||
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given binary data encoded in
|
||||
* byte mode. All input byte arrays are acceptable. Any text string
|
||||
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
|
||||
*/
|
||||
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
|
||||
*/
|
||||
struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given text string encoded in alphanumeric mode.
|
||||
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
|
||||
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
||||
*/
|
||||
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing an Extended Channel Interpretation
|
||||
* (ECI) designator with the given assignment value.
|
||||
*/
|
||||
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
|
||||
|
||||
|
||||
/*---- Functions to extract raw data from QR Codes ----*/
|
||||
|
||||
/*
|
||||
* Returns the side length of the given QR Code, assuming that encoding succeeded.
|
||||
* The result is in the range [21, 177]. Note that the length of the array buffer
|
||||
* is related to the side length - every 'uint8_t qrcode[]' must have length at least
|
||||
* qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
|
||||
*/
|
||||
int qrcodegen_getSize(const uint8_t qrcode[]);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the color of the module (pixel) at the given coordinates, which is false
|
||||
* for light or true for dark. The top left corner has the coordinates (x=0, y=0).
|
||||
* If the given coordinates are out of bounds, then false (light) is returned.
|
||||
*/
|
||||
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -38,6 +38,7 @@ lib_deps =
|
|||
adafruit/Adafruit NeoPixel@^1.11.0
|
||||
https://github.com/dsbaars/universal_pin
|
||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
|
||||
|
||||
[env:lolin_s3_mini]
|
||||
extends = btclock_base
|
||||
|
|
|
@ -43,4 +43,5 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
|||
CONFIG_RTC_CLK_CAL_CYCLES=576
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "config.hpp"
|
||||
|
||||
|
||||
|
||||
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
|
||||
|
||||
Preferences preferences;
|
||||
|
@ -14,6 +12,8 @@ void setup()
|
|||
setupHardware();
|
||||
if (mcp.digitalRead(3) == LOW)
|
||||
{
|
||||
preferences.putBool("wifiConfigured", false);
|
||||
|
||||
WiFi.eraseAP();
|
||||
blinkDelay(100, 3);
|
||||
}
|
||||
|
@ -29,43 +29,90 @@ void setup()
|
|||
|
||||
setupTasks();
|
||||
setupTimers();
|
||||
setupWebsocketClients();
|
||||
|
||||
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 4096, NULL, tskIDLE_PRIORITY, NULL);
|
||||
|
||||
setupButtonTask();
|
||||
}
|
||||
|
||||
void tryImprovSetup()
|
||||
{
|
||||
// if (mcp.digitalRead(3) == LOW)
|
||||
// {
|
||||
// WiFi.persistent(false);
|
||||
// blinkDelay(100, 3);
|
||||
// }
|
||||
// else
|
||||
if (!preferences.getBool("wifiConfigured", false))
|
||||
{
|
||||
WiFi.begin();
|
||||
}
|
||||
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
|
||||
|
||||
uint8_t x_buffer[16];
|
||||
uint8_t x_position = 0;
|
||||
uint8_t x_buffer[16];
|
||||
uint8_t x_position = 0;
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
if (Serial.available() > 0)
|
||||
// Hold second button to start QR code wifi config
|
||||
if (!mcp.digitalRead(2) == LOW)
|
||||
{
|
||||
uint8_t b = Serial.read();
|
||||
WiFiManager wm;
|
||||
|
||||
if (parse_improv_serial_byte(x_position, b, x_buffer, onImprovCommandCallback, onImprovErrorCallback))
|
||||
byte mac[6];
|
||||
WiFi.macAddress(mac);
|
||||
String softAP_SSID = String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
|
||||
WiFi.setHostname(softAP_SSID.c_str());
|
||||
String softAP_password = base64::encode(String(mac[2], 16) + String(mac[4], 16) + String(mac[5], 16) + String(mac[1], 16)).substring(2, 10);
|
||||
|
||||
wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600));
|
||||
wm.setWiFiAutoReconnect(true);
|
||||
wm.setAPCallback([&](WiFiManager *wifiManager)
|
||||
{
|
||||
|
||||
Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
|
||||
WiFi.softAPIP().toString().c_str(),
|
||||
wifiManager->getConfigPortalSSID().c_str(),
|
||||
softAP_password.c_str());
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
delay(1000);
|
||||
|
||||
const String qrText = "qrWIFI:S:" + wifiManager->getConfigPortalSSID() + ";T:WPA;P:" + softAP_password.c_str() + ";;";
|
||||
const String explainText = "*SSID: *\r\n" + wifiManager->getConfigPortalSSID() + "\r\n\r\n*Password:*\r\n" + softAP_password;
|
||||
std::array<String, NUM_SCREENS> epdContent = {"Welcome!", "", "To setup\r\nscan QR or\r\nconnect\r\nmanually", "", explainText, "", qrText};
|
||||
setEpdContent(epdContent);
|
||||
|
||||
});
|
||||
|
||||
wm.setSaveConfigCallback([]() {
|
||||
preferences.putBool("wifiConfigured", true);
|
||||
});
|
||||
|
||||
|
||||
bool ac = wm.autoConnect(softAP_SSID.c_str(), softAP_password.c_str());
|
||||
wm.server->stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
x_buffer[x_position++] = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
x_position = 0;
|
||||
if (Serial.available() > 0)
|
||||
{
|
||||
uint8_t b = Serial.read();
|
||||
|
||||
if (parse_improv_serial_byte(x_position, b, x_buffer, onImprovCommandCallback, onImprovErrorCallback))
|
||||
{
|
||||
x_buffer[x_position++] = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
x_position = 0;
|
||||
}
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
WiFi.begin();
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(400));
|
||||
}
|
||||
}
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
||||
}
|
||||
|
||||
void setupTime()
|
||||
|
@ -90,17 +137,19 @@ void setupPreferences()
|
|||
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
||||
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
||||
|
||||
screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
||||
screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
||||
screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar";
|
||||
screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
||||
screenNameMap[SCREEN_TIME] = "Time";
|
||||
screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
||||
screenNameMap[SCREEN_TIME] = "Time";
|
||||
screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
||||
}
|
||||
|
||||
void setupWebsocketClients()
|
||||
void setupWebsocketClients(void *pvParameters)
|
||||
{
|
||||
setupBlockNotify();
|
||||
setupPriceNotify();
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void setupTimers()
|
||||
|
@ -112,22 +161,27 @@ void setupTimers()
|
|||
void finishSetup()
|
||||
{
|
||||
|
||||
if (preferences.getBool("ledStatus", false)) {
|
||||
if (preferences.getBool("ledStatus", false))
|
||||
{
|
||||
setLights(preferences.getUInt("ledColor", 0xFFCC00));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
clearLeds();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::string> getScreenNameMap() {
|
||||
std::vector<std::string> getScreenNameMap()
|
||||
{
|
||||
return screenNameMap;
|
||||
}
|
||||
|
||||
void setupHardware()
|
||||
{
|
||||
setupLeds();
|
||||
WiFi.setHostname(getMyHostname().c_str());;
|
||||
|
||||
WiFi.setHostname(getMyHostname().c_str());
|
||||
;
|
||||
if (psramInit())
|
||||
{
|
||||
Serial.println(F("PSRAM is correctly initialized"));
|
||||
|
@ -200,10 +254,9 @@ bool improv_connectWifi(std::string ssid, std::string password)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void onImprovErrorCallback(improv::Error err)
|
||||
{
|
||||
blinkDelayColor(100, 1, 255,0,0);
|
||||
blinkDelayColor(100, 1, 255, 0, 0);
|
||||
// pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
||||
// pixels.setPixelColor(1, pixels.Color(255, 0, 0));
|
||||
// pixels.setPixelColor(2, pixels.Color(255, 0, 0));
|
||||
|
@ -254,14 +307,18 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
|
|||
}
|
||||
|
||||
improv_set_state(improv::STATE_PROVISIONING);
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECTING);
|
||||
|
||||
if (improv_connectWifi(cmd.ssid, cmd.password))
|
||||
{
|
||||
|
||||
blinkDelay(100, 3);
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
||||
|
||||
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C", "E", "S", "S"};
|
||||
// setEpdContent(epdContent);
|
||||
|
||||
preferences.putBool("wifiConfigured", true);
|
||||
|
||||
improv_set_state(improv::STATE_PROVISIONED);
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, getLocalUrl(), false);
|
||||
improv_send_response(data);
|
||||
|
@ -269,6 +326,8 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
|
|||
}
|
||||
else
|
||||
{
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
||||
|
||||
improv_set_state(improv::STATE_STOPPED);
|
||||
improv_set_error(improv::Error::ERROR_UNABLE_TO_CONNECT);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
#pragma once;
|
||||
#include <WiFiManager.h>
|
||||
#include "base64.h"
|
||||
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <Preferences.h>
|
||||
#include <Adafruit_MCP23X17.h>
|
||||
|
||||
#include "shared.hpp"
|
||||
#include <esp_system.h>
|
||||
#include <esp_netif.h>
|
||||
#include <esp_sntp.h>
|
||||
#include "epd.hpp"
|
||||
#include "improv.hpp"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -21,6 +20,7 @@
|
|||
#include "lib/button_handler.hpp"
|
||||
#include "lib/led_handler.hpp"
|
||||
|
||||
|
||||
#define NTP_SERVER "pool.ntp.org"
|
||||
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"
|
||||
#define TIME_OFFSET_SECONDS 3600
|
||||
|
@ -39,7 +39,7 @@
|
|||
void setup();
|
||||
void setupTime();
|
||||
void setupPreferences();
|
||||
void setupWebsocketClients();
|
||||
void setupWebsocketClients(void *pvParameters);
|
||||
void setupHardware();
|
||||
void tryImprovSetup();
|
||||
void setupTimers();
|
||||
|
|
|
@ -63,6 +63,8 @@ int bgColor = GxEPD_BLACK;
|
|||
#define FONT_SMALL Antonio_SemiBold20pt7b
|
||||
#define FONT_BIG Antonio_SemiBold90pt7b
|
||||
|
||||
uint8_t qrcode[800];
|
||||
|
||||
void setupDisplays()
|
||||
{
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
|
@ -78,7 +80,7 @@ void setupDisplays()
|
|||
int *taskParam = new int;
|
||||
*taskParam = i;
|
||||
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 4096, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 10000, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
|
||||
}
|
||||
|
||||
xTaskCreate(taskEpd, "epd_task", 2048, NULL, tskIDLE_PRIORITY, &epdTaskHandle);
|
||||
|
@ -92,10 +94,10 @@ void setupDisplays()
|
|||
"K"};
|
||||
|
||||
setEpdContent(epdContent);
|
||||
// for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
// {
|
||||
// xTaskNotifyGive(tasks[i]);
|
||||
// }
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
{
|
||||
xTaskNotifyGive(tasks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void taskEpd(void *pvParameters)
|
||||
|
@ -186,6 +188,14 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
|
|||
splitText(epdIndex, top, bottom, updatePartial);
|
||||
#endif
|
||||
}
|
||||
else if (epdContent[epdIndex].startsWith(F("qr")))
|
||||
{
|
||||
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
}
|
||||
else if (epdContent[epdIndex].length() > 5)
|
||||
{
|
||||
renderText(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
@ -223,7 +233,7 @@ void updateDisplayAlt(int epdIndex)
|
|||
{
|
||||
}
|
||||
|
||||
void splitText(const uint dispNum, String top, String bottom, bool partial)
|
||||
void splitText(const uint dispNum, const String &top, const String &bottom, bool partial)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(&FONT_SMALL);
|
||||
|
@ -384,3 +394,63 @@ std::array<String, NUM_SCREENS> getCurrentEpdContent()
|
|||
// }
|
||||
return currentEpdContent;
|
||||
}
|
||||
void renderText(const uint dispNum, const String &text, bool partial)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
|
||||
displays[dispNum].fillScreen(GxEPD_WHITE);
|
||||
displays[dispNum].setTextColor(GxEPD_BLACK);
|
||||
displays[dispNum].setCursor(0, 50);
|
||||
// displays[dispNum].setFont(&FreeSans9pt7b);
|
||||
|
||||
// std::regex pattern("/\*(.*)\*/");
|
||||
|
||||
std::stringstream ss;
|
||||
ss.str(text.c_str());
|
||||
|
||||
std::string line;
|
||||
|
||||
while (std::getline(ss, line, '\n'))
|
||||
{
|
||||
if (line.rfind("*", 0) == 0)
|
||||
{
|
||||
line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
|
||||
|
||||
displays[dispNum].setFont(&FreeSansBold9pt7b);
|
||||
displays[dispNum].println(line.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
displays[dispNum].setFont(&FreeSans9pt7b);
|
||||
displays[dispNum].println(line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
displays[dispNum].display(partial);
|
||||
}
|
||||
|
||||
void renderQr(const uint dispNum, const String &text, bool partial)
|
||||
{
|
||||
uint8_t tempBuffer[800];
|
||||
bool ok = qrcodegen_encodeText(text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
|
||||
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
|
||||
|
||||
const int size = qrcodegen_getSize(qrcode);
|
||||
|
||||
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
|
||||
const int paddingY = floor(float(displays[dispNum].height() - (size * 4)) / 2);
|
||||
displays[dispNum].setRotation(2);
|
||||
|
||||
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
|
||||
displays[dispNum].fillScreen(GxEPD_WHITE);
|
||||
const int border = 0;
|
||||
|
||||
for (int y = -border; y < size * 4 + border; y++)
|
||||
{
|
||||
for (int x = -border; x < size * 4 + border; x++)
|
||||
{
|
||||
displays[dispNum].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
displays[dispNum].display(partial);
|
||||
}
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
#include "shared.hpp"
|
||||
#include "config.hpp"
|
||||
#include "fonts/fonts.hpp"
|
||||
#include <Fonts/FreeSansBold9pt7b.h>
|
||||
#include <Fonts/FreeSans9pt7b.h>
|
||||
#include <regex>
|
||||
|
||||
#include "qrcodegen.h"
|
||||
|
||||
void setupDisplays();
|
||||
void taskEpd(void *pvParameters);
|
||||
|
||||
void splitText(const uint dispNum, String top, String bottom, bool partial);
|
||||
void splitText(const uint dispNum, const String& top, const String& bottom, bool partial);
|
||||
void splitTextPaged(const uint dispNum, String top, String bottom, bool partial);
|
||||
|
||||
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font);
|
||||
|
@ -24,5 +29,8 @@ int getFgColor();
|
|||
void setBgColor(int color);
|
||||
void setFgColor(int color);
|
||||
|
||||
void renderText(const uint dispNum, const String& text, bool partial);
|
||||
void renderQr(const uint dispNum, const String& text, bool partial);
|
||||
|
||||
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent);
|
||||
std::array<String, NUM_SCREENS> getCurrentEpdContent();
|
||||
|
|
|
@ -23,17 +23,40 @@ void ledTask(void *parameter)
|
|||
|
||||
switch (ledTaskParams)
|
||||
{
|
||||
case LED_EFFECT_WIFI_CONNECT_ERROR:
|
||||
case LED_FLASH_ERROR:
|
||||
blinkDelayColor(250, 3, 255, 0, 0);
|
||||
break;
|
||||
case LED_EFFECT_WIFI_CONNECT_SUCCESS:
|
||||
case LED_FLASH_SUCCESS:
|
||||
blinkDelayColor(250, 3, 0, 255, 0);
|
||||
blinkDelayColor(150, 3, 0, 255, 0);
|
||||
break;
|
||||
case LED_FLASH_UPDATE:
|
||||
break;
|
||||
case LED_FLASH_BLOCK_NOTIFY:
|
||||
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0), pixels.Color(8, 2, 0));
|
||||
break;
|
||||
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
|
||||
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236), pixels.Color(156, 225, 240));
|
||||
break;
|
||||
case LED_EFFECT_WIFI_CONNECTING:
|
||||
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
|
||||
{
|
||||
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
|
||||
{
|
||||
if (j == i)
|
||||
{
|
||||
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
|
||||
}
|
||||
}
|
||||
pixels.show();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
break;
|
||||
case LED_EFFECT_PAUSE_TIMER:
|
||||
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
|
||||
{
|
||||
|
@ -59,7 +82,7 @@ void ledTask(void *parameter)
|
|||
break;
|
||||
case LED_EFFECT_START_TIMER:
|
||||
pixels.clear();
|
||||
pixels.setPixelColor((NEOPIXEL_COUNT-1), pixels.Color(255, 0, 0));
|
||||
pixels.setPixelColor((NEOPIXEL_COUNT - 1), pixels.Color(255, 0, 0));
|
||||
pixels.show();
|
||||
|
||||
delay(900);
|
||||
|
@ -101,18 +124,25 @@ void ledTask(void *parameter)
|
|||
void setupLeds()
|
||||
{
|
||||
pixels.begin();
|
||||
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
|
||||
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
||||
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
|
||||
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
|
||||
pixels.setPixelColor(3, pixels.Color(255, 255, 255));
|
||||
if (preferences.getBool("ledTestOnPower", true))
|
||||
{
|
||||
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
|
||||
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
||||
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
|
||||
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
|
||||
pixels.setPixelColor(3, pixels.Color(255, 255, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels.clear();
|
||||
}
|
||||
pixels.show();
|
||||
setupLedTask();
|
||||
}
|
||||
|
||||
void setupLedTask()
|
||||
{
|
||||
ledTaskQueue = xQueueCreate(5, sizeof(char));
|
||||
ledTaskQueue = xQueueCreate(2, sizeof(char));
|
||||
|
||||
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
|
||||
}
|
||||
|
@ -207,7 +237,6 @@ void setLights(uint32_t color)
|
|||
pixels.show();
|
||||
}
|
||||
|
||||
|
||||
QueueHandle_t getLedTaskQueue()
|
||||
{
|
||||
return ledTaskQueue;
|
||||
|
|
|
@ -19,6 +19,10 @@ const int LED_FLASH_UPDATE = 2;
|
|||
const int LED_FLASH_BLOCK_NOTIFY = 3;
|
||||
const int LED_EFFECT_START_TIMER = 4;
|
||||
const int LED_EFFECT_PAUSE_TIMER = 5;
|
||||
const int LED_EFFECT_WIFI_WAIT_FOR_CONFIG = 100;
|
||||
const int LED_EFFECT_WIFI_CONNECTING = 101;
|
||||
const int LED_EFFECT_WIFI_CONNECT_ERROR = 102;
|
||||
const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103;
|
||||
|
||||
extern TaskHandle_t ledTaskHandle;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ void setupWebserver()
|
|||
}
|
||||
// send event with message "hello!", id current millis
|
||||
// and set reconnect delay to 1 second
|
||||
eventSourceUpdate();
|
||||
client->send("welcome",NULL,millis(),1000);
|
||||
});
|
||||
server.addHandler(&events);
|
||||
|
||||
|
@ -58,15 +58,15 @@ void setupWebserver()
|
|||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
server.begin();
|
||||
if (!MDNS.begin(getMyHostname()))
|
||||
{
|
||||
Serial.println(F("Error setting up MDNS responder!"));
|
||||
while (1)
|
||||
{
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
// if (!MDNS.begin(getMyHostname()))
|
||||
// {
|
||||
// Serial.println(F("Error setting up MDNS responder!"));
|
||||
// while (1)
|
||||
// {
|
||||
// delay(1000);
|
||||
// }
|
||||
// }
|
||||
// MDNS.addService("http", "tcp", 80);
|
||||
|
||||
xTaskCreate(eventSourceTask, "eventSourceTask", 4096, NULL, tskIDLE_PRIORITY, &eventSourceTaskHandle);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "WebServer.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ESPmDNS.h>
|
||||
// #include <ESPmDNS.h>
|
||||
|
||||
#include "lib/block_notify.hpp"
|
||||
#include "lib/price_notify.hpp"
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#include "Arduino.h"
|
||||
#include <WiFiManager.h>
|
||||
#define WEBSERVER_H
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
#include "lib/config.hpp"
|
||||
|
||||
//char ptrTaskList[400];
|
||||
|
|
Loading…
Reference in a new issue