Atm-Automat/firmware/src/main.cpp
2025-02-12 10:07:58 +01:00

875 lines
No EOL
26 KiB
C++

#include "lightning_atm.h"
const unsigned int COINS[] = { 0, 0, 5, 10, 20, 50, 100, 200, 1, 2 };
bool button_pressed = false;
unsigned int inserted_cents = 0;
unsigned long long time_last_press = millis();
String baseURLATM;
String secretATM;
String currencyATM;
// *** for Waveshare ESP32 Driver board *** //
#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
SPIClass hspi(HSPI);
#endif
// *** end Waveshare ESP32 Driver board *** //
void setup()
{
initialize_display(); // connection to the e-ink display
Serial.begin(9600);
// *** for Waveshare ESP32 Driver board *** //
#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
hspi.begin(13, 12, 14, 15); // remap hspi for EPD (swap pins)
display.epd2.selectSPI(hspi, SPISettings(4000000, MSBFIRST, SPI_MODE0));
#endif
// *** end Waveshare ESP32 Driver board *** //
if (DEBUG_MODE) // serial connection for debugging over USB
{
sleep(3);
Serial.println("Setup with debug mode..."); // for monitoring with serial monitor to debug
Serial.println("Selected display type: " + display_type);
}
pinMode(COIN_PIN, INPUT_PULLUP); // coin acceptor input
pinMode(LED_BUTTON_PIN, OUTPUT); // LED of the LED Button
pinMode(BUTTON_PIN, INPUT_PULLUP); // Button
pinMode(MOSFET_PIN, OUTPUT); // mosfet relay to block the coin acceptor
digitalWrite(MOSFET_PIN, LOW); // set it low to accept coins, high to block coins
attachInterrupt(BUTTON_PIN, button_pressed_itr, FALLING); // interrupt, will set button_pressed to true when button is pressed
home_screen(); // will show first screen
digitalWrite(LED_BUTTON_PIN, HIGH); // light up the led
baseURLATM = getValue(lnurlDeviceString, ',', 0); // setup wallet data from string
secretATM = getValue(lnurlDeviceString, ',', 1);
currencyATM = getValue(lnurlDeviceString, ',', 2);
}
void loop()
{
unsigned int pulses = 0;
unsigned long long time_last_press;
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();
}
}
}
// function to handle the button interrupt
void IRAM_ATTR button_pressed_itr()
{
button_pressed = true;
}
// blocking loop which is called when the qr code is shown
void wait_for_user_to_scan()
{
unsigned long long time;
bool light_on;
if (DEBUG_MODE)
Serial.println("Waiting for user to scan qr code and press button...");
light_on = true;
time = millis(); // save start time
digitalWrite(LED_BUTTON_PIN, HIGH); // light up the led
delay(5000);
button_pressed = false;
Serial.println("wait for user to press button or 10 minutes to go back to home screen");
while (!button_pressed && (millis() - time) < 600000)
{
if (!light_on && (millis() - time) > 30000)
{
digitalWrite(LED_BUTTON_PIN, HIGH);
light_on = true;
}
else if (light_on && (millis() - time) > 30000)
{
digitalWrite(LED_BUTTON_PIN, LOW);
light_on = false;
}
delay(500);
}
Serial.println("Exit waiting");
}
unsigned int detect_coin()
{
unsigned long last_pulse;
unsigned int pulses;
bool prev_value;
bool read_value;
unsigned long entering_time;
unsigned long current_time;
if (DEBUG_MODE)
Serial.println("Starting coin detection...");
pulses = 0;
read_value = 1;
prev_value = digitalRead(COIN_PIN);
digitalWrite(LED_BUTTON_PIN, HIGH);
digitalWrite(MOSFET_PIN, LOW);
button_pressed = false;
entering_time = millis();
while (true && !button_pressed)
{
read_value = digitalRead(COIN_PIN);
if (read_value != prev_value && !read_value)
{
delay(35);
read_value = digitalRead(COIN_PIN);
if (!read_value)
{
pulses++;
last_pulse = millis();
digitalWrite(MOSFET_PIN, HIGH);
}
}
prev_value = read_value;
current_time = millis();
if (pulses > 0 && (current_time - last_pulse > PULSE_TIMEOUT))
{
break;
}
else if (pulses == 0 && ((current_time - entering_time) > 43200000) // refreshes the screen every 12h
&& inserted_cents == 0)
{
clean_screen();
delay(10000);
entering_time = millis();
home_screen();
}
else if (inserted_cents > 0 && (current_time - entering_time) > 360000) // break the loop if no new coin is inserted for some time
button_pressed = true;
}
if (DEBUG_MODE)
Serial.println("Pulses: " + String(pulses));
if (button_pressed)
{
if (DEBUG_MODE)
Serial.println("Button pressed, stopping coin detection");
return (0);
}
return (pulses);
}
/*
** DISPLAY UTILS
*/
// sleep is to put the screen in hibernation mode for longer static frames
void display_sleep()
{
if (display_type == "GxEPD2_150_BN")
display.hibernate();
else if (display_type == "GxEPD2_270")
display.hibernate();
else if (display_type == "GxEPD2_270_GDEY027T91")
display.hibernate();
else if (display_type == "GxEPD2_213_B74")
display.hibernate();
else if (display_type == "GxEPD2_213_flex")
display.hibernate();
else
Serial.println("No suitable display class defined.");
}
void initialize_display()
{
if (display_type == "GxEPD2_150_BN")
display.init(115200, true, 2, false);
else if (display_type == "GxEPD2_270")
display.init(115200, true, 2, false);
else if (display_type == "GxEPD2_270_GDEY027T91")
display.init(115200, true, 2, false);
else if (display_type == "GxEPD2_213_B74")
display.init(115200, true, 2, false);
else if (display_type == "GxEPD2_213_flex")
display.init(115200, true, 2, false);
else
Serial.println("No suitable display class defined.");
}
void home_screen()
{
if (display_type == "GxEPD2_150_BN")
home_screen_waveshare_1_54();
else if (display_type == "GxEPD2_270")
home_screen_waveshare_2_7();
else if (display_type == "GxEPD2_270_GDEY027T91")
home_screen_waveshare_2_7();
else if (display_type == "GxEPD2_213_B74")
home_screen_waveshare_2_13();
else if (display_type == "GxEPD2_213_flex")
home_screen_waveshare_2_13_flex();
else
Serial.println("No suitable display class defined.");
if (DEBUG_MODE)
Serial.println("Home screen printed.");
}
void show_inserted_amount(int amount_in_cents)
{
String amount_in_euro_string;
amount_in_euro_string = get_amount_string(amount_in_cents);
if (display_type == "GxEPD2_150_BN")
show_inserted_amount_waveshare_1_54(amount_in_euro_string);
else if (display_type == "GxEPD2_270")
show_inserted_amount_waveshare_2_7(amount_in_euro_string);
else if (display_type == "GxEPD2_270_GDEY027T91")
show_inserted_amount_waveshare_2_7(amount_in_euro_string);
else if (display_type == "GxEPD2_213_B74")
show_inserted_amount_waveshare_2_13(amount_in_euro_string);
else if (display_type == "GxEPD2_213_flex")
show_inserted_amount_waveshare_2_13_flex(amount_in_euro_string);
else
Serial.println("No suitable display class defined.");
if (DEBUG_MODE)
Serial.println("New amount on screen.");
}
void qr_withdrawl_screen(const char* qr_content)
{
if (display_type == "GxEPD2_150_BN")
qr_withdrawl_screen_waveshare_1_54(qr_content);
else if (display_type == "GxEPD2_270")
qr_withdrawl_screen_waveshare_2_7(qr_content);
else if (display_type == "GxEPD2_270_GDEY027T91")
qr_withdrawl_screen_waveshare_2_7(qr_content);
else if (display_type == "GxEPD2_213_B74")
qr_withdrawl_screen_waveshare_2_13(qr_content);
else if (display_type == "GxEPD2_213_flex")
qr_withdrawl_screen_waveshare_2_13_flex(qr_content);
else
Serial.println("No suitable display class defined.");
if (DEBUG_MODE)
Serial.println("QR generated and Withdrawl screen printed.");
}
// Called to clean the e-ink screen for storage over longer periods
// by button presses on home screen
void clean_screen()
{
if (DEBUG_MODE)
Serial.println("Cleaning screen...");
if (display_type == "GxEPD2_150_BN")
clean_screen_waveshare_1_54();
else if (display_type == "GxEPD2_270")
clean_screen_waveshare_2_7();
else if (display_type == "GxEPD2_270_GDEY027T91")
clean_screen_waveshare_2_7();
else if (display_type == "GxEPD2_213_B74")
clean_screen_waveshare_2_13();
else if (display_type == "GxEPD2_213_flex")
clean_screen_waveshare_2_13_flex();
else
Serial.println("No suitable display class defined.");
}
// converts a cent amount to a String like "1.15 Euro"
String get_amount_string(int amount_in_cents)
{
String euro;
String cents;
String return_value;
int euro_value;
int cent_remainder;
euro_value = amount_in_cents / 100;
cent_remainder = amount_in_cents % 100;
euro = String(euro_value);
if (cent_remainder > 9)
cents = String(cent_remainder);
else if (cent_remainder < 10)
cents = "0" + String(cent_remainder);
return_value = String(euro) + "." + String(cents) + " " + currencyATM;
if (DEBUG_MODE)
Serial.println("Calculated amount string: " + return_value);
return (return_value);
}
////////////////////////////////////////////
///////////////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:
// <variant_byte><len|nonce><len|payload:{pin}{amount}><hmac>
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 - <pin><currency byte><amount>
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]) : "";
}
// Display functions for specific display types
// ################################################################
// # Waveshare 1.54 inch e-Paper Display Modul with SPI Interface #
// ################################################################
void home_screen_waveshare_1_54()
{
if (DEBUG_MODE)
Serial.println("Home screen for Waveshare 1.54 inch display...");
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(0, 10);
display.setTextSize(3);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Insert\nEuro coins\non the\nright\nside to\nstart ->");
display.setCursor(0, 160);
display.setTextSize(1);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Prepare Lightning enabled Bitcoin\nwallet before starting!\n\nSupported coins:\n5 - 50 Cent and 1 - 2 Euro");
display.nextPage();
display.hibernate();
}
void show_inserted_amount_waveshare_1_54(String amount_in_euro)
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(0, 4);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Inserted amount:");
display.setCursor(10, 90);
display.setTextSize(3);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(amount_in_euro);
display.setCursor(0, 160);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Press button\n once finished.");
display.nextPage();
}
void qr_withdrawl_screen_waveshare_1_54(const char* qr_content)
{
QRCode qrcoded;
uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)]; // 20 is "qr version"
t_qrdata qr;
// initialize qr code data
qrcode_initText(&qrcoded, qrcodeData, QR_VERSION, 0, qr_content);
qr.qr_size = qrcoded.size * 2;
qr.start_x = (150 - qr.qr_size) / 2;
qr.start_y = (150 - qr.qr_size) / 2;
qr.module_size = 3;
display.setRotation(1);
display.setFullWindow();
display.firstPage();
// loop trough Y and X axis to draw the qr code rectangle by rectangle
// 0, 0 is the top left of the screen
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++)
{
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);
}
}
// draw the text messages on the screen
display.setCursor(0, 4);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Please scan QR:"); // top message
display.setCursor(0, 170);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Press the \nbutton to reset"); // bottom message
display.nextPage();
display.hibernate();
}
void clean_screen_waveshare_1_54()
{
display.firstPage();
display.nextPage();
display.hibernate();
}
// ##############################################
// # Waveshare 2.7 inch e-ink display functions #
// ##############################################
void home_screen_waveshare_2_7()
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(11, 20);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Insert Euro coins\n on the right ->\n to start ATM");
display.drawBitmap(172, 65, bitcoin_logo, 64, 64, GxEPD_BLACK);
display.setCursor(12, 140);
display.setTextSize(1);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Prepare Lightning enabled Bitcoin\n wallet before starting!\n Supported coins: 5 - 50 Cent, 1 - 2 Euro");
display.nextPage();
display.hibernate();
}
void show_inserted_amount_waveshare_2_7(String amount_in_euro)
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(11, 10);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Inserted amount:");
display.setCursor(20, 75);
display.setTextSize(3);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(amount_in_euro);
display.setCursor(11, 135);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Press button\n once finished.");
display.nextPage();
}
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;
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;
display.setRotation(1);
display.setFullWindow();
display.firstPage();
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++)
{
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
display.setCursor(11, 155);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Reset - press button"); // bottom message
display.nextPage();
display.hibernate();
}
void clean_screen_waveshare_2_7()
{
display.firstPage();
display.nextPage();
display.hibernate();
}
// #########################################################
// # Waveshare 2.13 inch e-ink display (250x122) functions #
// #########################################################
void home_screen_waveshare_2_13()
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(5, 5);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("LIGHTNING ATM");
display.setCursor(3, 33);
display.println("Insert coins");
display.setCursor(3, 50);
display.println("on the right");
display.setCursor(3, 67);
display.println("side to start");
display.drawBitmap(180, 15, bitcoin_logo, 64, 64, GxEPD_BLACK);
display.setCursor(0, 95);
display.setTextSize(1);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Prepare Lightning enabled Bitcoin\n wallet before starting!\n Supported coins: 2 cent to 2 euro");
display.nextPage();
display.hibernate();
}
void show_inserted_amount_waveshare_2_13(String amount_in_euro)
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(10, 4);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Inserted amount:");
display.setCursor(35, 45);
display.setTextSize(3);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(amount_in_euro);
display.setCursor(0, 85);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Press button\n to show QR code");
display.nextPage();
}
void qr_withdrawl_screen_waveshare_2_13(const char* qr_content)
{
QRCode qrcoded;
uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)]; // 20 is "qr version"
t_qrdata qr;
qrcode_initText(&qrcoded, qrcodeData, QR_VERSION, 0, qr_content);
qr.qr_size = qrcoded.size * 2;
qr.start_x = (230 - qr.qr_size) / 2;
qr.start_y = (122 - qr.qr_size) / 2;
qr.module_size = 2;
display.setRotation(1);
display.setFullWindow();
display.firstPage();
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++)
{
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(0, 20);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Scan\n\n QR\n\n code"); // top message
display.setCursor(181, 32);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Reset");
display.setCursor(181, 53);
display.println("press");
display.setCursor(176, 77);
display.println("button");
display.nextPage();
display.hibernate();
}
void clean_screen_waveshare_2_13()
{
display.firstPage();
display.nextPage();
display.hibernate();
}
// ###########################################################################
// # Waveshare 2.13 inch e-ink display (D) flex (yellow) (212x104) functions #
// ###########################################################################
void home_screen_waveshare_2_13_flex()
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(5, 5);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("LIGHTNING ATM");
display.setCursor(3, 25);
display.println("Insert coins");
display.setCursor(3, 42);
display.println("on the right");
display.setCursor(3, 59);
display.println("side to start");
display.drawBitmap(151, 8, bitcoin_logo, 64, 64, GxEPD_BLACK);
display.setCursor(0, 80);
display.setTextSize(1);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Prepare Lightning enabled Bitcoin\n wallet before starting!\n Supported coins: 2 cent to 2 euro");
display.nextPage();
display.hibernate();
}
void show_inserted_amount_waveshare_2_13_flex(String amount_in_euro)
{
display.setRotation(1);
display.setFullWindow();
display.firstPage();
display.setCursor(0, 4);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Inserted amount:");
display.setCursor(30, 35);
display.setTextSize(3);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(amount_in_euro);
display.setCursor(0, 70);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Press button\n to show QR code");
display.nextPage();
}
void qr_withdrawl_screen_waveshare_2_13_flex(const char* qr_content)
{
QRCode qrcoded;
uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)]; // 20 is "qr version"
t_qrdata qr;
qrcode_initText(&qrcoded, qrcodeData, QR_VERSION, 0, qr_content);
qr.qr_size = qrcoded.size * 2;
qr.start_x = (220 - qr.qr_size) / 2;
qr.start_y = (105 - qr.qr_size) / 2;
qr.module_size = 2;
display.setRotation(1);
display.setFullWindow();
display.firstPage();
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++)
{
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(0, 12);
display.setTextSize(2);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println(" Scan\n\n QR\n\n code"); // top message
display.setCursor(170, 37);
display.setTextSize(1);
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
display.println("Reset");
display.setCursor(170, 47);
display.println("press");
display.setCursor(167, 57);
display.println("button");
display.nextPage();
display.hibernate();
}
void clean_screen_waveshare_2_13_flex()
{
display.firstPage();
display.nextPage();
display.hibernate();
}