diff --git a/.gitignore b/.gitignore index c11c412..456016a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -firmware/.vscode/ -firmware/.pio/ +.vscode/ +.pio/ telegram_notification_bot/atmbot_rust/target/ \ No newline at end of file diff --git a/firmware/.gitignore b/firmware/.gitignore index 24a84d6..968a41b 100644 --- a/firmware/.gitignore +++ b/firmware/.gitignore @@ -1,4 +1,2 @@ .pio/ .vscode/ - -config.h diff --git a/firmware/include/config.example.h b/firmware/include/config.example.h deleted file mode 100644 index 9981d29..0000000 --- a/firmware/include/config.example.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -/* config.example.h is an EXAMPLE and needsnto be renamed to config.h */ -/* Rename this file to config.h and add yout real configuration including keys here. -The config.h will be ignored by git and you private data is not pushed to the repository */ - -//* PIN connections *// -//* HX916 coin acceptor connections: *// -static const uint8_t COIN_PIN = 17; // pin at the ESP32 the HX916 COIN signal is connected at -static const uint8_t MOSFET_PIN = 16; // pin at the ESP32 the HX916 SET signal is connected at (Please connect the signal that is pulled up by default) - -//* Button connections: *// -static const uint8_t LED_BUTTON_PIN = 21; // pin at the ESP32 the LED of the button is connected at -static const uint8_t BUTTON_PIN = 32; // pin at the ESP32 the button is connected at - -//* eInk display *// -static const uint8_t DSPLY_PIN_CS = 26; // pin at the ESP32 the display CS pin is connected at -static const uint8_t DSPLY_PIN_DC = 25; // pin at the ESP32 the display DC pin is connected at -static const uint8_t DSPLY_PIN_RST = 33; // pin at the ESP32 the display RST pin is connected at -static const uint8_t DSPLY_PIN_BUSY = 27; // pin at the ESP32 the display BUSY pin is connected at - -//* HX916 coin pulse configuration *// -static constexpr unsigned int COINS[] = { 1, 2, 5, 10, 20, 50, 100, 200}; // teached values of coins -static constexpr size_t COINS_COUNT = sizeof(COINS) / sizeof(COINS[0]); // number of teached values -static const unsigned long COIN_PULSE_WIDTH_MS = 45; // set by switch at HX916: fast = 25, med = 35, slow=45 - -//* Config data required by the bleskomat based ATM, to comunicate with the LNBITS server *// -static const char* APIKEY_ID = "Place your apikey ID here."; -static const char* APIKEY_KEY = "Place your apikey key here."; -static const char* APIKEY_ENCODING = "hex"; -static const char* CALLBACK_URL = "Place your callback URL here."; -static const bool SHORTEN = true; - -#endif \ No newline at end of file diff --git a/firmware/include/epaperdisplay.h b/firmware/include/epaperdisplay.h index b4d6062..6e6bde5 100644 --- a/firmware/include/epaperdisplay.h +++ b/firmware/include/epaperdisplay.h @@ -14,19 +14,8 @@ class EpaperDisplay private: GxEPD2_DISPLAY_CLASS *epDisplay; - typedef struct s_qrdata - { - uint16_t current_y; - uint16_t current_x; - uint16_t start_x; - uint16_t start_y; - uint16_t module_size; - uint16_t qr_size; - } t_qrdata; - public: - const uint8_t QR_VERSION = 15; // 20 is standard. 6 for simpler QR code, but does not always work. - + EpaperDisplay(uint8_t DSPLY_PIN_CS, uint8_t DSPLY_PIN_DC, uint8_t DSPLY_PIN_RST, uint8_t DSPLY_PIN_BUSY); ~EpaperDisplay(); void init(); @@ -36,13 +25,10 @@ class EpaperDisplay void updateInsertedAmount(const char*); void cleanScreen(); - uint16_t xCenterText(const char* text); - void drawXCenteredText(const char* text, uint8_t textSize, uint16_t y, uint16_t fgColor, uint16_t bgColor ); void drawText(const char* text, uint8_t textSize, uint16_t x, uint16_t y, uint16_t fgColor, uint16_t bgColor ); void drawText(const char* text, uint16_t x, uint16_t y); void updateText(const char* text, uint16_t x, uint16_t y); void updateText(const char* text, uint8_t textSize, uint16_t x, uint16_t y, uint16_t fgColor, uint16_t bgColor); - void qrCode(const char* content); }; diff --git a/firmware/include/lnurlutil.h b/firmware/include/lnurlutil.h deleted file mode 100644 index aab1477..0000000 --- a/firmware/include/lnurlutil.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef BLESKOMAT_UTIL_H -#define BLESKOMAT_UTIL_H - -// Lnurluril.h/cpp code basically taken form Bleskomat util code: https://github.com/bleskomat/bleskomat-diy and simplyfied to our needs. -// Thank you! - -#include -#include -#include -#include -#include - -namespace lnurlutil { - std::string createQrContent (const double accumulatedValue); - std::string createSignedLnurlWithdraw(const double &amount); - std::string lnurlEncode(const std::string &text); - std::string toUpperCase(std::string s); - std::string urlEncode(const std::string &value); - std::string floatToStringWithPrecision(const float &value, const unsigned short &precision = 6); -} - -#endif diff --git a/firmware/include/main.h b/firmware/include/main.h index ebfa484..c67ef41 100644 --- a/firmware/include/main.h +++ b/firmware/include/main.h @@ -1,4 +1,100 @@ #ifndef MAIN_H #define MAIN_H +#include +#include +#include +#include "qrcode.h" +#include "Bitcoin.h" +#include +#include +#include +#include + +// ######################################## +// ########### USER ACTION ########### +// ######################################## +// Generate and copy in LNbits with the LNURLDevice extension the string for the ATM and paste it here: +const char* lnurlDeviceString = "https://legend.lnbits.com/lnurldevice/api/v1/lnurl/idexample,keyexample,EUR"; +// #################### EXAMPLE: https://legend.lnbits.com/lnurldevice/api/v1/lnurl/idexample,keyexample,EUR +// ######################################## +// ######################################## +// ######################################## + +// Activate for debugging over Serial (1), deactivate in production use (0) +#define DEBUG_MODE 1 + +#define COIN_PIN 17 + +#define PULSE_TIMEOUT 200 +const uint8_t LED_BUTTON_PIN = 21; // old: 13 | new: 21 +const uint8_t BUTTON_PIN = 32; +#define MOSFET_PIN 16 // old: 12 | new: 16 +#define QR_VERSION 6 // 20 is standard. 6 for simpler QR code, but does not always work. + +const uint8_t DSPLY_PIN_CS = 26; +const uint8_t DSPLY_PIN_DC = 25; +const uint8_t DSPLY_PIN_RST = 33; +const uint8_t DSPLY_PIN_BUSY = 27; + +constexpr unsigned int COINS[] = { 1, 2, 5, 10, 20, 50, 100, 200}; +constexpr size_t COINS_COUNT = sizeof(COINS) / sizeof(COINS[0]); +const unsigned long COIN_PULSE_WIDTH_MS = 45; + +typedef struct s_qrdata +{ + uint8_t current_y; + uint8_t current_x; + uint8_t start_x; + uint8_t start_y; + uint8_t module_size; + uint8_t qr_size; +} t_qrdata; + +extern String baseURLATM; +extern String secretATM; +extern String currencyATM; + + +// put function declarations here: +void clean_screen(); +void initialize_display(); +void to_upper(char* arr); +void qr_withdrawl_screen(const char* qr_content); +char* makeLNURL(float total); +int xor_encrypt(uint8_t* output, size_t outlen, uint8_t* key, size_t keylen, uint8_t* nonce, size_t nonce_len, uint64_t pin, uint64_t amount_in_cents); +void show_inserted_amount(int amount_in_cents); +String get_amount_string(int amount_in_cents); +unsigned int detect_coin(); +void home_screen(); +void IRAM_ATTR button_pressed_itr(); +void wait_for_user_to_scan(); +String getValue(String data, char separator, int index); +void display_sleep(); +void test_macro(); + +// Waveshare 1.54 inch e-ink display functions +void home_screen_waveshare_1_54(); +void show_inserted_amount_waveshare_1_54(String amount_in_euro); +void qr_withdrawl_screen_waveshare_1_54(const char* qr_content); +void clean_screen_waveshare_1_54(); + +// Waveshare 2.7 inch e-ink display functions +void home_screen_waveshare_2_7(); +void show_inserted_amount_waveshare_2_7(String amount_in_euro); +void qr_withdrawl_screen_waveshare_2_7(const char* qr_content); +void clean_screen_waveshare_2_7(); + +// Waveshare 2.13 inch e-ink display (250x122) functions +void home_screen_waveshare_2_13(); +void show_inserted_amount_waveshare_2_13(String amount_in_euro); +void qr_withdrawl_screen_waveshare_2_13(const char* qr_content); +void clean_screen_waveshare_2_13(); + +// Waveshare 2.13 inch e-ink display (D) flex (yellow) (212x104) functions +void home_screen_waveshare_2_13_flex(); +void show_inserted_amount_waveshare_2_13_flex(String amount_in_euro); +void qr_withdrawl_screen_waveshare_2_13_flex(const char* qr_content); +void clean_screen_waveshare_2_13_flex(); + #endif diff --git a/firmware/platformio.ini b/firmware/platformio.ini index 1d529a5..48746ed 100644 --- a/firmware/platformio.ini +++ b/firmware/platformio.ini @@ -16,4 +16,4 @@ monitor_speed = 115200 lib_deps = zinggjm/GxEPD2@^1.5.2 ricmoo/QRCode@^0.0.1 - chill1/lnurl@0.4.1 + stepansnigirev/uBitcoin@^0.2.0 diff --git a/firmware/src/epaperdisplay.cpp b/firmware/src/epaperdisplay.cpp index 33f57f5..4718eec 100644 --- a/firmware/src/epaperdisplay.cpp +++ b/firmware/src/epaperdisplay.cpp @@ -1,5 +1,4 @@ #include "epaperdisplay.h" -#include "qrcode.h" // *** for Waveshare ESP32 Driver board *** // #if defined(ESP32) && defined(USE_HSPI_FOR_EPD) @@ -29,12 +28,10 @@ void EpaperDisplay::init() hspi.begin(13, 12, 14, 15); // remap hspi for EPD (swap pins) epDisplay>epd2.selectSPI(hspi, SPISettings(4000000, MSBFIRST, SPI_MODE0)); #endif - epDisplay->setRotation(1); } - void EpaperDisplay::cleanScreen() { epDisplay->firstPage(); @@ -45,20 +42,6 @@ void EpaperDisplay::cleanScreen() epDisplay->hibernate(); } - -uint16_t EpaperDisplay::xCenterText(const char* text) -{ - int16_t xul, yul; - uint16_t textWidth, textHeight; - epDisplay->getTextBounds(text, 0, 0, &xul, &yul, &textWidth, &textHeight); - xul = (epDisplay->width() - textWidth)/2; - if(xul < 0 ) - { - xul = 0; - } - return xul; -} - void EpaperDisplay::drawText(const char* text, uint16_t x, uint16_t y) { epDisplay->setCursor(x, y); @@ -72,12 +55,6 @@ void EpaperDisplay::drawText(const char* text, uint8_t textSize, uint16_t x, uin drawText(text, x, y); } -void EpaperDisplay::drawXCenteredText(const char* text, uint8_t textSize, uint16_t y, uint16_t fgColor, uint16_t bgColor ) -{ - epDisplay->setTextSize(textSize); - epDisplay->setTextColor(fgColor, bgColor); - drawText(text, xCenterText(text), y); -} void EpaperDisplay::updateText(const char* text, uint16_t x, uint16_t y) { @@ -131,53 +108,47 @@ void EpaperDisplay::updateInsertedAmount(const char* amount_in_euro) updateText(amount_in_euro, 3, 20, 75, GxEPD_BLACK, GxEPD_WHITE); } -void EpaperDisplay::qrCode(const char* content) +#if 0 + +void qr_withdrawl_screen_waveshare_2_7(const char* qr_content) { QRCode qrcoded; uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)]; // 20 is "qr version" t_qrdata qr; - uint16_t color = GxEPD_BLACK; - qrcode_initText(&qrcoded, qrcodeData, QR_VERSION, 0, content); + qrcode_initText(&qrcoded, qrcodeData, QR_VERSION, 0, qr_content); + qr.qr_size = qrcoded.size * 3; + qr.start_x = (264 - qr.qr_size) / 2; + qr.start_y = (176 - qr.qr_size) / 2; qr.module_size = 3; - qr.qr_size = qrcoded.size * qr.module_size; - Serial.printf("Qrcode size: %d, qrcoded.size %d\n", qr.qr_size, qrcoded.size); - qr.start_x = (epDisplay->width() - qr.qr_size) / 2; - qr.start_y = (epDisplay->height() - qr.qr_size) / 2; - - epDisplay->setFullWindow(); - epDisplay->firstPage(); - do + display.setRotation(1); + display.setFullWindow(); + display.firstPage(); + for (qr.current_y = 0; qr.current_y < qrcoded.size; qr.current_y++) { - - for (qr.current_y = 0; qr.current_y < qrcoded.size; qr.current_y++) + for (qr.current_x = 0; qr.current_x < qrcoded.size; qr.current_x++) { - for (qr.current_x = 0; qr.current_x < qrcoded.size; qr.current_x++) - { - if (qrcode_getModule(&qrcoded, qr.current_x, qr.current_y)) - { - color = GxEPD_BLACK; - } - else - { - color = GxEPD_WHITE; - } - - epDisplay->fillRect(qr.start_x + (qr.module_size * qr.current_x), - qr.start_y + (qr.module_size * qr.current_y), qr.module_size, qr.module_size, color); - } + if (qrcode_getModule(&qrcoded, qr.current_x, qr.current_y)) + display.fillRect(qr.start_x + qr.module_size * qr.current_x, + qr.start_y + qr.module_size * qr.current_y, qr.module_size, qr.module_size, GxEPD_BLACK); + else + display.fillRect(qr.start_x + qr.module_size * qr.current_x, + qr.start_y + qr.module_size * qr.current_y, qr.module_size, qr.module_size, GxEPD_WHITE); } + } + display.setCursor(11, 5); + display.setTextSize(2); + display.setTextColor(GxEPD_BLACK, GxEPD_WHITE); + display.println("Please scan QR code:"); // top message - uint16_t radius_px = 5; - epDisplay->drawRoundRect(qr.start_x - radius_px , qr.start_y - radius_px, qr.qr_size+(2*radius_px), qr.qr_size+(2*radius_px), radius_px, GxEPD_BLACK); + display.setCursor(11, 155); + display.setTextSize(2); + display.setTextColor(GxEPD_BLACK, GxEPD_WHITE); + display.println("Reset - press button"); // bottom message - drawXCenteredText("Please scan the QR code!\n", 2, 10, GxEPD_BLACK, GxEPD_WHITE ); - drawXCenteredText("This will transfer the paid value to your wallet.\n", 1, 30, GxEPD_BLACK, GxEPD_WHITE ); - drawXCenteredText("Reset - press button\n", 2, epDisplay->height() - 20, GxEPD_BLACK, GxEPD_WHITE ); - - /* code */ - } while (epDisplay->nextPage()); - epDisplay->hibernate(); + display.nextPage(); + display.hibernate(); } +#endif \ No newline at end of file diff --git a/firmware/src/lnurlutil.cpp b/firmware/src/lnurlutil.cpp deleted file mode 100644 index 0dad936..0000000 --- a/firmware/src/lnurlutil.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "lnurlutil.h" -#include "config.h" - -namespace { - -const uint8_t FIATPRECESION = 2; -const char* FIATCURRENCY = "EUR"; - - Lnurl::SignerConfig getLnurlSignerConfig() { - struct Lnurl::SignerConfig lnurl; - lnurl.apiKey.id = "86f6d045d26e4e39"; - lnurl.apiKey.key = "b2ff678c2c634868b70bf678948e92c4382b1e8f7c651bd648b0caefd5cca613"; - lnurl.apiKey.encoding = "hex"; - lnurl.callbackUrl = "https://lnbits.makerlab-murnau.de/bleskomat/u"; - lnurl.shorten = true; - return lnurl; - } - - std::string generate_nonce() { - std::ostringstream ss; - uint32_t entropy = esp_random(); - // Random numbers generated by esp_random are probably sufficient. - // But just to be safe let's append the current time in microseconds as well. - auto extraEntropyFromTime = std::ceil((std::chrono::steady_clock::now().time_since_epoch().count() / 1000) % (UINT32_MAX / 10)); - entropy += extraEntropyFromTime; - ss << entropy; - return ss.str(); - } -} - -namespace lnurlutil { - - std::string createQrContent (const double accumulatedValue) { - const std::string signedUrl = createSignedLnurlWithdraw(accumulatedValue); - const std::string encoded = lnurlEncode(signedUrl); - std::string qrcodeData = ""; - qrcodeData += toUpperCase(encoded); - return qrcodeData; - } - - std::string createSignedLnurlWithdraw(const double &t_amount) { - Lnurl::Signer signer(getLnurlSignerConfig()); - std::string nonce = generate_nonce(); - Lnurl::WithdrawParams params; - std::string amount = floatToStringWithPrecision(t_amount, FIATPRECESION); - params.minWithdrawable = amount; - params.maxWithdrawable = amount; - params.defaultDescription = ""; - params.custom["f"] = FIATCURRENCY; - return signer.create_url(params, nonce); - } - - std::string lnurlEncode(const std::string &text) { - return Lnurl::encode(text); - } - - std::string toUpperCase(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::toupper(c); }); - return s; - } - - std::string urlEncode(const std::string &value) { - std::ostringstream escaped; - escaped.fill('0'); - escaped << std::hex; - for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) { - std::string::value_type c = (*i); - // Keep alphanumeric and other accepted characters intact - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { - escaped << c; - continue; - } - // Any other characters are percent-encoded - escaped << std::uppercase; - escaped << '%' << std::setw(2) << int((unsigned char) c); - escaped << std::nouppercase; - } - return escaped.str(); - } - - std::string floatToStringWithPrecision(const float &value, const unsigned short &precision) { - std::ostringstream out; - out.precision(precision); - out << std::fixed << value; - return out.str(); - } -} - - diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 5b04ec5..b48b84a 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,26 +1,24 @@ + + #include "main.h" -#include #include "epaperdisplay.h" #include "button.h" #include "coinacceptor.h" -#include "lnurlutil.h" -#include "config.h" EpaperDisplay epDisp(DSPLY_PIN_CS, DSPLY_PIN_DC, DSPLY_PIN_RST, DSPLY_PIN_BUSY); AtmButton button(BUTTON_PIN, LED_BUTTON_PIN); CoinAcceptor cacc(COIN_PIN, MOSFET_PIN, COINS, COINS_COUNT, COIN_PULSE_WIDTH_MS); +/*------------*/ + +String baseURLATM, secretATM, currencyATM; + void setup() { Serial.begin(115200); cacc.begin(); button.begin(); epDisp.init(); - std::string lnurl = lnurlutil::createQrContent(0.05); - epDisp.qrCode(lnurl.c_str()); - while(0==0) - {delay(1000);} - epDisp.homeScreen(); while(0 == 0) @@ -33,11 +31,192 @@ void setup() delay(1000); } + baseURLATM = getValue(lnurlDeviceString, ',', 0); // setup wallet data from string + secretATM = getValue(lnurlDeviceString, ',', 1); + currencyATM = getValue(lnurlDeviceString, ',', 2); + while(0==0) {delay(1000);} } void loop() { +#if 0 + pulses = 0; + pulses = detect_coin(); // detect_coin() is a loop to detect the input of coins, will return the amount of pulses + if (pulses >= 2 && pulses <= 9) + { + digitalWrite(MOSFET_PIN, HIGH); + digitalWrite(LED_BUTTON_PIN, LOW); + inserted_cents += COINS[pulses]; + show_inserted_amount(inserted_cents); + } + else if (button_pressed && inserted_cents > 0) + { + digitalWrite(MOSFET_PIN, HIGH); + button_pressed = false; + char* lnurl = makeLNURL(inserted_cents); + qr_withdrawl_screen(lnurl); + free(lnurl); + wait_for_user_to_scan(); + digitalWrite(LED_BUTTON_PIN, HIGH); + home_screen(); + digitalWrite(MOSFET_PIN, LOW); + inserted_cents = 0; + } + else if (button_pressed && !pulses && !inserted_cents) // to clean the screen (for storage), press the button several times + { + int press_counter = 0; + + button_pressed = false; + time_last_press = millis(); + while ((millis() - time_last_press) < 4000 && press_counter < 6) + { + if (button_pressed) + { + if (DEBUG_MODE) + Serial.println("Button pressed"); + time_last_press = millis(); + button_pressed = false; + press_counter++; + delay(500); + } + } + if (press_counter > 5) + { + if (DEBUG_MODE) + Serial.println("Button pressed over 5 times, will clean screen..."); + digitalWrite(LED_BUTTON_PIN, LOW); + clean_screen(); + display_sleep(); + delay(30000); + home_screen(); + } + } +#endif } + + + +//////////////////////////////////////////// +///////////////LNURL STUFF////////////////// +////USING STEPAN SNIGREVS GREAT CRYTPO////// +////////////THANK YOU STEPAN//////////////// +//////////////////////////////////////////// + +int xor_encrypt(uint8_t* output, size_t outlen, uint8_t* key, size_t keylen, uint8_t* nonce, size_t nonce_len, uint64_t pin, uint64_t amount_in_cents) +{ + // check we have space for all the data: + // + if (outlen < 2 + nonce_len + 1 + lenVarInt(pin) + 1 + lenVarInt(amount_in_cents) + 8) + { + return 0; + } + + int cur = 0; + output[cur] = 1; // variant: XOR encryption + cur++; + + // nonce_len | nonce + output[cur] = nonce_len; + cur++; + memcpy(output + cur, nonce, nonce_len); + cur += nonce_len; + + // payload, unxored first - + int payload_len = lenVarInt(pin) + 1 + lenVarInt(amount_in_cents); + output[cur] = (uint8_t)payload_len; + cur++; + uint8_t* payload = output + cur; // pointer to the start of the payload + cur += writeVarInt(pin, output + cur, outlen - cur); // pin code + cur += writeVarInt(amount_in_cents, output + cur, outlen - cur); // amount + cur++; + + // xor it with round key + uint8_t hmacresult[32]; + SHA256 h; + h.beginHMAC(key, keylen); + h.write((uint8_t*)"Round secret:", 13); + h.write(nonce, nonce_len); + h.endHMAC(hmacresult); + for (int i = 0; i < payload_len; i++) + { + payload[i] = payload[i] ^ hmacresult[i]; + } + + // add hmac to authenticate + h.beginHMAC(key, keylen); + h.write((uint8_t*)"Data:", 5); + h.write(output, cur); + h.endHMAC(hmacresult); + memcpy(output + cur, hmacresult, 8); + cur += 8; + + // return number of bytes written to the output + return cur; +} + +char* makeLNURL(float total) +{ + int randomPin = random(1000, 9999); + byte nonce[8]; + for (int i = 0; i < 8; i++) + { + nonce[i] = random(256); + } + byte payload[51]; // 51 bytes is max one can get with xor-encryption + size_t payload_len = xor_encrypt(payload, sizeof(payload), (uint8_t*)secretATM.c_str(), secretATM.length(), nonce, sizeof(nonce), randomPin, float(total)); + String preparedURL = baseURLATM + "?atm=1&p="; + preparedURL += toBase64(payload, payload_len, BASE64_URLSAFE | BASE64_NOPADDING); + if (DEBUG_MODE) + Serial.println(preparedURL); + char Buf[200]; + preparedURL.toCharArray(Buf, 200); + char* url = Buf; + byte* data = (byte*)calloc(strlen(url) * 2, sizeof(byte)); + if (!data) + return (NULL); + size_t len = 0; + int res = convert_bits(data, &len, 5, (byte*)url, strlen(url), 8, 1); + char* charLnurl = (char*)calloc(strlen(url) * 2, sizeof(byte)); + if (!charLnurl) + { + free(data); + return (NULL); + } + bech32_encode(charLnurl, "lnurl", data, len); + to_upper(charLnurl); + free(data); + return (charLnurl); +} + +void to_upper(char* arr) +{ + for (size_t i = 0; i < strlen(arr); i++) + { + if (arr[i] >= 'a' && arr[i] <= 'z') + { + arr[i] = arr[i] - 'a' + 'A'; + } + } +} + +// Function to seperate the LNURLDevice string in key, url and currency +String getValue(const String data, char separator, int index) +{ + int found = 0; + int strIndex[] = { 0, -1 }; + const int maxIndex = data.length() - 1; + + for (int i = 0; i <= maxIndex && found <= index; i++) + { + if (data.charAt(i) == separator || i == maxIndex) + { + found++; + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + } + } + return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; +}