initial commit to this copied repo.
33
.devcontainer/Containerfile
Normal file
|
@ -0,0 +1,33 @@
|
|||
ARG VARIANT=bookworm
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT}
|
||||
|
||||
RUN sudo apt-get update
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
sudo apt-get -y install --no-install-recommends \
|
||||
clang \
|
||||
python3-venv \
|
||||
udev
|
||||
|
||||
## Set up udev rules for PlatformIO
|
||||
RUN curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
|
||||
RUN service udev restart
|
||||
RUN usermod -a -G dialout vscode
|
||||
RUN usermod -a -G plugdev vscode
|
||||
|
||||
ARG FEDORA_COMPAT=0
|
||||
### Set up compatibility with Fedora host (if needed)
|
||||
### Since Fedora uses `18` as the group for dialout, we need to add it to the container
|
||||
RUN if [ "$FEDORA_COMPAT" = "1" ]; then \
|
||||
sudo groupadd -g 18 compat_dialout; \
|
||||
sudo usermod -a -G compat_dialout vscode; \
|
||||
fi
|
||||
|
||||
|
||||
# Install PlatformIO CLI
|
||||
USER vscode
|
||||
RUN curl -fsSL -o /tmp/get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
|
||||
RUN python3 /tmp/get-platformio.py
|
||||
RUN echo 'export PATH="$PATH:$HOME/.platformio/penv/bin"' | tee -a /home/vscode/.bashrc /home/vscode/.zshrc
|
||||
RUN echo 'export PATH="$PATH:$HOME/.platformio/penv/bin"' | sudo tee -a /root/.bashrc /root/.zshrc
|
||||
|
||||
|
33
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "PlatformIO (Community)",
|
||||
"build": {
|
||||
"dockerfile": "Containerfile",
|
||||
"context": ".",
|
||||
"args": {
|
||||
"FEDORA_COMPAT" : "1", // enables fedora compatibility mode (extra dialout group with gid 18)
|
||||
"VARIANT": "bookworm"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "zsh"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools",
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
},
|
||||
"forwardPorts": [
|
||||
8008
|
||||
],
|
||||
"mounts": [
|
||||
"source=/dev/,target=/dev/,type=bind,consistency=consistent"
|
||||
],
|
||||
"runArgs": [
|
||||
"--privileged"
|
||||
],
|
||||
"features": {},
|
||||
"postAttachCommand": "sudo service udev restart"
|
||||
}
|
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.vscode/
|
||||
.pio/
|
||||
telegram_notification_bot/atmbot_rust/target/
|
BIN
3d-print-model/Back_Lid_NORMAL_v1_NoSupp.stl
Normal file
BIN
3d-print-model/Back_Lid_THICKER_v1_NoSupp.stl
Normal file
BIN
3d-print-model/CCatch v3.stl
Normal file
BIN
3d-print-model/Front_Glass_v1_NoSupp.stl
Normal file
|
@ -0,0 +1,9 @@
|
|||
I have added new 3D models, which can be supplemented with magnets so that you no longer have to attach the front panel to the case with a rubber band. You can find the procedure and more details in my Twitter Thread ...
|
||||
|
||||
Magnets: 5x5mm round nickel plate: https://www.supermagnete.ch/scheibenmagnete-neodym/scheibenmagnet-5mm-5mm_S-05-05-N
|
||||
Metal plates: 2x10mm self-adhesive: https://www.supermagnete.ch/magnethaftgruende-selbstklebend/metallscheiben-selbstklebend-10mm_PAS-10
|
||||
|
||||
For more information please DM me on Twitter or Telegram! @Nicolas_b58
|
||||
|
||||
Update axelhamburch 838776:
|
||||
In this version, the front panel has a hole for a 12 mm LED button in the upper area of the front panel. You will need a drill and possibly a file for the "screen backplate". Depending on which display you use, you may also need this for a feed-through for the ribbon cable.
|
BIN
3d-print-model/Magnet Case/Case Magnet.stl
Normal file
BIN
3d-print-model/Magnet Case/Front Magnet.stl
Normal file
BIN
3d-print-model/Magnet Case/Logo Backplate Magnet.stl
Normal file
BIN
3d-print-model/Magnet Case/Screen Backplate Magnet.stl
Normal file
6
3d-print-model/Magnet Case/info.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
I have added new 3D models, which can be supplemented with magnets so that you no longer have to attach the front panel to the case with a rubber band. You can find the procedure and more details in my Twitter Thread ...
|
||||
|
||||
Magnets: 5x5mm round nickel plate: https://www.supermagnete.ch/scheibenmagnete-neodym/scheibenmagnet-5mm-5mm_S-05-05-N
|
||||
Metal plates: 2x10mm self-adhesive: https://www.supermagnete.ch/magnethaftgruende-selbstklebend/metallscheiben-selbstklebend-10mm_PAS-10
|
||||
|
||||
For more information please DM me on Twitter or Telegram! @Nicolas_b58
|
41
3d-print-model/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
<img src="images/3d_printed_case.jpeg"> <br>
|
||||
|
||||
# 3D Printed L-ATM Case PE-Edition
|
||||
|
||||
Below files can be used to print a case, which lowers your overall costs. The models can be printed without any supports.
|
||||
|
||||
This is the first version and is designed to be used with inserts to mount the Coin-Acceptor as well as the Rasbperry-Pi Zero. For the Coin Acceptor use **M3** inserts [(Amazon)](https://amzn.to/35eNkgj), for the Raspberry-Pi **M2.5** [(Amazon)](https://amzn.to/2HjF0Uo) inserts are needed.
|
||||
The Lids holds by friction fit.
|
||||
|
||||
|
||||
For the case **needed** are 3 Parts:
|
||||
- **Shell**
|
||||
|
||||
- **Back Lid**
|
||||
- two versions
|
||||
- normal with 2mm thick plate
|
||||
- thicker with 3mm thick plate
|
||||
- **Top Lid**
|
||||
- there are two separate versions:
|
||||
- with Lightning-Logo, *for dual color print add a **color change at Z 3.00 mm***. <img src="images/top_lid_with_logo.jpeg" width="480">
|
||||
- without any logo
|
||||
- Mounting Rim
|
||||
- optional but *recommended* to properly glue the Top Lid onto the Back Lid.
|
||||
|
||||
|
||||
1. Print Shell / Back Lid / Top Lid / Mounting Rim
|
||||
2. Use soldering iron to:
|
||||
1. Add M3 Inserts into Coin Acceptor holes
|
||||
2. Add M2.5 Inserts into Raspberry holes
|
||||
|
||||
<br><img src="images/shell_inserts.jpeg" width="480"> <br>
|
||||
<img src="images/shell_inserted.jpeg" width="480">
|
||||
|
||||
3. If you have printed the *Mounting Rim* put it around the BackLid
|
||||
<br><img src="images/lids_and_rim.jpeg" width="480"> <br> <img src="images/rim_and_backlid.jpeg" width="480">
|
||||
4. put glue on the Top Lid and use something to press both together
|
||||
<img src="images/mounting_rim.jpeg" width="480">
|
||||
5. this is what it should look like without the rim
|
||||
<img src="images/glued_lid.jpeg" width="480">
|
||||
|
||||
|
BIN
3d-print-model/Shell_v1_NoSupp.stl
Normal file
BIN
3d-print-model/Top_Lid_No_Logo_v1_NoSupp.stl
Normal file
BIN
3d-print-model/Top_Lid_with_Logo_v1_NoSupp.stl
Normal file
BIN
3d-print-model/images/3d_printed_case.jpeg
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
3d-print-model/images/glued_lid.jpeg
Normal file
After Width: | Height: | Size: 931 KiB |
BIN
3d-print-model/images/lids_and_rim.jpeg
Normal file
After Width: | Height: | Size: 875 KiB |
BIN
3d-print-model/images/mounting_rim.jpeg
Normal file
After Width: | Height: | Size: 1,020 KiB |
BIN
3d-print-model/images/rim_and_backlid.jpeg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
3d-print-model/images/shell_inserted.jpeg
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
3d-print-model/images/shell_inserts.jpeg
Normal file
After Width: | Height: | Size: 2.7 MiB |
BIN
3d-print-model/images/shell_m3_inserts.jpeg
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
3d-print-model/images/shell_with_mounted_pi.jpeg
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
3d-print-model/images/top_lid_with_logo.jpeg
Normal file
After Width: | Height: | Size: 994 KiB |
BIN
3d-print-model/just_logo.stl
Normal file
BIN
3d-print-model/optional_Mounting_Rim_v1_NoSupp.stl
Normal file
BIN
3d-print-model/transport_screen_cover.3mf
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Felix
|
||||
|
||||
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.
|
123
README.md
Normal file
|
@ -0,0 +1,123 @@
|
|||
# offline-LightningATM-esp32
|
||||
Bitcoin ATM (coins only) with lightning network support, running offline on an esp32.
|
||||
The lightning network is a 2nd layer protocol on top of the bitcoin protocol enabling trustless transactions with instant settlement and cheap fees.
|
||||
|
||||
## Special thanks to
|
||||
@21isenough and contributors for the 3d models and inspiration
|
||||
[RPi based Lightning ATM repo](https://github.com/21isenough/LightningATM)
|
||||
|
||||
LNBits, Ben Arc and Stepan Snigirev for the cryptograpy used to make this ATM working without internet connection
|
||||
[Fossa ATM repository](https://github.com/lnbits/fossa) | [LNBits Homepage](https://lnbits.com/) | [uBitcoin library](https://github.com/micro-bitcoin/uBitcoin)
|
||||
|
||||
Axel Hamburch for the very detailed guide in german language on assembly of the electronics
|
||||
[Ereignishorizont Blog](https://ereignishorizont.xyz/lightning-atm/)
|
||||
|
||||
## Used parts
|
||||
All the parts are available on eBay and Aliexpress
|
||||
|
||||
* ESP32 NodeMCU Dev Board | Any "normal" esp32 dev board should do the job here | [Example](https://web.archive.org/web/20231202141343/https://www.berrybase.de/en/esp32-nodemcu-development-board)
|
||||
* DC-DC Adjustable Step-up Boost Power Supply LM2587S 5V -> 12V | for the coin acceptor, runs on 12V [Example](https://de.aliexpress.com/item/32834930982.html)
|
||||
* Waveshare 1.54 inch e-Paper Display Modul with SPI Interface | [Example](https://www.waveshare.com/1.54inch-e-paper-module.htm)
|
||||
* Programmable Coin Acceptor (HX-616) - 6 Coin | [Example](https://de.aliexpress.com/item/1005005203759184.html)
|
||||
* 10mm Metal Push Button Switch 3-6V with Yellow LED, Self-reset Momentary | [Example](https://de.aliexpress.com/item/1005004527235094.html)
|
||||
* USB Type C socket | to plug in the power supply (i used a Raspberry Pi type C power supply) [Example](https://de.aliexpress.com/item/1005005347655323.html)
|
||||
* Little Mosfet modules ("15A 400W MOS FET Trigger") | To block the coin acceptor at certain points [Example](https://de.aliexpress.com/item/33038160184.html)
|
||||
* Orange PLA Filament for the 3D Printer | [Example](https://us.polymaker.com/products/polylite-pla)
|
||||
* Jumper Wires | [Example](https://de.aliexpress.com/item/1005005945668553.html)
|
||||
* Heat-Set Threaded Inserts M3 | [Example](https://www.prusa3d.com/product/threaded-inserts-m3-standard-100-pcs/)
|
||||
|
||||
All in all would calculate around $100 for the neccessary parts
|
||||
|
||||
# Assembly Instructions for Lightning ATM
|
||||
|
||||
## 1. Connecting the Waveshare 1.54 inch Display to the ESP32
|
||||
|
||||
Begin by connecting the Waveshare display to the ESP32 using the following pin assignments:
|
||||
|
||||
| Display Pins | ESP32 GPIO |
|
||||
|--------------|------------|
|
||||
| Busy | 27 |
|
||||
| RST | 33 |
|
||||
| DC | 25 |
|
||||
| CS | 26 |
|
||||
| CLK | SCK = 18 |
|
||||
| DIN | MOSI = 23 |
|
||||
| GND | GND |
|
||||
| 3.3V | 3.3V |
|
||||
|
||||
## 2. Programming the Coin Acceptor
|
||||
|
||||
Ensure to adjust the voltage of the step-up converter before connecting the coin acceptor. Detailed programming instructions are available in the following guides:
|
||||
- [Coin Acceptor Programming Guide (English)](https://github.com/21isenough/LightningATM/blob/master/docs/guide/coin_validator.md)
|
||||
- [Coin Acceptor Programming Guide (German)](https://ereignishorizont.xyz/lightning-atm/)
|
||||
|
||||
## 3. Connecting the Coin Acceptor to the ESP32
|
||||
|
||||
- For the Coin <-> Pin 17 connection, use a cable as short as possible.
|
||||
- Short circuit the two pins below the switch on the coin acceptor with the MOSFET on GND IN and GND OUT.
|
||||
- Connect the MOSFET GND pin to the ESP32 GND and the PWM pin to the pin specified in `include/lightning_atm.h`.
|
||||
|
||||
| Periphery Pin | ESP32 GPIO |
|
||||
|--------------------------|------------|
|
||||
| Coin Acceptor 'Coin Pin' | 17 |
|
||||
| Mosfet PWM Pin | 16 |
|
||||
| Mosfet GND Pin | GND |
|
||||
|
||||
|
||||
## 4. Connecting the LED Button
|
||||
|
||||
Connect the LED Button to the ESP32 according to the pin assignments specified in `include/lightning_atm.h`:
|
||||
|
||||
| Periphery Pin | ESP32 GPIO |
|
||||
|---------------------------|------------|
|
||||
| Button LED Pin (+) | 21 |
|
||||
| Button LED Pin (-) | GND |
|
||||
| Button PIN 1 | 32 |
|
||||
| Button PIN 2 | GND |
|
||||
|
||||
For wiring inspiration and guidance, refer to [Lightning ATM Documentation](https://github.com/21isenough/LightningATM/tree/master/docs).
|
||||
|
||||
## 5. Circuit Diagram
|
||||
|
||||
Below are two possible wiring options. Depending on which ESP32 type and display / driver board you are using.
|
||||
|
||||
1. Standard ESP32 with separate display / driver board
|
||||
|
||||

|
||||
|
||||
2. Waveshare ESP32 with integrated driver board and separate display
|
||||
|
||||

|
||||
|
||||
## Setup software
|
||||
|
||||
1. Install VSCode and the PlattformIO IDE Extension.
|
||||
2. Clone this repository and open it as PlattformIO Project.
|
||||
3. Create an [LNbits](https://legend.lnbits.com/) wallet. Add the LNURLDevice extension and create a new LNURLDevice instance with ATM.
|
||||
|
||||
[](./assets/wallet_settings_01.png)
|
||||
|
||||
4. Copy the LNURLDevice Settings String and paste it into the code under “include/lighting_atm.h” under “USER ACTION”.
|
||||
|
||||
[](./assets/wallet_settings_02.png)
|
||||
|
||||
5. Flash the software on the esp32. You may have to disconnect the ESP32 from the step up converter before connecting it to the computer to prevent faults, or power it up with the power supply and use an usb isolator.
|
||||
|
||||
If you need help ask me on Nostr @npub1z9n5ktfjrlpyywds9t7ljekr9cm9jjnzs27h702te5fy8p2c4dgs5zvycf
|
||||
|
||||
If this software and guide provided value to you feel free to send some sats to x@lnaddress.com
|
||||
|
||||
|
||||
## Images
|
||||
|
||||
Standard ESP32
|
||||

|
||||

|
||||
|
||||
Waveshare ESP32
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
BIN
assets/Photos/ATM_Photo_1.jpg
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
assets/Photos/ATM_Photo_2.jpg
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
assets/Photos/ATM_Photo_3.jpg
Normal file
After Width: | Height: | Size: 2.7 MiB |
BIN
assets/Photos/ATM_Photo_4.jpg
Normal file
After Width: | Height: | Size: 2 MiB |
BIN
assets/WaveshareESP32_construction_001.jpg
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
assets/WaveshareESP32_construction_002.jpg
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
assets/WaveshareESP32_construction_003.jpg
Normal file
After Width: | Height: | Size: 145 KiB |
BIN
assets/WaveshareESP32_construction_004.jpg
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
assets/WaveshareESP32_construction_005.jpg
Normal file
After Width: | Height: | Size: 125 KiB |
2325
assets/offlineATM-837791.svg
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
assets/offlineATM-837791.vsdx
Normal file
1175
assets/offlineATM-WaveshareESP32-840538.svg
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
assets/offlineATM-WaveshareESP32-xxxxxx.png
Normal file
After Width: | Height: | Size: 965 KiB |
BIN
assets/offlineATM-xxxxxx.png
Normal file
After Width: | Height: | Size: 161 KiB |
BIN
assets/wallet_settings_01.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
assets/wallet_settings_01_thumb.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
assets/wallet_settings_02.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
assets/wallet_settings_02_thumb.png
Normal file
After Width: | Height: | Size: 60 KiB |
10
firmware/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
11
firmware/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"editor.defaultFormatter": "ms-vscode.cpptools",
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.formatOnSave": true,
|
||||
"files.autoSave": "onFocusChange",
|
||||
"[cpp]": {
|
||||
"editor.defaultFormatter": "ms-vscode.cpptools"
|
||||
},
|
||||
"cmake.configureOnOpen": false,
|
||||
"C_Cpp.formatting": "vcFormat"
|
||||
}
|
289
firmware/include/GxEPD2_display_selection_new_style.h
Normal file
|
@ -0,0 +1,289 @@
|
|||
// Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
|
||||
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
|
||||
//
|
||||
// Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
|
||||
//
|
||||
// Author: Jean-Marc Zingg
|
||||
//
|
||||
// Version: see library.properties
|
||||
//
|
||||
// Library: https://github.com/ZinggJM/GxEPD2
|
||||
|
||||
// Supporting Arduino Forum Topics:
|
||||
// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
|
||||
// Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0
|
||||
|
||||
// *** for Waveshare ESP32 Driver board *** //
|
||||
// mapping of Waveshare ESP32 Driver Board
|
||||
// BUSY -> 25, RST -> 26, DC -> 27, CS-> 15, CLK -> 13, DIN -> 14
|
||||
// uncomment next two lines for Waveshare ESP32 Driver Board
|
||||
#define USE_HSPI_FOR_EPD
|
||||
#define ENABLE_GxEPD2_GFX 0
|
||||
// *** end Waveshare ESP32 Driver board *** //
|
||||
|
||||
// NOTE: you may need to adapt or select for your wiring in the processor specific conditional compile sections below
|
||||
// select the display class (only one), matching the kind of display panel
|
||||
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
|
||||
// #define GxEPD2_DISPLAY_CLASS GxEPD2_3C
|
||||
// #define GxEPD2_DISPLAY_CLASS GxEPD2_4C
|
||||
// #define GxEPD2_DISPLAY_CLASS GxEPD2_7C
|
||||
|
||||
// select the display driver class (only one) for your panel
|
||||
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_150_BN // 1.54 inch Waveshare e-paper display
|
||||
// const String display_type = "GxEPD2_150_BN";
|
||||
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_B74 // Waveshare 2.13 inch e-paper display - Version 3
|
||||
// const String display_type = "GxEPD2_213_B74";
|
||||
|
||||
#define GxEPD2_DRIVER_CLASS GxEPD2_213_flex // Waveshare 2.13 inch e-paper display - (D) flex (yellow)
|
||||
const String display_type = "GxEPD2_213_flex";
|
||||
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_270 // Waveshare 264x176, 2.7inch E-Ink display - Version 1
|
||||
// const String display_type = "GxEPD2_270";
|
||||
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_270_GDEY027T91 // Waveshare 264x176, 2.7inch E-Ink display - Version 2
|
||||
// const String display_type = "GxEPD2_270_GDEY027T91";
|
||||
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154 // GDEP015OC1 200x200, IL3829, (WFC0000CZ07), no longer available
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154_D67 // GDEH0154D67 200x200, SSD1681, (HINK-E154A07-A1)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_102 // GDEW0102T4 80x128, UC8175, (WFT0102CZA2)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154_T8 // GDEW0154T8 152x152, UC8151 (IL0373), (WFT0154CZ17)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154_M09 // GDEW0154M09 200x200, JD79653A, (WFT0154CZB3)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154_M10 // GDEW0154M10 152x152, UC8151D, (WFT0154CZ17)
|
||||
/// #define GxEPD2_DRIVER_CLASS GxEPD2_154_GDEY0154D67 // GDEY0154D67 200x200, SSD1681, (FPC-B001 20.05.21)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213 // GDE0213B1 122x250, IL3895, (HINK-E0213-G01), phased out
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_B72 // GDEH0213B72 122x250, SSD1675A (IL3897), (HINK-E0213A22-A0 SLH1852)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_B73 // GDEH0213B73 122x250, SSD1675B, (HINK-E0213A22-A0 SLH1914)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_B74 // GDEM0213B74 122x250, SSD1680, FPC-7528B)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_flex // GDEW0213I5F 104x212, UC8151 (IL0373)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_M21 // GDEW0213M21 104x212, UC8151 (IL0373), (WFT0213CZ16)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_T5D // GDEW0213T5D 104x212, UC8151D, (WFT0213CZ16)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_BN // DEPG0213BN 122x250, SSD1680, (FPC-7528B), TTGO T5 V2.4.1, V2.3.1
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_GDEY0213B74 // GDEY0213B74 122x250, SSD1680, (FPC-A002 20.04.08)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_260 // GDEW026T0 152x296, UC8151 (IL0373), (WFT0154CZ17)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_260_M01 // GDEW026M01 152x296, UC8151 (IL0373), (WFT0260CZB2)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_266_BN // DEPG0266BN 152x296, SSD1680, (FPC7510), TTGO T5 V2.66, TTGO T5 V2.4.1
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_266_GDEY0266T90 // GDEY0266T90 152x296, SSD1680, (FPC-A003 HB)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290 // GDEH029A1 128x296, SSD1608 (IL3820), (E029A01-FPC-A1 SYX1553)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_T5 // GDEW029T5 128x296, UC8151 (IL0373), (WFT0290CZ10)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_T5D // GDEW029T5D 128x296, UC8151D, (WFT0290CZ10)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_I6FD // GDEW029I6FD 128x296, UC8151D, (WFT0290CZ10)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_T94 // GDEM029T94 128x296, SSD1680, (FPC-7519 rev.b)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_T94_V2 // GDEM029T94 128x296, SSD1680, (FPC-7519 rev.b), Waveshare 2.9" V2 variant
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_BS // DEPG0290BS 128x296, SSD1680, (FPC-7519 rev.b)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_M06 // GDEW029M06 128x296, UC8151D, (WFT0290CZ10)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_GDEY029T94 // GDEY029T94 128x296, SSD1680, (FPC-A005 20.06.15)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_371 // GDEW0371W7 240x416, UC8171 (IL0324), (missing)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_370_TC1 // ED037TC1 280x480, SSD1677, (ICA-FU-20 ichia 2029), Waveshare 3.7"
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_420 // GDEW042T2 400x300, UC8176 (IL0398), (WFT042CZ15)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_420_M01 // GDEW042M01 400x300, UC8176 (IL0398), (WFT042CZ15)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_420_GDEY042T91 // GDEY042T91 400x300, SSD1683 (no inking)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_583 // GDEW0583T7 600x448, UC8159c (IL0371), (missing)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_583_T8 // GDEW0583T8 648x480, EK79655 (GD7965), (WFT0583CZ61)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_583_GDEQ0583T31 // GDEQ0583T31 648x480, UC8179, (P583010-MF1-B)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750 // GDEW075T8 640x384, UC8159c (IL0371), (WF0583CZ09)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750_T7 // GDEW075T7 800x480, EK79655 (GD7965), (WFT0583CZ61)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750_YT7 // GDEY075T7 800x480, UC8179 (GD7965), (FPC-C001 20.8.20)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_1160_T91 // GDEH116T91 960x640, SSD1677, (none or hidden)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_1248 // GDEW1248T3 1304x984, UC8179, (WFT1248BZ23,WFT1248BZ24)
|
||||
// 3-color e-papers
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154c // GDEW0154Z04 200x200, IL0376F, (WFT0000CZ04), no longer available
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_154_Z90c // GDEH0154Z90 200x200, SSD1681, (HINK-E154A07-A1)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213c // GDEW0213Z16 104x212, UC8151 (IL0373), (WFT0213CZ16)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_Z19c // GDEH0213Z19 104x212, UC8151D, (HINK-E0213A20-A2 2020-11-19)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_213_Z98c // GDEY0213Z98 122x250, SSD1680, (FPC-A002 20.04.08)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_266c // GDEY0266Z90 152x296, SSD1680, (FPC-7510)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_270c // GDEW027C44 176x264, IL91874, (WFI0190CZ22)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290c // GDEW029Z10 128x296, UC8151 (IL0373), (WFT0290CZ10)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_Z13c // GDEH029Z13 128x296, UC8151D, (HINK-E029A10-A3 20160809)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_290_C90c // GDEM029C90 128x296, SSD1680, (FPC-7519 rev.b)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_420c // GDEW042Z15 400x300, UC8176 (IL0398), (WFT0420CZ15)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_420c_Z21 // GDEQ042Z21 400x300, UC8276, (hidden)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_583c // GDEW0583Z21 600x448, UC8159c (IL0371), (missing)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_583c_Z83 // GDEW0583Z83 648x480, EK79655 (GD7965), (WFT0583CZ61)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750c // GDEW075Z09 640x384, UC8159c (IL0371), (WF0583CZ09)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z08 // GDEW075Z08 800x480, EK79655 (GD7965), (WFT0583CZ61)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z90 // GDEH075Z90 880x528, SSD1677, (HINK-E075A07-A0)
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_1248c // GDEY1248Z51 1304x984, UC8179, (WFT1248BZ23,WFT1248BZ24)
|
||||
// 4-color e-paper
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_437c // Waveshare 4.37" 4-color
|
||||
// 7-color e-paper
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_565c // Waveshare 5.65" 7-color
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_730c_GDEY073D46 // GDEY073D46 800x480 7-color, (N-FPC-001 2021.11.26)
|
||||
// grey levels parallel IF e-papers on Waveshare e-Paper IT8951 Driver HAT
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_it60 // ED060SCT 800x600
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_it60_1448x1072 // ED060KC1 1448x1072
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_it78_1872x1404 // ED078KC2 1872x1404
|
||||
// #define GxEPD2_DRIVER_CLASS GxEPD2_it103_1872x1404 // ES103TC1 1872x1404
|
||||
|
||||
// SS is usually used for CS. define here for easy change
|
||||
#ifndef EPD_CS
|
||||
#define EPD_CS SS
|
||||
#endif
|
||||
|
||||
#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
|
||||
|
||||
// somehow there should be an easier way to do this
|
||||
#define GxEPD2_BW_IS_GxEPD2_BW true
|
||||
#define GxEPD2_3C_IS_GxEPD2_3C true
|
||||
#define GxEPD2_4C_IS_GxEPD2_4C true
|
||||
#define GxEPD2_7C_IS_GxEPD2_7C true
|
||||
#define GxEPD2_1248_IS_GxEPD2_1248 true
|
||||
#define GxEPD2_1248c_IS_GxEPD2_1248c true
|
||||
#define IS_GxEPD(c, x) (c##x)
|
||||
#define IS_GxEPD2_BW(x) IS_GxEPD(GxEPD2_BW_IS_, x)
|
||||
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)
|
||||
#define IS_GxEPD2_4C(x) IS_GxEPD(GxEPD2_4C_IS_, x)
|
||||
#define IS_GxEPD2_7C(x) IS_GxEPD(GxEPD2_7C_IS_, x)
|
||||
#define IS_GxEPD2_1248(x) IS_GxEPD(GxEPD2_1248_IS_, x)
|
||||
#define IS_GxEPD2_1248c(x) IS_GxEPD(GxEPD2_1248c_IS_, x)
|
||||
|
||||
#include "GxEPD2_selection_check.h"
|
||||
|
||||
#if defined(ESP8266)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE (81920ul - 34000ul - 5000ul) // ~34000 base use, change 5000 to your application use
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
|
||||
// mapping of Waveshare e-Paper ESP8266 Driver Board, new version
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=15*/ EPD_CS, /*DC=4*/ 4, /*RST=2*/ 2, /*BUSY=5*/ 5));
|
||||
// mapping of Waveshare e-Paper ESP8266 Driver Board, old version
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=15*/ EPD_CS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 65536ul // e.g.
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
#if !IS_GxEPD2_1248(GxEPD2_DRIVER_CLASS) && !IS_GxEPD2_1248c(GxEPD2_DRIVER_CLASS)
|
||||
#if defined(ARDUINO_LOLIN_D32_PRO)
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/0, /*RST=*/2, /*BUSY=*/15)); // my LOLIN_D32_PRO proto board
|
||||
#else
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // my suggested wiring and proto board
|
||||
// *** uncomment next line for standard ESP32 *** //
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ 26, /*DC=*/25, /*RST=*/33, /*BUSY=*/27)); // esp32 NodeMCU following AZ Delivery guide pinout
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ 5, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // LILYGO_T5_V2.4.1
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 19, /*RST=*/ 4, /*BUSY=*/ 34)); // LILYGO® TTGO T5 2.66
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 2, /*RST=*/ 0, /*BUSY=*/ 4)); // e.g. TTGO T8 ESP32-WROVER
|
||||
// *** uncomment next line for Waveshare ESP32 Driver board *** //
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ 15, /*DC=*/ 27, /*RST=*/ 26, /*BUSY=*/ 25)); // Waveshare ESP32 Driver Board
|
||||
#endif
|
||||
#else // GxEPD2_1248 or GxEPD2_1248c
|
||||
// Waveshare 12.48 b/w or b/w/r SPI display board and frame or Good Display 12.48 b/w panel GDEW1248T3 or b/w/r panel GDEY1248Z51
|
||||
// general constructor for use with all parameters, e.g. for Waveshare ESP32 driver board mounted on connection board
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*sck=*/13, /*miso=*/12, /*mosi=*/14,
|
||||
/*cs_m1=*/23, /*cs_s1=*/22, /*cs_m2=*/16, /*cs_s2=*/19,
|
||||
/*dc1=*/25, /*dc2=*/17, /*rst1=*/33, /*rst2=*/5,
|
||||
/*busy_m1=*/32, /*busy_s1=*/26, /*busy_m2=*/18, /*busy_s2=*/4));
|
||||
#endif
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
// can't use package "STMF1 Boards (STM32Duino.com)" (Roger Clark) anymore with Adafruit_GFX, use "STM32 Boards (selected from submenu)" (STMicroelectronics)
|
||||
#if defined(ARDUINO_ARCH_STM32)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 15000ul // ~15k is a good compromise
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=PA4*/ EPD_CS, /*DC=*/PA3, /*RST=*/PA2, /*BUSY=*/PA1));
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
#if defined(__AVR)
|
||||
#if defined(ARDUINO_AVR_MEGA2560) // Note: SS is on 53 on MEGA
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 5000 // e.g. full height for 200x200
|
||||
#else // Note: SS is on 10 on UNO, NANO
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 800 //
|
||||
#endif
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/EPD_CS, /*DC=*/8, /*RST=*/9, /*BUSY=*/7));
|
||||
// for Arduino Micro or Arduino Leonardo with CS on 10 on my proto boards (SS would be 17) uncomment instead:
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ 10, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 32768ul // e.g., up to 96k
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=10*/ EPD_CS, /*DC=*/8, /*RST=*/9, /*BUSY=*/7));
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAMD)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 15000ul // ~15k is a good compromise
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/7, /*RST=*/6, /*BUSY=*/5));
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1)); // my Seed XIOA0
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 3, /*DC=*/ 2, /*RST=*/ 1, /*BUSY=*/ 0)); // my other Seed XIOA0
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040)
|
||||
#define MAX_DISPLAY_BUFFER_SIZE 131072ul // e.g. half of available ram
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) || IS_GxEPD2_4C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
|
||||
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
|
||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||
#endif
|
||||
#if defined(ARDUINO_NANO_RP2040_CONNECT)
|
||||
// adapt the constructor parameters to your wiring
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/EPD_CS, /*DC=*/8, /*RST=*/9, /*BUSY=*/7));
|
||||
#endif
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO)
|
||||
// adapt the constructor parameters to your wiring
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7)); // my proto board
|
||||
// mapping of GoodDisplay DESPI-PICO. NOTE: uses alternate HW SPI pins!
|
||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/3, /*DC=*/2, /*RST=*/1, /*BUSY=*/0)); // DESPI-PICO
|
||||
// GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ 3, /*DC=*/ 2, /*RST=*/ 11, /*BUSY=*/ 10)); // DESPI-PICO modified
|
||||
#endif
|
||||
#undef MAX_DISPLAY_BUFFER_SIZE
|
||||
#undef MAX_HEIGHT
|
||||
#endif
|
||||
|
||||
#endif
|
106
firmware/include/GxEPD2_selection_check.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
|
||||
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
|
||||
//
|
||||
// Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
|
||||
//
|
||||
// Author: Jean-Marc Zingg
|
||||
//
|
||||
// Version: see library.properties
|
||||
//
|
||||
// Library: https://github.com/ZinggJM/GxEPD2
|
||||
|
||||
// Supporting Arduino Forum Topics:
|
||||
// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
|
||||
// Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0
|
||||
|
||||
#define GxEPD2_102_IS_BW true
|
||||
#define GxEPD2_150_BN_IS_BW true
|
||||
#define GxEPD2_154_IS_BW true
|
||||
#define GxEPD2_154_D67_IS_BW true
|
||||
#define GxEPD2_154_T8_IS_BW true
|
||||
#define GxEPD2_154_M09_IS_BW true
|
||||
#define GxEPD2_154_M10_IS_BW true
|
||||
#define GxEPD2_154_GDEY0154D67_IS_BW true
|
||||
#define GxEPD2_213_IS_BW true
|
||||
#define GxEPD2_213_B72_IS_BW true
|
||||
#define GxEPD2_213_B73_IS_BW true
|
||||
#define GxEPD2_213_B74_IS_BW true
|
||||
#define GxEPD2_213_flex_IS_BW true
|
||||
#define GxEPD2_213_M21_IS_BW true
|
||||
#define GxEPD2_213_T5D_IS_BW true
|
||||
#define GxEPD2_213_BN_IS_BW true
|
||||
#define GxEPD2_213_GDEY0213B74_IS_BW true
|
||||
#define GxEPD2_260_IS_BW true
|
||||
#define GxEPD2_260_M01_IS_BW true
|
||||
#define GxEPD2_266_BN_IS_BW true
|
||||
#define GxEPD2_266_GDEY0266T90_IS_BW true
|
||||
#define GxEPD2_270_IS_BW true
|
||||
#define GxEPD2_270_GDEY027T91_IS_BW true
|
||||
#define GxEPD2_290_IS_BW true
|
||||
#define GxEPD2_290_T5_IS_BW true
|
||||
#define GxEPD2_290_T5D_IS_BW true
|
||||
#define GxEPD2_290_I6FD_IS_BW true
|
||||
#define GxEPD2_290_T94_IS_BW true
|
||||
#define GxEPD2_290_T94_V2_IS_BW true
|
||||
#define GxEPD2_290_BS_IS_BW true
|
||||
#define GxEPD2_290_M06_IS_BW true
|
||||
#define GxEPD2_290_GDEY029T94_IS_BW true
|
||||
#define GxEPD2_371_IS_BW true
|
||||
#define GxEPD2_370_TC1_IS_BW true
|
||||
#define GxEPD2_420_IS_BW true
|
||||
#define GxEPD2_420_M01_IS_BW true
|
||||
#define GxEPD2_420_GDEY042T91_IS_BW true
|
||||
#define GxEPD2_583_IS_BW true
|
||||
#define GxEPD2_583_T8_IS_BW true
|
||||
#define GxEPD2_583_GDEQ0583T31_IS_BW true
|
||||
#define GxEPD2_750_IS_BW true
|
||||
#define GxEPD2_750_T7_IS_BW true
|
||||
#define GxEPD2_750_YT7_IS_BW true
|
||||
#define GxEPD2_1160_T91_IS_BW true
|
||||
#define GxEPD2_1248_IS_BW true
|
||||
#define GxEPD2_it60_IS_BW true
|
||||
#define GxEPD2_it60_1448x1072_IS_BW true
|
||||
#define GxEPD2_it78_1872x1404_IS_BW true
|
||||
#define GxEPD2_it103_1872x1404_IS_BW true
|
||||
// 3-color e-papers
|
||||
#define GxEPD2_154c_IS_3C true
|
||||
#define GxEPD2_154_Z90c_IS_3C true
|
||||
#define GxEPD2_213c_IS_3C true
|
||||
#define GxEPD2_213_Z19c_IS_3C true
|
||||
#define GxEPD2_213_Z98c_IS_3C true
|
||||
#define GxEPD2_266c_IS_3C true
|
||||
#define GxEPD2_270c_IS_3C true
|
||||
#define GxEPD2_290c_IS_3C true
|
||||
#define GxEPD2_290_Z13c_IS_3C true
|
||||
#define GxEPD2_290_C90c_IS_3C true
|
||||
#define GxEPD2_420c_IS_3C true
|
||||
#define GxEPD2_420c_Z21_IS_3C true
|
||||
#define GxEPD2_583c_IS_3C true
|
||||
#define GxEPD2_583c_Z83_IS_3C true
|
||||
#define GxEPD2_750c_IS_3C true
|
||||
#define GxEPD2_750c_Z08_IS_3C true
|
||||
#define GxEPD2_750c_Z90_IS_3C true
|
||||
#define GxEPD2_1248c_IS_3C true
|
||||
// 4-color e-paper
|
||||
#define GxEPD2_437c_IS_4C true
|
||||
// 7-color e-paper
|
||||
#define GxEPD2_565c_IS_7C true
|
||||
#define GxEPD2_730c_GDEY073D46_IS_7C true
|
||||
|
||||
#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
|
||||
#define IS_GxEPD2_DRIVER(c, x) (c##x)
|
||||
#define IS_GxEPD2_DRIVER_BW(x) IS_GxEPD2_DRIVER(x, _IS_BW)
|
||||
#define IS_GxEPD2_DRIVER_3C(x) IS_GxEPD2_DRIVER(x, _IS_3C)
|
||||
#define IS_GxEPD2_DRIVER_4C(x) IS_GxEPD2_DRIVER(x, _IS_4C)
|
||||
#define IS_GxEPD2_DRIVER_7C(x) IS_GxEPD2_DRIVER(x, _IS_7C)
|
||||
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS) && IS_GxEPD2_DRIVER_3C(GxEPD2_DRIVER_CLASS)
|
||||
#error "GxEPD2_BW used with 3-color driver class"
|
||||
#endif
|
||||
#if IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) && IS_GxEPD2_DRIVER_BW(GxEPD2_DRIVER_CLASS)
|
||||
#error "GxEPD2_3C used with b/w driver class"
|
||||
#endif
|
||||
#if !IS_GxEPD2_DRIVER_BW(GxEPD2_DRIVER_CLASS) && !IS_GxEPD2_DRIVER_3C(GxEPD2_DRIVER_CLASS) && !IS_GxEPD2_DRIVER_4C(GxEPD2_DRIVER_CLASS) && !IS_GxEPD2_DRIVER_7C(GxEPD2_DRIVER_CLASS)
|
||||
#error "neither BW nor 3C nor 4C nor 7C kind defined for driver class (error in GxEPD2_selection_check.h)"
|
||||
#endif
|
||||
|
||||
#endif
|
83
firmware/include/GxEPD2_wiring_examples.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
|
||||
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
|
||||
//
|
||||
// Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
|
||||
//
|
||||
// Author: Jean-Marc Zingg
|
||||
//
|
||||
// Version: see library.properties
|
||||
//
|
||||
// Library: https://github.com/ZinggJM/GxEPD2
|
||||
|
||||
// Supporting Arduino Forum Topics:
|
||||
// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
|
||||
// Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0
|
||||
|
||||
// mapping suggestion from Waveshare SPI e-Paper to Wemos D1 mini
|
||||
// BUSY -> D2, RST -> D4, DC -> D3, CS -> D8, CLK -> D5, DIN -> D7, GND -> GND, 3.3V -> 3.3V
|
||||
// NOTE: connect 3.3k pull-down from D8 to GND if your board or shield has level converters
|
||||
// NOTE for ESP8266: using SS (GPIO15) for CS may cause boot mode problems, use different pin in case, or 4k7 pull-down
|
||||
// NOTE: connect 1k pull-up from D4 (RST) to 3.3V if your board or shield has the "clever" reset circuit, or use a different pin
|
||||
|
||||
// mapping suggestion from Waveshare SPI e-Paper to generic ESP8266
|
||||
// BUSY -> GPIO4, RST -> GPIO2, DC -> GPIO0, CS -> GPIO15, CLK -> GPIO14, DIN -> GPIO13, GND -> GND, 3.3V -> 3.3V
|
||||
// NOTE: connect 3.3k pull-down from GPIO15 to GND if your board or shield has level converters
|
||||
// NOTE for ESP8266: using SS (GPIO15) for CS may cause boot mode problems, use different pin in case, or 3.3k pull-down
|
||||
// NOTE: connect 1k pull-up from GPIO2 (RST) to 3.3V if your board or shield has the "clever" reset circuit, or use a different pin
|
||||
|
||||
// mapping of Waveshare e-Paper ESP8266 Driver Board, new version
|
||||
// BUSY -> GPIO5, RST -> GPIO2, DC -> GPIO4, CS -> GPIO15, CLK -> GPIO14, DIN -> GPIO13, GND -> GND, 3.3V -> 3.3V
|
||||
// NOTE for ESP8266: using SS (GPIO15) for CS may cause boot mode problems, add a 3.3k pull-down in case
|
||||
// the e-Paper ESP8266 Driver Board should have no boot mode issue, as it doesn't use level converters
|
||||
|
||||
// mapping of Waveshare e-Paper ESP8266 Driver Board, old version
|
||||
// BUSY -> GPIO16, RST -> GPIO5, DC -> GPIO4, CS -> GPIO15, CLK -> GPIO14, DIN -> GPIO13, GND -> GND, 3.3V -> 3.3V
|
||||
// NOTE for ESP8266: using SS (GPIO15) for CS may cause boot mode problems, add a 3.3k pull-down in case
|
||||
// the e-Paper ESP8266 Driver Board should have no boot mode issue, as it doesn't use level converters
|
||||
|
||||
// mapping suggestion for ESP32, e.g. LOLIN32, see .../variants/.../pins_arduino.h for your board
|
||||
// NOTE: there are variants with different pins for SPI ! CHECK SPI PINS OF YOUR BOARD
|
||||
// BUSY -> 4, RST -> 16, DC -> 17, CS -> SS(5), CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V
|
||||
|
||||
// mapping of Waveshare ESP32 Driver Board
|
||||
// BUSY -> 25, RST -> 26, DC -> 27, CS-> 15, CLK -> 13, DIN -> 14
|
||||
// NOTE: this board uses "unusual" SPI pins and requires re-mapping of HW SPI to these pins in SPIClass
|
||||
// see example GxEPD2_WS_ESP32_Driver.ino, it shows how this can be done easily
|
||||
|
||||
// mapping suggestion for ESP32, e.g. LOLIN32 D32 PRO
|
||||
// BUSY -> 15, RST -> 2, DC -> 0, CS -> 5, CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V
|
||||
// note: use explicit value for CS, as SS is re-defined to TF_CS(4) in pins_arduino.h for Board: "LOLIN D32 PRO"
|
||||
|
||||
// mapping suggestion for ESP32, e.g. TTGO T8 ESP32-WROVER
|
||||
// BUSY -> 4, RST -> 0, DC -> 2, CS -> SS(5), CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V
|
||||
// for use with Board: "ESP32 Dev Module":
|
||||
|
||||
// new mapping suggestion for STM32F1, e.g. STM32F103C8T6 "BluePill"
|
||||
// BUSY -> A1, RST -> A2, DC -> A3, CS-> A4, CLK -> A5, DIN -> A7
|
||||
|
||||
// mapping suggestion for AVR, UNO, NANO etc.
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 10, CLK -> 13, DIN -> 11
|
||||
|
||||
// mapping suggestion for AVR, Arduino Micro, Leonardo
|
||||
// note: on Leonardo board HW SPI pins are on 6-pin ICSP header
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 10, CLK -> 15, DIN -> 16
|
||||
|
||||
// mapping of Waveshare Universal e-Paper Raw Panel Driver Shield for Arduino / NUCLEO
|
||||
// BUSY -> 7, RST -> 8, DC -> 9, CS-> 10, CLK -> 13, DIN -> 11
|
||||
|
||||
// mapping suggestion for Arduino MEGA
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 53, CLK -> 52, DIN -> 51
|
||||
|
||||
// mapping suggestion for Arduino DUE, note: pin 77 is on board pin 10, SS is 10
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 10, CLK -> 76, DIN -> 75
|
||||
// SPI pins are on 6 pin 2x3 SPI header, no SS on SPI header!
|
||||
|
||||
// mapping suggestion for Arduino MKR1000 or MKRZERO
|
||||
// note: can't use SS on MKR1000: is defined as 24, should be 4
|
||||
// BUSY -> 5, RST -> 6, DC -> 7, CS-> 4, CLK -> 9, DIN -> 8
|
||||
|
||||
// mapping suggestion for Arduino Nano RP2040 Connect
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 10, CLK -> 13, DIN -> 11
|
||||
|
||||
// mapping suggestion for Raspberry Pi Pico RP2040
|
||||
// BUSY -> 7, RST -> 9, DC -> 8, CS-> 5, CLK -> 2, DIN -> 3
|
39
firmware/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
135
firmware/include/lightning_atm.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
#ifndef LIGHTNING_ATM_H
|
||||
#define LIGHTNING_ATM_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <GxEPD2_BW.h>
|
||||
#include <Fonts/FreeMonoBold9pt7b.h>
|
||||
#include "qrcode.h"
|
||||
#include "Bitcoin.h"
|
||||
#include <stdlib.h>
|
||||
#include <Hash.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// ########################################
|
||||
// ########### USER ACTION ###########
|
||||
// ########################################
|
||||
// Generate and copy in LNbits with the LNURLDevice extension the string for the ATM and paste it here:
|
||||
const String lnurlDeviceString = "https://legend.lnbits.com/lnurldevice/api/v1/lnurl/idexample,keyexample,EUR";
|
||||
// #################### EXAMPLE: https://legend.lnbits.com/lnurldevice/api/v1/lnurl/idexample,keyexample,EUR
|
||||
// ########################################
|
||||
// ########################################
|
||||
// ########################################
|
||||
|
||||
// select the display class and display driver class in the following file (new style):
|
||||
// 1.54 inch Waveshare e-paper display is "GxEPD2_DRIVER_CLASS GxEPD2_150_BN"
|
||||
// Waveshare 264x176, 2.7inch E-Ink display - Version 1 = "GxEPD2_DRIVER_CLASS GxEPD2_270"
|
||||
// Waveshare 264x176, 2.7inch E-Ink display - Version 2 = "GxEPD2_DRIVER_CLASS GxEPD2_270_GDEY027T91"
|
||||
// Waveshare 2.13 inch e-paper display version 3 is "GxEPD2_DRIVER_CLASS GxEPD2_213_B74"
|
||||
// Waveshare 2.13 inch e-paper display (D) flex (yellow) is "GxEPD2_DRIVER_CLASS GxEPD2_213_flex"
|
||||
// use search to find the correct line, and uncomment the other display drivers in this header file:
|
||||
#include "GxEPD2_display_selection_new_style.h"
|
||||
|
||||
// OTHER OPTIONS:
|
||||
|
||||
// Activate for debugging over Serial (1), deactivate in production use (0)
|
||||
#define DEBUG_MODE 0
|
||||
|
||||
#define COIN_PIN 17
|
||||
#define PULSE_TIMEOUT 200
|
||||
#define LED_BUTTON_PIN 21 // old: 13 | new: 21
|
||||
#define 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.
|
||||
|
||||
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;
|
||||
|
||||
// Bitcoin Logo
|
||||
const unsigned char bitcoin_logo[] PROGMEM = {
|
||||
// 'bitcoin64, 64x64px
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xfc, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||
0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
|
||||
0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x7f, 0xff, 0xf0, 0x00,
|
||||
0x00, 0x1f, 0xff, 0xfc, 0x63, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x63, 0xff, 0xfc, 0x00,
|
||||
0x00, 0x7f, 0xfe, 0x38, 0xe3, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0xe3, 0xff, 0xfe, 0x00,
|
||||
0x00, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0x00,
|
||||
0x00, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0x80,
|
||||
0x01, 0xff, 0xff, 0xc1, 0xe0, 0x3f, 0xff, 0x80, 0x01, 0xff, 0xff, 0x81, 0xf8, 0x1f, 0xff, 0x80,
|
||||
0x03, 0xff, 0xff, 0x83, 0xf8, 0x1f, 0xff, 0xc0, 0x03, 0xff, 0xff, 0x83, 0xf8, 0x1f, 0xff, 0xc0,
|
||||
0x03, 0xff, 0xff, 0x83, 0xf8, 0x1f, 0xff, 0xc0, 0x03, 0xff, 0xff, 0x01, 0xf0, 0x1f, 0xff, 0xc0,
|
||||
0x03, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x03, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xc0,
|
||||
0x03, 0xff, 0xff, 0x06, 0x00, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xfe, 0x07, 0xc0, 0x7f, 0xff, 0xc0,
|
||||
0x03, 0xff, 0xfe, 0x0f, 0xe0, 0x3f, 0xff, 0xc0, 0x03, 0xff, 0xfe, 0x0f, 0xf0, 0x3f, 0xff, 0xc0,
|
||||
0x03, 0xff, 0xec, 0x0f, 0xf0, 0x3f, 0xff, 0xc0, 0x03, 0xff, 0xe0, 0x0f, 0xf0, 0x3f, 0xff, 0xc0,
|
||||
0x01, 0xff, 0xc0, 0x0f, 0xf0, 0x3f, 0xff, 0x80, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0x80,
|
||||
0x01, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0x00,
|
||||
0x00, 0xff, 0xfe, 0x30, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x38, 0xc7, 0xff, 0xff, 0x00,
|
||||
0x00, 0x7f, 0xfe, 0x31, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xfc, 0x31, 0xff, 0xff, 0xfe, 0x00,
|
||||
0x00, 0x3f, 0xff, 0xf1, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x1f, 0xff, 0xf1, 0xff, 0xff, 0xf8, 0x00,
|
||||
0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00,
|
||||
0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
// 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
|
19
firmware/platformio.ini
Normal file
|
@ -0,0 +1,19 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 9600
|
||||
lib_deps =
|
||||
zinggjm/GxEPD2@^1.5.2
|
||||
ricmoo/QRCode@^0.0.1
|
||||
stepansnigirev/uBitcoin@^0.2.0
|
875
firmware/src/main.cpp
Normal file
|
@ -0,0 +1,875 @@
|
|||
#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();
|
||||
}
|
5
telegram_notification_bot/atmbot_python/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
This is a simple telegram bot to notify you when the atm wallet balance is too low, or somebody did use it.
|
||||
|
||||
1. Set the according api keys in the main.py file.
|
||||
2. Install python and the requests module (pip install requests)
|
||||
3. Run the bot with 'python3 main.py' on any server or 24/7 online computer
|
54
telegram_notification_bot/atmbot_python/main.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import requests
|
||||
from time import sleep
|
||||
|
||||
url = "https://legend.lnbits.com/api/v1/wallet" # your lnbits instance URL
|
||||
api_key = "ABCDEFG" # your lnbits wallet api key (not admin!)
|
||||
|
||||
bot_token = '123123123:12312jn3123123kjn123f' # telegram bot token, create bot with @botfather
|
||||
chat_id = '12312313123' # your telegram chat id, contact a chat id bot to get it
|
||||
|
||||
min_balance = 1000 # minimum balance at which you want to get notified
|
||||
refresh_interval = 5600 # refresh interval in which tha balances are fetched in seconds
|
||||
|
||||
|
||||
def get_wallet_balance():
|
||||
headers = {
|
||||
"X-Api-Key": api_key
|
||||
}
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data["balance"]
|
||||
else:
|
||||
return int(-1)
|
||||
|
||||
def send_telegram_message(token, chat_id, message):
|
||||
url = f'https://api.telegram.org/bot{token}/sendMessage'
|
||||
data = {'chat_id': chat_id, 'text': message}
|
||||
response = requests.post(url, data=data)
|
||||
return response.json()
|
||||
|
||||
def bot():
|
||||
previous_balance = 0
|
||||
while (True):
|
||||
balance = get_wallet_balance()
|
||||
if balance == -1:
|
||||
print("Balance check failed.")
|
||||
sleep(3600)
|
||||
continue
|
||||
else:
|
||||
balance = int(balance/1000)
|
||||
difference = previous_balance - balance
|
||||
if difference > 1:
|
||||
message = f"{difference} Sats have been withdrawn!"
|
||||
send_telegram_message(bot_token, chat_id, message)
|
||||
elif balance < min_balance:
|
||||
message = f"Only {balance} Sats left in ATM, refill!"
|
||||
send_telegram_message(bot_token, chat_id, message)
|
||||
previous_balance = balance
|
||||
sleep(refresh_interval)
|
||||
|
||||
bot()
|
||||
|
||||
|
6
telegram_notification_bot/atmbot_rust/.env.example
Normal file
|
@ -0,0 +1,6 @@
|
|||
LNBITS_URL=
|
||||
MIN_BALANCE=30000
|
||||
REFRESH_INTERVAL=300
|
||||
LNBITS_API_KEY=
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
TELEGRAM_CHAT_ID=
|
1305
telegram_notification_bot/atmbot_rust/Cargo.lock
generated
Normal file
18
telegram_notification_bot/atmbot_rust/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "atmbot_rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dotenv = "0.15.0"
|
||||
reqwest = { version = "0.12.5", features = ["blocking", "json"] }
|
||||
serde_json = "1.0.120"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
debug = false
|
6
telegram_notification_bot/atmbot_rust/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
This is a simple telegram bot to notify you when the atm wallet balance is too low, or somebody did use it.
|
||||
|
||||
1. Set the according api keys in the .env file.
|
||||
2. Install rust https://www.rust-lang.org/tools/install
|
||||
3. Compile the bot with cargo build --release
|
||||
4. Run the bot, it is located in ./target/release/atmbot
|
82
telegram_notification_bot/atmbot_rust/src/main.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use dotenv::dotenv;
|
||||
use reqwest::blocking::Client;
|
||||
use reqwest::header::{HeaderMap, HeaderValue};
|
||||
use serde_json::Value;
|
||||
use std::env;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dotenv().ok();
|
||||
|
||||
let url = env::var("LNBITS_URL").expect("URL not set in .env file");
|
||||
let min_balance: i64 = env::var("MIN_BALANCE")
|
||||
.expect("MIN_BALANCE not set in .env file")
|
||||
.parse()?;
|
||||
let refresh_interval: u64 = env::var("REFRESH_INTERVAL")
|
||||
.expect("REFRESH_INTERVAL not set in .env file")
|
||||
.parse()?;
|
||||
let api_key = env::var("LNBITS_API_KEY").expect("LNBITS_API_KEY not set in .env file");
|
||||
let bot_token =
|
||||
env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set in .env file");
|
||||
let chat_id = env::var("TELEGRAM_CHAT_ID").expect("TELEGRAM_CHAT_ID not set in .env file");
|
||||
|
||||
let mut previous_balance: i64 = 0;
|
||||
|
||||
loop {
|
||||
match get_wallet_balance(&api_key, &url) {
|
||||
Ok(balance) => {
|
||||
let balance = balance / 1000;
|
||||
let difference = previous_balance - balance;
|
||||
|
||||
if difference > 1 {
|
||||
let message = format!("{} Sats have been withdrawn!", difference);
|
||||
send_telegram_message(&bot_token, &chat_id, &message)?;
|
||||
} else if balance < min_balance {
|
||||
let message = format!("Only {} Sats left in ATM, refill!", balance);
|
||||
send_telegram_message(&bot_token, &chat_id, &message)?;
|
||||
}
|
||||
|
||||
previous_balance = balance;
|
||||
}
|
||||
Err(_) => {
|
||||
println!("Balance check failed.");
|
||||
sleep(Duration::from_secs(3600));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(refresh_interval));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_wallet_balance(api_key: &str, url: &str) -> Result<i64, Box<dyn std::error::Error>> {
|
||||
let client = Client::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("X-Api-Key", HeaderValue::from_str(api_key)?);
|
||||
|
||||
let response = client.get(url).headers(headers).send()?;
|
||||
|
||||
if response.status().is_success() {
|
||||
let data: Value = response.json()?;
|
||||
Ok(data["balance"]
|
||||
.as_i64()
|
||||
.expect("Failed to unwrap balance from returned JSON"))
|
||||
} else {
|
||||
Err(format!("Failed to get balance, status code: {}", response.status()).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn send_telegram_message(
|
||||
token: &str,
|
||||
chat_id: &str,
|
||||
message: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let telegram_url: String = format!("https://api.telegram.org/bot{}/sendMessage", token);
|
||||
let params = [("chat_id", chat_id), ("text", message)];
|
||||
|
||||
let client = Client::new();
|
||||
client.post(telegram_url).form(¶ms).send()?;
|
||||
|
||||
Ok(())
|
||||
}
|