Finally got the code split up into multiple files for organization

This commit is contained in:
Dionysus 2024-06-07 23:58:03 -04:00
parent 8950faf44a
commit a530492937
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
16 changed files with 1379 additions and 1349 deletions

694
src/Display.cpp Normal file
View File

@ -0,0 +1,694 @@
#include "Display.h"
#include "Storage.h"
#include "Utilities.h"
#include "Speaker.h"
#include "pins.h"
#include "bootScreen.h"
#include "IRC.h"
// External variables definitions
bool infoScreen = false;
bool configScreen = false;
bool screenOn = true;
const char* channel = "#comms";
unsigned long infoScreenStartTime = 0;
unsigned long configScreenStartTime = 0;
unsigned long lastStatusUpdateTime = 0;
unsigned long lastActivityTime = 0;
String inputBuffer = "";
std::vector<String> lines;
std::vector<bool> mentions;
std::map<String, uint32_t> nickColors;
TFT_eSPI tft = TFT_eSPI();
void addLine(String senderNick, String message, String type, bool mention, uint16_t errorColor, uint16_t reasonColor) {
if (type != "error" && nickColors.find(senderNick) == nickColors.end())
nickColors[senderNick] = generateRandomColor();
String formattedMessage;
if (type == "join") {
formattedMessage = "JOIN " + senderNick + " has joined " + String(channel);
} else if (type == "part") {
formattedMessage = "PART " + senderNick + " has EMO-QUIT " + String(channel);
} else if (type == "quit") {
formattedMessage = "QUIT " + senderNick;
} else if (type == "nick") {
int arrowPos = message.indexOf(" -> ");
String oldNick = senderNick;
String newNick = message.substring(arrowPos + 4);
if (nickColors.find(newNick) == nickColors.end()) {
nickColors[newNick] = generateRandomColor();
}
formattedMessage = "NICK " + oldNick + " -> " + newNick;
} else if (type == "kick") {
formattedMessage = "KICK " + senderNick + message;
} else if (type == "mode") {
formattedMessage = "MODE " + message;
tft.setTextColor(TFT_BLUE);
} else if (type == "action") {
formattedMessage = "* " + senderNick + " " + message;
} else if (type == "error") {
formattedMessage = "ERROR " + message;
senderNick = "ERROR";
} else {
formattedMessage = senderNick + ": " + message;
}
int linesRequired = calculateLinesRequired(formattedMessage);
while (lines.size() + linesRequired > MAX_LINES) {
lines.erase(lines.begin());
mentions.erase(mentions.begin());
}
if (type == "error") {
lines.push_back("ERROR " + message);
mentions.push_back(false);
} else {
lines.push_back(formattedMessage);
mentions.push_back(mention);
}
displayLines();
}
int calculateLinesRequired(String message) {
int linesRequired = 1;
int lineWidth = 0;
for (unsigned int i = 0; i < message.length(); i++) {
char c = message[i];
if (c == '\x03') {
if (i + 1 < message.length() && isdigit(message[i + 1])) {
i++;
if (i + 1 < message.length() && isdigit(message[i + 1]))
i++;
}
if (i + 1 < message.length() && message[i + 1] == ',' && isdigit(message[i + 2])) {
i += 2;
if (i + 1 < message.length() && isdigit(message[i + 1]))
i++;
}
} else if (c != '\x02' && c != '\x0F' && c != '\x1F') {
lineWidth += tft.textWidth(String(c));
if (lineWidth > SCREEN_WIDTH) {
linesRequired++;
lineWidth = tft.textWidth(String(c));
}
}
}
return linesRequired;
}
void displayCenteredText(String text) {
tft.fillScreen(TFT_BLACK);
tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString(text, SCREEN_WIDTH / 2, (SCREEN_HEIGHT + STATUS_BAR_HEIGHT) / 2);
}
void displayInputLine() {
tft.fillRect(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT, SCREEN_WIDTH, INPUT_LINE_HEIGHT, TFT_BLACK);
tft.setCursor(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(1);
String displayInput = inputBuffer;
int displayWidth = tft.textWidth(displayInput);
int inputWidth = SCREEN_WIDTH - tft.textWidth("> ");
while (displayWidth > inputWidth) {
displayInput = displayInput.substring(1);
displayWidth = tft.textWidth(displayInput);
}
if (inputBuffer.length() >= 510)
tft.setTextColor(TFT_RED);
tft.print("> " + displayInput);
tft.setTextColor(TFT_WHITE);
}
void displayLines() {
tft.fillRect(0, STATUS_BAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - STATUS_BAR_HEIGHT - INPUT_LINE_HEIGHT, TFT_BLACK);
int cursorY = STATUS_BAR_HEIGHT;
int totalLinesHeight = 0;
std::vector<int> lineHeights;
for (const String& line : lines) {
int lineHeight = calculateLinesRequired(line) * CHAR_HEIGHT;
lineHeights.push_back(lineHeight);
totalLinesHeight += lineHeight;
}
while (totalLinesHeight > SCREEN_HEIGHT - STATUS_BAR_HEIGHT - INPUT_LINE_HEIGHT) {
totalLinesHeight -= lineHeights.front();
lines.erase(lines.begin());
mentions.erase(mentions.begin());
lineHeights.erase(lineHeights.begin());
}
for (size_t i = 0; i < lines.size(); ++i) {
const String& line = lines[i];
bool mention = mentions[i];
tft.setCursor(0, cursorY);
if (line.startsWith("JOIN ")) {
tft.setTextColor(TFT_GREEN);
tft.print("JOIN ");
int startIndex = 5;
int endIndex = line.indexOf(" has joined ");
String senderNick = line.substring(startIndex, endIndex);
tft.setTextColor(nickColors[senderNick]);
tft.print(senderNick);
tft.setTextColor(TFT_WHITE);
tft.print(" has joined ");
tft.setTextColor(TFT_CYAN);
tft.print(channel);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("PART ")) {
tft.setTextColor(TFT_RED);
tft.print("PART ");
int startIndex = 5;
int endIndex = line.indexOf(" has EMO-QUIT ");
String senderNick = line.substring(startIndex, endIndex);
tft.setTextColor(nickColors[senderNick]);
tft.print(senderNick);
tft.setTextColor(TFT_WHITE);
tft.print(" has EMO-QUIT ");
tft.setTextColor(TFT_CYAN);
tft.print(channel);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("QUIT ")) {
tft.setTextColor(TFT_RED);
tft.print("QUIT ");
String senderNick = line.substring(5);
tft.setTextColor(nickColors[senderNick]);
tft.print(senderNick);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("NICK ")) {
tft.setTextColor(TFT_BLUE);
tft.print("NICK ");
int startIndex = 5;
int endIndex = line.indexOf(" -> ");
String oldNick = line.substring(startIndex, endIndex);
String newNick = line.substring(endIndex + 4);
tft.setTextColor(nickColors[oldNick]);
tft.print(oldNick);
tft.setTextColor(TFT_WHITE);
tft.print(" -> ");
tft.setTextColor(nickColors[newNick]);
tft.print(newNick);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("KICK ")) {
tft.setTextColor(TFT_RED);
tft.print("KICK ");
int startIndex = 5;
int endIndex = line.indexOf(" by ");
String kickedNick = line.substring(startIndex, endIndex);
String kicker = line.substring(endIndex + 4);
tft.setTextColor(nickColors[kickedNick]);
tft.print(kickedNick);
tft.setTextColor(TFT_WHITE);
tft.print(" by ");
tft.setTextColor(nickColors[kicker]);
tft.print(kicker);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("MODE ")) {
tft.setTextColor(TFT_BLUE);
tft.print("MODE ");
String modeChange = line.substring(5);
tft.setTextColor(TFT_WHITE);
tft.print(modeChange);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("ERROR ")) {
tft.setTextColor(TFT_RED);
tft.print("ERROR ");
String errorReason = line.substring(6);
tft.setTextColor(TFT_DARKGREY);
tft.print(errorReason);
cursorY += CHAR_HEIGHT;
} else if (line.startsWith("* ")) {
tft.setTextColor(TFT_MAGENTA);
tft.print("* ");
int startIndex = 2;
int endIndex = line.indexOf(' ', startIndex);
String senderNick = line.substring(startIndex, endIndex);
String actionMessage = line.substring(endIndex + 1);
tft.setTextColor(nickColors[senderNick]);
tft.print(senderNick);
tft.setTextColor(TFT_MAGENTA);
tft.print(" " + actionMessage);
cursorY += CHAR_HEIGHT;
} else {
int colonIndex = line.indexOf(':');
String senderNick = line.substring(0, colonIndex);
String message = line.substring(colonIndex + 1);
tft.setTextColor(nickColors[senderNick]);
tft.print(senderNick);
tft.setTextColor(TFT_WHITE);
cursorY = renderFormattedMessage(":" + message, cursorY, CHAR_HEIGHT, mention);
}
}
displayInputLine();
}
void displayXBM() {
tft.fillScreen(TFT_BLACK);
int x = (SCREEN_WIDTH - logo_width) / 2;
int y = (SCREEN_HEIGHT - logo_height) / 2;
tft.drawXBitmap(x, y, logo_bits, logo_width, logo_height, TFT_GREEN);
unsigned long startTime = millis();
bool wipeInitiated = false;
while (millis() - startTime < 3000) {
if (getKeyboardInput() == 'w') {
wipeNVS();
tft.fillScreen(TFT_BLACK);
displayCenteredText("NVS WIPED");
delay(2000);
wipeInitiated = true;
break;
}
}
}
uint32_t generateRandomColor() {
return tft.color565(random(0, 255), random(0, 255), random(0, 255));
}
uint16_t getColorFromCode(int colorCode) {
switch (colorCode) {
case 0: return TFT_WHITE;
case 1: return TFT_BLACK;
case 2: return tft.color565(0, 0, 128); // Dark Blue (Navy)
case 3: return TFT_GREEN;
case 4: return TFT_RED;
case 5: return tft.color565(128, 0, 0); // Brown (Maroon)
case 6: return tft.color565(128, 0, 128); // Purple
case 7: return tft.color565(255, 165, 0); // Orange
case 8: return TFT_YELLOW;
case 9: return tft.color565(144, 238, 144); // Light Green
case 10: return tft.color565(0, 255, 255); // Cyan (Light Blue)
case 11: return tft.color565(224, 255, 255); // Light Cyan (Aqua)
case 12: return TFT_BLUE;
case 13: return tft.color565(255, 192, 203); // Pink (Light Purple)
case 14: return tft.color565(128, 128, 128); // Grey
case 15: return tft.color565(211, 211, 211); // Light Grey
case 16: return 0x4000;
case 17: return 0x4100;
case 18: return 0x4220;
case 19: return 0x3220;
case 20: return 0x0220;
case 21: return 0x0225;
case 22: return 0x0228;
case 23: return 0x0128;
case 24: return 0x0008;
case 25: return 0x2808;
case 26: return 0x4008;
case 27: return 0x4005;
case 28: return 0x7000;
case 29: return 0x71C0;
case 30: return 0x73A0;
case 31: return 0x53A0;
case 32: return 0x03A0;
case 33: return 0x03A9;
case 34: return 0x03AE;
case 35: return 0x020E;
case 36: return 0x000E;
case 37: return 0x480E;
case 38: return 0x700E;
case 39: return 0x7008;
case 40: return 0xB000;
case 41: return 0xB300;
case 42: return 0xB5A0;
case 43: return 0x7DA0;
case 44: return 0x05A0;
case 45: return 0x05AE;
case 46: return 0x05B6;
case 47: return 0x0316;
case 48: return 0x0016;
case 49: return 0x7016;
case 50: return 0xB016;
case 51: return 0xB00D;
case 52: return 0xF800;
case 53: return 0xFC60;
case 54: return 0xFFE0;
case 55: return 0xB7E0;
case 56: return 0x07E0;
case 57: return 0x07F4;
case 58: return 0x07FF;
case 59: return 0x047F;
case 60: return 0x001F;
case 61: return 0xA01F;
case 62: return 0xF81F;
case 63: return 0xF813;
case 64: return 0xFACB;
case 65: return 0xFDAB;
case 66: return 0xFFEE;
case 67: return 0xCFEC;
case 68: return 0x6FED;
case 69: return 0x67F9;
case 70: return 0x6FFF;
case 71: return 0x5DBF;
case 72: return 0x5ADF;
case 73: return 0xC2DF;
case 74: return 0xFB3F;
case 75: return 0xFAD7;
case 76: return 0xFCF3;
case 77: return 0xFE93;
case 78: return 0xFFF3;
case 79: return 0xE7F3;
case 80: return 0x9FF3;
case 81: return 0x9FFB;
case 82: return 0x9FFF;
case 83: return 0x9E9F;
case 84: return 0x9CFF;
case 85: return 0xDCFF;
case 86: return 0xFCFF;
case 87: return 0xFCBA;
case 88: return 0x0000;
case 89: return 0x1082;
case 90: return 0x2945;
case 91: return 0x31A6;
case 92: return 0x4A69;
case 93: return 0x632C;
case 94: return 0x8410;
case 95: return 0x9CF3;
case 96: return 0xBDF7;
case 97: return 0xE71C;
case 98: return 0xFFFF;
default: return TFT_WHITE;
}
}
uint16_t getColorFromPercentage(int percentage) {
if (percentage > 75) return TFT_GREEN;
else if (percentage > 50) return TFT_YELLOW;
else if (percentage > 25) return TFT_ORANGE;
else return TFT_RED;
}
void handleKeyboardInput(char key) {
lastActivityTime = millis();
if (!screenOn) {
turnOnScreen();
screenOn = true;
return;
}
static bool altPressed = false;
if (key == '\n' || key == '\r') {
if (inputBuffer.startsWith("/nick ")) {
String newNick = inputBuffer.substring(6);
sendIRC("NICK " + newNick);
inputBuffer = "";
} else if (inputBuffer.startsWith("/config")) {
configScreen = true;
configScreenStartTime = millis();
inputBuffer = "";
} else if (inputBuffer.startsWith("/info")) {
infoScreen = true;
infoScreenStartTime = millis();
tft.fillScreen(TFT_BLACK);
printDeviceInfo();
inputBuffer = "";
} else if (inputBuffer.startsWith("/raw ")) {
String rawCommand = inputBuffer.substring(5);
sendIRC(rawCommand);
} else if (inputBuffer.startsWith("/me ")) {
String actionMessage = inputBuffer.substring(4);
sendIRC("PRIVMSG " + String(channel) + " :\001ACTION " + actionMessage + "\001");
addLine(irc_nickname, actionMessage, "action");
inputBuffer = "";
} else {
sendIRC("PRIVMSG " + String(channel) + " :" + inputBuffer);
addLine(irc_nickname, inputBuffer, "message");
}
inputBuffer = "";
displayInputLine();
} else if (key == '\b') {
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
displayInputLine();
}
} else {
inputBuffer += key;
displayInputLine();
}
}
void parseAndDisplay(String line) {
int firstSpace = line.indexOf(' ');
int secondSpace = line.indexOf(' ', firstSpace + 1);
if (firstSpace != -1 && secondSpace != -1) {
String command = line.substring(firstSpace + 1, secondSpace);
if (command == "PRIVMSG") {
int thirdSpace = line.indexOf(' ', secondSpace + 1);
String target = line.substring(secondSpace + 1, thirdSpace);
if (target == String(channel)) {
int colonPos = line.indexOf(':', thirdSpace);
String message = line.substring(colonPos + 1);
String senderNick = line.substring(1, line.indexOf('!'));
bool mention = message.indexOf(irc_nickname) != -1;
if (mention)
playNotificationSound();
if (message.startsWith(String("\x01") + "ACTION ") && message.endsWith("\x01")) {
String actionMessage = message.substring(8, message.length() - 1);
addLine(senderNick, actionMessage, "action");
} else {
addLine(senderNick, message, "message", mention);
}
}
} else if (command == "JOIN" && line.indexOf(channel) != -1) {
String senderNick = line.substring(1, line.indexOf('!'));
addLine(senderNick, " has joined " + String(channel), "join");
} else if (command == "PART" && line.indexOf(channel) != -1) {
String senderNick = line.substring(1, line.indexOf('!'));
addLine(senderNick, " has EMO-QUIT " + String(channel), "part");
} else if (command == "QUIT") {
String senderNick = line.substring(1, line.indexOf('!'));
addLine(senderNick, "", "quit");
} else if (command == "NICK") {
String prefix = line.startsWith(":") ? line.substring(1, firstSpace) : "";
String newNick = line.substring(line.lastIndexOf(':') + 1);
if (prefix.indexOf('!') == -1) {
addLine(irc_nickname, " -> " + newNick, "nick");
irc_nickname = newNick;
} else {
String oldNick = prefix.substring(0, prefix.indexOf('!'));
addLine(oldNick, " -> " + newNick, "nick");
if (oldNick == irc_nickname) {
irc_nickname = newNick;
}
}
} else if (command == "KICK") {
int thirdSpace = line.indexOf(' ', secondSpace + 1);
int fourthSpace = line.indexOf(' ', thirdSpace + 1);
String kicker = line.substring(1, line.indexOf('!'));
String kicked = line.substring(thirdSpace + 1, fourthSpace);
addLine(kicked, " by " + kicker, "kick");
} else if (command == "MODE") {
String modeChange = line.substring(secondSpace + 1);
addLine("", modeChange, "mode");
} else if (command == "432") {
addLine("ERROR", "ERR_ERRONEUSNICKNAME", "error", TFT_RED, TFT_DARKGREY);
} else if (command == "433") {
addLine("ERROR", "ERR_NICKNAMEINUSE", "error", TFT_RED, TFT_DARKGREY);
irc_nickname = "ACID_" + String(random(1000, 9999));
sendIRC("NICK " + irc_nickname);
}
}
}
int renderFormattedMessage(String message, int cursorY, int lineHeight, bool highlightNick) {
uint16_t fgColor = TFT_WHITE;
uint16_t bgColor = TFT_BLACK;
bool bold = false;
bool underline = false;
bool nickHighlighted = false;
int nickPos = -1;
if (highlightNick) {
nickPos = message.indexOf(irc_nickname);
}
for (unsigned int i = 0; i < message.length(); i++) {
char c = message[i];
if (c == '\x02') {
bold = !bold;
} else if (c == '\x1F') {
underline = !underline;
} else if (c == '\x03') {
fgColor = TFT_WHITE;
bgColor = TFT_BLACK;
if (i + 1 < message.length() && (isdigit(message[i + 1]) || message[i + 1] == ',')) {
int colorCode = -1;
if (isdigit(message[i + 1])) {
colorCode = message[++i] - '0';
if (i + 1 < message.length() && isdigit(message[i + 1]))
colorCode = colorCode * 10 + (message[++i] - '0');
}
if (colorCode != -1)
fgColor = getColorFromCode(colorCode);
if (i + 1 < message.length() && message[i + 1] == ',') {
i++;
int bgColorCode = -1;
if (isdigit(message[i + 1])) {
bgColorCode = message[++i] - '0';
if (i + 1 < message.length() && isdigit(message[i + 1]))
bgColorCode = bgColorCode * 10 + (message[++i] - '0');
}
if (bgColorCode != -1)
bgColor = getColorFromCode(bgColorCode);
}
tft.setTextColor(fgColor, bgColor);
}
} else if (c == '\x0F') {
fgColor = TFT_WHITE;
bgColor = TFT_BLACK;
bold = false;
underline = false;
tft.setTextColor(fgColor, bgColor);
tft.setTextFont(1);
} else {
if (highlightNick && !nickHighlighted && nickPos != -1 && i == nickPos) {
tft.setTextColor(TFT_YELLOW, bgColor);
for (char nc : irc_nickname) {
tft.print(nc);
i++;
}
i--;
tft.setTextColor(TFT_WHITE, bgColor);
nickHighlighted = true;
} else {
if (tft.getCursorX() + tft.textWidth(String(c)) > SCREEN_WIDTH) {
cursorY += lineHeight;
tft.setCursor(0, cursorY);
}
if (c == ' ') {
int spaceWidth = tft.textWidth(" ");
tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor);
tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY());
} else {
tft.fillRect(tft.getCursorX(), tft.getCursorY(), tft.textWidth(String(c)), lineHeight, bgColor);
tft.setTextColor(fgColor, bgColor);
tft.print(c);
}
}
}
}
if (message.endsWith(" ")) {
int trailingSpaces = 0;
for (int i = message.length() - 1; i >= 0 && message[i] == ' '; i--) {
trailingSpaces++;
}
for (int i = 0; i < trailingSpaces; i++) {
int spaceWidth = tft.textWidth(" ");
tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor);
tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY());
}
}
cursorY += lineHeight;
return cursorY;
}
void turnOffScreen() {
Serial.println("Screen turned off");
tft.writecommand(TFT_DISPOFF);
tft.writecommand(TFT_SLPIN);
digitalWrite(TFT_BL, LOW);
screenOn = false;
}
void turnOnScreen() {
Serial.println("Screen turned on");
digitalWrite(TFT_BL, HIGH);
tft.writecommand(TFT_SLPOUT);
tft.writecommand(TFT_DISPON);
screenOn = true;
}
void updateStatusBar() {
Serial.println("Updating status bar...");
uint16_t darkerGrey = tft.color565(25, 25, 25);
tft.fillRect(0, 0, SCREEN_WIDTH, STATUS_BAR_HEIGHT, darkerGrey);
struct tm timeinfo;
char timeStr[9];
if (!getLocalTime(&timeinfo)) {
sprintf(timeStr, "12:00 AM");
} else {
int hour = timeinfo.tm_hour;
char ampm[] = "AM";
if (hour == 0) {
hour = 12;
} else if (hour >= 12) {
if (hour > 12)
hour -= 12;
strcpy(ampm, "PM");
}
sprintf(timeStr, "%02d:%02d %s", hour, timeinfo.tm_min, ampm);
}
tft.setTextDatum(ML_DATUM);
tft.setTextColor(TFT_WHITE, darkerGrey);
tft.drawString(timeStr, 0, STATUS_BAR_HEIGHT / 2);
char wifiStr[15];
int wifiSignal = 0;
if (WiFi.status() != WL_CONNECTED) {
sprintf(wifiStr, "WiFi: N/A");
tft.setTextColor(TFT_PINK, darkerGrey);
tft.setTextDatum(MR_DATUM);
tft.drawString(wifiStr, SCREEN_WIDTH - 100, STATUS_BAR_HEIGHT / 2);
} else {
int32_t rssi = WiFi.RSSI();
if (rssi > -50) wifiSignal = 100;
else if (rssi > -60) wifiSignal = 80;
else if (rssi > -70) wifiSignal = 60;
else if (rssi > -80) wifiSignal = 40;
else if (rssi > -90) wifiSignal = 20;
else wifiSignal = 0;
sprintf(wifiStr, "WiFi: %d%%", wifiSignal);
tft.setTextDatum(MR_DATUM);
tft.setTextColor(TFT_PINK, darkerGrey);
tft.drawString("WiFi:", SCREEN_WIDTH - 120, STATUS_BAR_HEIGHT / 2);
tft.setTextColor(getColorFromPercentage(wifiSignal), darkerGrey);
tft.drawString(wifiStr + 6, SCREEN_WIDTH - 100, STATUS_BAR_HEIGHT / 2);
}
int batteryLevel = BL.getBatteryChargeLevel();
char batteryStr[15];
sprintf(batteryStr, "Batt: %d%%", batteryLevel);
tft.setTextDatum(MR_DATUM);
tft.setTextColor(TFT_CYAN, darkerGrey);
tft.drawString("Batt:", SCREEN_WIDTH - 40, STATUS_BAR_HEIGHT / 2);
tft.setTextColor(getColorFromPercentage(batteryLevel), darkerGrey);
tft.drawString(batteryStr + 5, SCREEN_WIDTH - 5, STATUS_BAR_HEIGHT / 2);
}

48
src/Display.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <TFT_eSPI.h>
#include <map>
#include <vector>
#include <WiFi.h>
#include <time.h>
// Constants
#define CHAR_HEIGHT 10
#define LINE_SPACING 0
#define STATUS_BAR_HEIGHT 10
#define INPUT_LINE_HEIGHT (CHAR_HEIGHT + LINE_SPACING)
#define MAX_LINES ((SCREEN_HEIGHT - INPUT_LINE_HEIGHT - STATUS_BAR_HEIGHT) / (CHAR_HEIGHT + LINE_SPACING))
// External variables
extern bool infoScreen;
extern bool configScreen;
extern bool screenOn;
extern const char* channel;
extern unsigned long infoScreenStartTime;
extern unsigned long configScreenStartTime;
extern unsigned long lastStatusUpdateTime;
extern unsigned long lastActivityTime;
extern String inputBuffer;
extern std::vector<String> lines;
extern std::vector<bool> mentions;
extern std::map<String, uint32_t> nickColors;
extern TFT_eSPI tft;
// Function declarations
void addLine(String senderNick, String message, String type, bool mention = false, uint16_t errorColor = TFT_WHITE, uint16_t reasonColor = TFT_WHITE);
int calculateLinesRequired(String message);
void displayCenteredText(String text);
void displayInputLine();
void displayLines();
void displayXBM();
uint32_t generateRandomColor();
uint16_t getColorFromCode(int colorCode);
uint16_t getColorFromPercentage(int percentage);
void handleKeyboardInput(char key);
void parseAndDisplay(String line);
int renderFormattedMessage(String message, int cursorY, int lineHeight, bool highlightNick = false);
void turnOffScreen();
void turnOnScreen();
void updateStatusBar();

76
src/IRC.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "IRC.h"
unsigned long joinChannelTime = 0;
bool readyToJoinChannel = false;
WiFiClient* client;
bool connectToIRC() {
if (irc_tls) {
Serial.println("Connecting to IRC with TLS: " + String(irc_server) + ":" + String(irc_port));
client = new WiFiClientSecure();
static_cast<WiFiClientSecure*>(client)->setInsecure();
return static_cast<WiFiClientSecure*>(client)->connect(irc_server.c_str(), irc_port);
} else {
Serial.println("Connecting to IRC: " + String(irc_server) + ":" + String(irc_port));
client = new WiFiClient();
return client->connect(irc_server.c_str(), irc_port);
}
}
void handleIRC() {
while (client->available()) {
String line = client->readStringUntil('\n');
if (line.length() > 512) {
Serial.println("WARNING: IRC line length exceeds 512 characters!");
line = line.substring(0, 512);
}
Serial.println("IRC: " + line);
int firstSpace = line.indexOf(' ');
int secondSpace = line.indexOf(' ', firstSpace + 1);
if (firstSpace != -1 && secondSpace != -1) {
String prefix = line.substring(0, firstSpace);
String command = line.substring(firstSpace + 1, secondSpace);
if (command == "001") {
joinChannelTime = millis() + 2500;
readyToJoinChannel = true;
}
}
if (line.startsWith("PING")) {
String pingResponse = "PONG " + line.substring(line.indexOf(' ') + 1);
sendIRC(pingResponse);
} else {
parseAndDisplay(line);
lastActivityTime = millis();
}
}
}
void sendIRC(String command) {
if (command.length() > 510) {
Serial.println("Failed to send: Command too long");
return;
}
if (client->connected()) {
if (client->println(command)) {
Serial.println("IRC: >>> " + command);
} else {
Serial.println("Failed to send: " + command);
}
} else {
Serial.println("Failed to send: Not connected to IRC");
}
}

14
src/IRC.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <WiFiClientSecure.h>
#include "Display.h"
#include "Storage.h"
extern WiFiClient* client;
extern unsigned long joinChannelTime;
extern bool readyToJoinChannel;
bool connectToIRC();
void handleIRC();
void sendIRC(String command);

View File

@ -4,6 +4,38 @@
SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
void recvLoop() {
String recv;
while (true) {
if (radio.available()) {
int state = radio.readData(recv);
if (state == RADIOLIB_ERR_NONE) {
playNotificationSound();
Serial.print(F("[RADIO] Received packet!"));
Serial.print(F(" Data:"));
Serial.print(recv);
Serial.print(F(" RSSI:"));
Serial.print(radio.getRSSI());
Serial.print(F(" dBm"));
Serial.print(F(" SNR:"));
Serial.print(radio.getSNR());
Serial.println(F(" dB"));
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
Serial.println(F("CRC error!"));
} else {
Serial.print(F("Failed, code "));
Serial.println(state);
}
} else {
Serial.println(F("Radio became unavailable!"));
break;
}
}
}
bool setupRadio() {
pinMode(RADIO_CS_PIN, OUTPUT);
digitalWrite(RADIO_CS_PIN, HIGH);
@ -91,34 +123,3 @@ bool transmit() {
return false;
}
}
void recvLoop() {
String recv;
while (true) {
if (radio.available()) {
int state = radio.readData(recv);
if (state == RADIOLIB_ERR_NONE) {
Serial.print(F("[RADIO] Received packet!"));
Serial.print(F(" Data:"));
Serial.print(recv);
Serial.print(F(" RSSI:"));
Serial.print(radio.getRSSI());
Serial.print(F(" dBm"));
Serial.print(F(" SNR:"));
Serial.print(radio.getSNR());
Serial.println(F(" dB"));
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
Serial.println(F("CRC error!"));
} else {
Serial.print(F("Failed, code "));
Serial.println(state);
}
} else {
Serial.println(F("Radio became unavailable!"));
break;
}
}
}

View File

@ -3,9 +3,10 @@
#include <RadioLib.h>
#include "pins.h"
#include "Speaker.h"
extern SX1262 radio;
void recvLoop();
bool setupRadio();
bool transmit();
void recvLoop();

250
src/Network.cpp Normal file
View File

@ -0,0 +1,250 @@
#include "Network.h"
std::vector<WiFiNetwork> wifiNetworks;
int selectedNetworkIndex = 0;
WireGuard wg;
void connectToWiFi(String ssid, String password) {
Serial.println("Connecting to WiFi network: " + ssid);
WiFi.begin(ssid.c_str(), password.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
displayCenteredText("CONNECTING TO " + ssid);
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
displayCenteredText("CONNECTED TO " + ssid);
updateTimeFromNTP();
preferences.begin("config", false);
preferences.putString("wifi_ssid", ssid);
preferences.putString("wifi_password", password);
preferences.end();
Serial.println("Stored WiFi credentials updated");
wifi_ssid = ssid;
wifi_password = password;
} else {
Serial.println("Failed to connect to WiFi network: " + ssid);
displayCenteredText("WIFI CONNECTION FAILED");
preferences.begin("config", false);
preferences.putString("wifi_ssid", "");
preferences.putString("wifi_password", "");
preferences.end();
Serial.println("Stored WiFi credentials removed");
wifi_ssid = "";
wifi_password = "";
scanWiFiNetworks();
}
}
void displayPasswordInputLine() {
tft.fillRect(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT, SCREEN_WIDTH, INPUT_LINE_HEIGHT, TFT_BLACK);
tft.setCursor(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(1);
tft.print("> " + inputBuffer);
}
void displayWiFiNetworks() {
tft.fillRect(0, STATUS_BAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - STATUS_BAR_HEIGHT, TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_CYAN);
tft.setCursor(0, STATUS_BAR_HEIGHT);
tft.printf("# CH RSSI ENC SSID\n");
tft.setTextColor(TFT_WHITE);
int maxDisplayedLines = (SCREEN_HEIGHT - STATUS_BAR_HEIGHT - CHAR_HEIGHT) / (CHAR_HEIGHT + LINE_SPACING);
int startIdx = selectedNetworkIndex >= maxDisplayedLines ? selectedNetworkIndex - maxDisplayedLines + 1 : 0;
for (int i = startIdx; i < wifiNetworks.size() && i < startIdx + maxDisplayedLines; ++i) {
displayWiFiNetwork(i, i - startIdx + 1);
}
}
void displayWiFiNetwork(int index, int displayIndex) {
int y = STATUS_BAR_HEIGHT + displayIndex * (CHAR_HEIGHT + LINE_SPACING);
tft.setCursor(0, y);
if (index == selectedNetworkIndex)
tft.setTextColor(TFT_GREEN, TFT_BLACK);
else
tft.setTextColor(TFT_WHITE, TFT_BLACK);
WiFiNetwork net = wifiNetworks[index];
uint16_t rssiColor = getColorFromPercentage((int32_t)net.rssi);
tft.printf("%-2d %-3d ", net.index, net.channel);
tft.setTextColor(rssiColor, TFT_BLACK);
tft.printf("%-5d ", net.rssi);
tft.setTextColor(index == selectedNetworkIndex ? TFT_GREEN : TFT_WHITE, TFT_BLACK);
tft.printf("%-8s %s", net.encryption.c_str(), net.ssid.c_str());
}
String getEncryptionType(wifi_auth_mode_t encryptionType) {
switch (encryptionType) {
case (WIFI_AUTH_OPEN):
return "Open";
case (WIFI_AUTH_WEP):
return "WEP";
case (WIFI_AUTH_WPA_PSK):
return "WPA_PSK";
case (WIFI_AUTH_WPA2_PSK):
return "WPA2_PSK";
case (WIFI_AUTH_WPA_WPA2_PSK):
return "WPA_WPA2_PSK";
case (WIFI_AUTH_WPA2_ENTERPRISE):
return "WPA2_ENTERPRISE";
default:
return "Unknown";
}
}
void handlePasswordInput(char key) {
if (key == '\n' || key == '\r') {
wifi_password = inputBuffer;
inputBuffer = "";
connectToWiFi(wifi_ssid, wifi_password);
} else if (key == '\b') {
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
displayPasswordInputLine();
}
} else if (inputBuffer.length() < 63) {
inputBuffer += key;
displayPasswordInputLine();
}
}
void handleWiFiSelection(char key) {
if (key == 'u') {
updateSelectedNetwork(-1);
} else if (key == 'd') {
updateSelectedNetwork(1);
} else if (key == '\n' || key == '\r') {
wifi_ssid = wifiNetworks[selectedNetworkIndex].ssid;
if (wifiNetworks[selectedNetworkIndex].encryption == "Secured") {
inputBuffer = "";
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE);
tft.setCursor(0, STATUS_BAR_HEIGHT);
tft.println("ENTER PASSWORD:");
displayPasswordInputLine();
while (true) {
char incoming = getKeyboardInput();
if (incoming != 0) {
handlePasswordInput(incoming);
if (incoming == '\n' || incoming == '\r')
break;
}
}
} else {
wifi_password = "";
connectToWiFi(wifi_ssid, wifi_password);
}
}
}
void randomizeMacAddress() {
Serial.println("Current MAC Address: " + WiFi.macAddress());
uint8_t new_mac[6];
for (int i = 0; i < 6; ++i)
new_mac[i] = random(0x00, 0xFF);
if (esp_wifi_set_mac(WIFI_IF_STA, new_mac) == ESP_OK)
Serial.println("New MAC Address: " + WiFi.macAddress());
else
Serial.print("Failed to set new MAC Address");
}
void scanWiFiNetworks() {
Serial.println("Scanning for WiFi networks...");
displayCenteredText("SCANNING WIFI");
delay(1000);
int n = WiFi.scanNetworks();
if (n == 0) {
Serial.println("No WiFi networks found");
displayCenteredText("NO NETWORKS FOUND");
delay(2000);
scanWiFiNetworks();
return;
}
Serial.print("Total number of networks found: ");
Serial.println(n);
for (int i = 0; i < n && i < 100; i++) {
WiFiNetwork net;
net.index = i + 1;
net.channel = WiFi.channel(i);
net.rssi = WiFi.RSSI(i);
net.encryption = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "Open" : "Secured";
String ssid = WiFi.SSID(i).substring(0, 32);
net.ssid = ssid;
wifiNetworks.push_back(net);
}
displayWiFiNetworks();
}
void updateSelectedNetwork(int delta) {
int newIndex = selectedNetworkIndex + delta;
if (newIndex >= 0 && newIndex < wifiNetworks.size()) {
selectedNetworkIndex = newIndex;
displayWiFiNetworks();
}
}
void wgConnect(const IPAddress& localIp, const char* privateKey, const char* endpointAddress, const char* publicKey, uint16_t endpointPort) {
wg.begin(localIp, privateKey, endpointAddress, publicKey, endpointPort);
}
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
switch (event) {
case SYSTEM_EVENT_STA_CONNECTED:
Serial.println("WiFi connected");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi disconnected");
break;
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi got IP address: " + WiFi.localIP().toString());
break;
case SYSTEM_EVENT_STA_LOST_IP:
Serial.println("WiFi lost IP address");
break;
default:
break;
}
}

36
src/Network.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <esp_wifi.h>
#include <WireGuard-ESP32.h>
#include "Display.h"
#include "Storage.h"
#include "Utilities.h"
#include "WiFi.h"
struct WiFiNetwork {
int index;
int channel;
int rssi;
String encryption;
String ssid;
};
extern std::vector<WiFiNetwork> wifiNetworks;
extern int selectedNetworkIndex;
extern WireGuard wg;
void connectToWiFi(String ssid, String password);
void displayPasswordInputLine();
void displayWiFiNetworks();
void displayWiFiNetwork(int index, int displayIndex);
String getEncryptionType(wifi_auth_mode_t encryptionType);
void handlePasswordInput(char key);
void handleWiFiSelection(char key);
void randomizeMacAddress();
void scanWiFiNetworks();
void updateSelectedNetwork(int delta);
void wgConnect(const IPAddress& localIp, const char* privateKey, const char* endpointAddress, const char* publicKey, uint16_t endpointPort);
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);

View File

@ -1,6 +1,52 @@
#include "Speaker.h"
void playNotificationSound() {
playTone(1000, 50);
delay(100);
playTone(1500, 50);
delay(100);
playTone(2000, 50);
delay(100);
playTone(500, 50);
}
void playRTTTL(const char* rtttl) {
static AudioGeneratorRTTTL *rtttlGenerator = new AudioGeneratorRTTTL();
static AudioOutputI2S *audioOutput = new AudioOutputI2S();
static AudioFileSourcePROGMEM *fileSource = new AudioFileSourcePROGMEM(rtttl, strlen(rtttl));
audioOutput->begin();
rtttlGenerator->begin(fileSource, audioOutput);
while (rtttlGenerator->isRunning())
rtttlGenerator->loop();
rtttlGenerator->stop();
fileSource->close();
}
void playTone(float frequency, int duration, int volume) {
volume = constrain(volume, 0, 32767);
const int wave_period = SAMPLE_RATE / frequency;
int16_t sample_buffer[wave_period];
for (int i = 0; i < wave_period; ++i)
sample_buffer[i] = (i < wave_period / 2) ? volume : -volume;
int total_samples = SAMPLE_RATE * duration / 1000;
int samples_written = 0;
while (samples_written < total_samples) {
int to_write = min(wave_period, total_samples - samples_written);
i2s_write(BOARD_I2S_PORT, sample_buffer, to_write * sizeof(int16_t), (size_t *)&to_write, portMAX_DELAY);
samples_written += to_write;
}
}
void setupI2S() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
@ -27,49 +73,3 @@ void setupI2S() {
i2s_set_pin(BOARD_I2S_PORT, &pin_config);
i2s_set_clk(BOARD_I2S_PORT, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
}
void playTone(float frequency, int duration, int volume) {
volume = constrain(volume, 0, 32767);
const int wave_period = SAMPLE_RATE / frequency;
int16_t sample_buffer[wave_period];
for (int i = 0; i < wave_period; ++i)
sample_buffer[i] = (i < wave_period / 2) ? volume : -volume;
int total_samples = SAMPLE_RATE * duration / 1000;
int samples_written = 0;
while (samples_written < total_samples) {
int to_write = min(wave_period, total_samples - samples_written);
i2s_write(BOARD_I2S_PORT, sample_buffer, to_write * sizeof(int16_t), (size_t *)&to_write, portMAX_DELAY);
samples_written += to_write;
}
}
void playRTTTL(const char* rtttl) {
static AudioGeneratorRTTTL *rtttlGenerator = new AudioGeneratorRTTTL();
static AudioOutputI2S *audioOutput = new AudioOutputI2S();
static AudioFileSourcePROGMEM *fileSource = new AudioFileSourcePROGMEM(rtttl, strlen(rtttl));
audioOutput->begin();
rtttlGenerator->begin(fileSource, audioOutput);
while (rtttlGenerator->isRunning())
rtttlGenerator->loop();
rtttlGenerator->stop();
fileSource->close();
}
void playNotificationSound() {
playTone(1000, 150);
delay(100);
playTone(1500, 150);
delay(100);
playTone(2000, 150);
delay(100);
playTone(500, 150);
}

View File

@ -11,7 +11,7 @@
#define BOARD_I2S_PORT I2S_NUM_0
#define SAMPLE_RATE 44100
void setupI2S();
void playTone(float frequency, int duration, int volume = 16383);
void playRTTTL(const char* rtttl);
void playNotificationSound();
void playRTTTL(const char* rtttl);
void playTone(float frequency, int duration, int volume = 16383);
void setupI2S();

View File

@ -3,6 +3,7 @@
Preferences preferences;
// Config variables
String irc_nickname;
String irc_username;
String irc_realname;

View File

@ -1,8 +1,8 @@
#pragma once
#include <Arduino.h>
#include <Preferences.h>
#include "nvs_flash.h"
#include <Preferences.h>
#include <SD.h>
#include "pins.h"

148
src/Utilities.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "Utilities.h"
Pangodream_18650_CL BL(BOARD_BAT_ADC, CONV_FACTOR, READS);
String formatBytes(size_t bytes) {
if (bytes < 1024)
return String(bytes) + " B";
else if (bytes < (1024 * 1024))
return String(bytes / 1024.0, 2) + " KB";
else if (bytes < (1024 * 1024 * 1024))
return String(bytes / 1024.0 / 1024.0, 2) + " MB";
else
return String(bytes / 1024.0 / 1024.0 / 1024.0, 2) + " GB";
}
char getKeyboardInput() {
char incoming = 0;
Wire.requestFrom(LILYGO_KB_SLAVE_ADDRESS, 1);
if (Wire.available()) {
incoming = Wire.read();
if (incoming != (char)0x00) {
return incoming;
}
}
return 0;
}
void setBrightness(uint8_t value) {
static uint8_t level = 16;
static uint8_t steps = 16;
value = constrain(value, 0, steps); // Ensure the brightness value is within the valid range
if (value == 0) {
digitalWrite(BOARD_BL_PIN, 0);
delay(3);
level = 0;
return;
}
if (level == 0) {
digitalWrite(BOARD_BL_PIN, 1);
level = steps;
delayMicroseconds(30);
}
int from = steps - level;
int to = steps - value;
int num = (steps + to - from) % steps;
for (int i = 0; i < num; i++) {
digitalWrite(BOARD_BL_PIN, 0);
digitalWrite(BOARD_BL_PIN, 1);
}
level = value;
}
void updateTimeFromNTP() {
Serial.println("Syncing time with NTP server...");
configTime(-5 * 3600, 3600, "pool.ntp.org", "north-america.pool.ntp.org", "time.nist.gov");
for (int i = 0; i < 10; ++i) { // Try up to 10 times
delay(2000);
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
Serial.println(&timeinfo, "Time synchronized: %A, %B %d %Y %H:%M:%S");
return;
} else {
Serial.println("Failed to synchronize time, retrying...");
}
}
Serial.println("Failed to synchronize time after multiple attempts."); // What do we do if we can't sync with the NTP server?
}
void printDeviceInfo() {
Serial.println("Gathering device info...");
// Get MAC Address
uint8_t mac[6];
esp_efuse_mac_get_default(mac);
String macAddress = String(mac[0], HEX) + ":" + String(mac[1], HEX) + ":" + String(mac[2], HEX) + ":" + String(mac[3], HEX) + ":" + String(mac[4], HEX) + ":" + String(mac[5], HEX);
// Get Chip Info
uint32_t chipId = ESP.getEfuseMac();
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
String chipInfo = String(chip_info.model) + " Rev " + String(chip_info.revision) + ", " + String(chip_info.cores) + " cores, " + String(ESP.getCpuFreqMHz()) + " MHz";
// Get Flash Info
size_t flashSize = spi_flash_get_chip_size();
size_t flashUsed = ESP.getFlashChipSize() - ESP.getFreeSketchSpace();
String flashInfo = formatBytes(flashUsed) + " / " + formatBytes(flashSize);
// Get PSRAM Info
size_t total_psram = ESP.getPsramSize();
size_t free_psram = ESP.getFreePsram();
String psramInfo = formatBytes(total_psram - free_psram) + " / " + formatBytes(total_psram);
// Get Heap Info
size_t total_heap = ESP.getHeapSize();
size_t free_heap = ESP.getFreeHeap();
String heapInfo = formatBytes(total_heap - free_heap) + " / " + formatBytes(total_heap);
// Get WiFi Info
String wifiInfo = "Not connected";
String wifiSSID = "";
String wifiChannel = "";
String wifiSignal = "";
String wifiLocalIP = "";
String wifiGatewayIP = "";
if (WiFi.status() == WL_CONNECTED) {
wifiSSID = WiFi.SSID();
wifiChannel = String(WiFi.channel());
wifiSignal = String(WiFi.RSSI()) + " dBm";
wifiLocalIP = WiFi.localIP().toString();
wifiGatewayIP = WiFi.gatewayIP().toString();
}
Serial.println("MCU: ESP32-S3FN16R8");
Serial.println("LoRa Tranciever: Semtech SX1262 (915 MHz)"); // Need to set the frequency in pins.h
Serial.println("LCD: ST7789 SPI"); // Update this
Serial.println("Chip ID: " + String(chipId, HEX));
Serial.println("MAC Address: " + macAddress);
Serial.println("Chip Info: " + chipInfo);
Serial.println("Battery:");
Serial.println(" Pin Value: " + String(analogRead(34)));
//Serial.println(" Average Pin Value: " + String(BL.pinRead()));
//Serial.println(" Volts: " + String(BL.getBatteryVolts()));
//Serial.println(" Charge Level: " + String(BL.getBatteryChargeLevel()) + "%");
Serial.println("Memory:");
Serial.println(" Flash: " + flashInfo);
Serial.println(" PSRAM: " + psramInfo);
Serial.println(" Heap: " + heapInfo);
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi Info: " + wifiInfo);
Serial.println(" SSID: " + wifiSSID);
Serial.println(" Channel: " + wifiChannel);
Serial.println(" Signal: " + wifiSignal);
Serial.println(" Local IP: " + wifiLocalIP);
Serial.println(" Gateway IP: " + wifiGatewayIP);
}
}

17
src/Utilities.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "Arduino.h"
#include "WiFi.h"
#include "Wire.h"
#include <Pangodream_18650_CL.h> // Power management
#include "pins.h"
extern Pangodream_18650_CL BL;
String formatBytes(size_t bytes);
char getKeyboardInput();
void printDeviceInfo();
void setBrightness(uint8_t value);
void updateTimeFromNTP();

View File

@ -1,5 +1,6 @@
#define logo_width 199
#define logo_height 165
static unsigned char logo_bits[] = {
0x00, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

File diff suppressed because it is too large Load Diff