diff --git a/src/main.ino b/src/main.ino
@@ -1,48 +1,57 @@
+// Standard includes
+#include <map>
+#include <time.h>
+#include <vector>
+
+// Aurduino includes
+#include <Pangodream_18650_CL.h> // Power management
#include <Preferences.h>
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <Wire.h>
-#include <Pangodream_18650_CL.h> // POWER MANAGEMENT
-#include <map>
-#include <vector>
-#include <time.h>
+// Local includes
#include "boot_screen.h"
#include "pins.h"
-#define SCREEN_WIDTH 320
-#define SCREEN_HEIGHT 240
-#define CHAR_HEIGHT 10
-#define LINE_SPACING 0
-#define INPUT_LINE_HEIGHT (CHAR_HEIGHT + LINE_SPACING)
+// 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))
-#define BOARD_BAT_ADC 4 // Define the ADC pin used for battery reading
-#define CONV_FACTOR 1.8 // Conversion factor for the ADC to voltage conversion
-#define READS 20 // Number of readings for averaging
-Pangodream_18650_CL BL(BOARD_BAT_ADC, CONV_FACTOR, READS);
+/// Struct to hold information about a WiFi network (do we want to include mac addresses)
+struct WiFiNetwork {
+ int index;
+ int channel;
+ int rssi;
+ String encryption;
+ String ssid;
+};
+// Initialize components and objects
+Pangodream_18650_CL BL(BOARD_BAT_ADC, CONV_FACTOR, READS);
+Preferences preferences;
TFT_eSPI tft = TFT_eSPI();
WiFiClientSecure client;
-std::map<String, uint32_t> nickColors;
-std::vector<String> lines;
-std::vector<bool> mentions;
-String inputBuffer = "";
-
-// WiFi credentials
+// Initialize variables
+String inputBuffer = "";
String ssid = "";
String password = "";
String nick = "";
-bool debugMode = false;
-unsigned long debugStartTime = 0;
+// Memory vectors & maps
+std::map<String, uint32_t> nickColors;
+std::vector<String> lines; // Possible rename to bufferLines ?
+std::vector<bool> mentions;
+std::vector<WiFiNetwork> wifiNetworks;
-// IRC connection
+// IRC connection (These will eventually be set dynamically when we have a settings menu)
const char* server = "irc.supernets.org";
const int port = 6697;
bool useSSL = true;
@@ -50,469 +59,373 @@ const char* channel = "#comms";
// IRC identity
const char* user = "tdeck";
-const char* realname = "ACID DROP Firmware 1.0.0"; // Need to eventually set this up to use a variable
+const char* realname = "ACID DROP Firmware v0.1.0b"; // Need to eventually set this up to use a variable
+// Timing variables
+unsigned long infoScreenStartTime = 0;
unsigned long joinChannelTime = 0;
-bool readyToJoinChannel = false;
-
unsigned long lastStatusUpdateTime = 0;
-const unsigned long STATUS_UPDATE_INTERVAL = 15000;
-
unsigned long lastActivityTime = 0;
-const unsigned long INACTIVITY_TIMEOUT = 30000; // 30 seconds
+
+// Timing constants
+const unsigned long STATUS_UPDATE_INTERVAL = 15000; // 15 seconds
+const unsigned long INACTIVITY_TIMEOUT = 30000; // 30 seconds
+
+// Dynamic variables
+bool infoScreen = false;
+bool readyToJoinChannel = false;
bool screenOn = true;
+int selectedNetworkIndex = 0;
-struct WiFiNetwork {
- int index;
- int channel;
- int rssi;
- String encryption;
- String ssid;
-};
-std::vector<WiFiNetwork> wifiNetworks;
-int selectedNetworkIndex = 0;
+// Main functions ---------------------------------------------------------------------------------
+void displayXBM() {
+ tft.fillScreen(TFT_BLACK);
+
+ // Get the center position for the logo
+ 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);
+}
-Preferences preferences;
void setup() {
+ // Initialize serial communication
Serial.begin(115200);
Serial.println("Booting device...");
- pinMode(BOARD_POWERON, OUTPUT);
+ // Turn on the power to the board
+ pinMode(BOARD_POWERON, OUTPUT);
digitalWrite(BOARD_POWERON, HIGH);
+ // Turn on power to the screen
pinMode(TFT_BL, OUTPUT);
- digitalWrite(TFT_BL, HIGH); // Turn on the backlight initially
+ digitalWrite(TFT_BL, HIGH);
+ // Start the I2C bus for the keyboard
Wire.begin(BOARD_I2C_SDA, BOARD_I2C_SCL);
+ // Initialize the screen
tft.begin();
tft.setRotation(1);
tft.invertDisplay(1);
-
Serial.println("TFT initialized");
- preferences.begin("wifi", false); // Initialize preferences with the namespace "wifi"
+ // Display the boot screen for 3 seconds
+ displayXBM();
+ delay(3000);
+
+ // Load the WiFi preferences if they exist
+ preferences.begin("wifi", false);
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
+ // Connect to WiFi if credentials are stored, otherwise scan for networks
if (ssid.length() > 0 && password.length() > 0) {
+ Serial.println("Stored WiFi credentials found");
connectToWiFi();
- if (WiFi.status() != WL_CONNECTED) {
+ if (WiFi.status() != WL_CONNECTED) // ISSUE: If we are out of range of the last stored network, it is still going to attempt to connect to it 10 times...
scanWiFiNetworks();
- displayWiFiNetworks();
- }
} else {
- displayXBM();
- delay(3000);
- displayCenteredText("SCANNING WIFI");
- delay(1000);
scanWiFiNetworks();
- displayWiFiNetworks();
}
+ // Initialize the random seed for random nick generation
randomSeed(analogRead(0));
int randomNum = random(1000, 10000);
nick = "ACID_" + String(randomNum);
}
-int renderFormattedMessage(String message, int cursorY, int lineHeight, bool highlightNick = false) {
- uint16_t fgColor = TFT_WHITE;
- uint16_t bgColor = TFT_BLACK;
- bool bold = false;
- bool underline = false;
- bool nickHighlighted = false; // Track if the nick has been highlighted
-
- for (unsigned int i = 0; i < message.length(); i++) {
- char c = message[i];
- if (c == '\x02') { // Bold
- bold = !bold;
- tft.setTextFont(bold ? 2 : 1);
- } else if (c == '\x1F') { // Underline
- underline = !underline;
- // need to add this still
- } else if (c == '\x03') { // Color
- 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);
+void loop() {
+ // Handle the info screen if it is active (from /info command)
+ if (infoScreen) {
+ if (millis() - infoScreenStartTime > 10000) { // 10 seconds
+ infoScreen = false;
+ tft.fillScreen(TFT_BLACK);
+ displayLines(); // Redraw the previous buffer
+ }
+ } else {
+ // Handle keyboard input for WiFi if the SSID is empty still (aka not connected)
+ if (ssid.isEmpty()) {
+ char incoming = getKeyboardInput();
+ if (incoming != 0) {
+ handleWiFiSelection(incoming);
+ lastActivityTime = millis(); // Reset activity timer
}
- } else if (c == '\x0F') { // Reset
- fgColor = TFT_WHITE;
- bgColor = TFT_BLACK;
- bold = false;
- underline = false;
- tft.setTextColor(fgColor, bgColor);
- tft.setTextFont(1);
} else {
- if (highlightNick && !nickHighlighted && message.substring(i).startsWith(nick)) {
- tft.setTextColor(TFT_YELLOW, bgColor); // Set both foreground and background color
- for (char nc : nick) {
- tft.print(nc);
- i++;
- }
- i--; // Adjust for the loop increment
- tft.setTextColor(TFT_WHITE, bgColor); // Reset to white foreground with current background
- nickHighlighted = true;
+ // Handle status bar updating on an interval
+ if (millis() - lastStatusUpdateTime > STATUS_UPDATE_INTERVAL) {
+ updateStatusBar();
+ lastStatusUpdateTime = millis();
+ }
+
+ // Handle IRC data
+ if (client.connected()) {
+ handleIRC();
} else {
- if (tft.getCursorX() + tft.textWidth(String(c)) > SCREEN_WIDTH) {
- cursorY += lineHeight;
- tft.setCursor(0, cursorY);
- }
- if (c == ' ') {
- // Draw background for spaces
- int spaceWidth = tft.textWidth(" ");
- tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor);
- tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY());
+ // Connect to IRC if not connected (or reconnect to wifi if disconnected)
+ if (WiFi.status() == WL_CONNECTED) {
+ displayCenteredText("CONNECTING TO " + String(server));
+ if (connectToIRC()) {
+ displayCenteredText("CONNECTED TO " + String(server));
+ sendIRC("NICK " + String(nick));
+ sendIRC("USER " + String(user) + " 0 * :" + String(realname));
+ } else {
+ displayCenteredText("CONNECTION FAILED");
+ delay(1000);
+ }
} else {
- // Ensure that background color is applied to characters
- tft.fillRect(tft.getCursorX(), tft.getCursorY(), tft.textWidth(String(c)), lineHeight, bgColor);
- tft.setTextColor(fgColor, bgColor);
- tft.print(c);
+ displayCenteredText("RECONNECTING TO WIFI");
+ WiFi.begin(ssid.c_str(), password.c_str());
}
}
+
+ // Join channel after a delay
+ if (readyToJoinChannel && millis() >= joinChannelTime) {
+ tft.fillScreen(TFT_BLACK);
+ updateStatusBar();
+ sendIRC("JOIN " + String(channel));
+ readyToJoinChannel = false;
+ }
+
+ // Handle keyboard input (for IRC)
+ char incoming = getKeyboardInput();
+ if (incoming != 0) {
+ handleKeyboardInput(incoming);
+ lastActivityTime = millis();
+ }
+
+ // Handle inactivity timeout
+ if (screenOn && millis() - lastActivityTime > INACTIVITY_TIMEOUT) {
+ turnOffScreen();
+ }
}
}
+}
- // Ensure trailing spaces are displayed with background color
- 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());
- }
+// ------------------------------------------------------------------------------------------------
+
+
+
+// WiFi functions ---------------------------------------------------------------------------------
+void connectToWiFi() {
+ Serial.println("Connecting to WiFi...");
+ WiFi.begin(ssid.c_str(), password.c_str());
+
+ // Wait for the WiFi connection to complete (or timeout after 10 seconds)
+ int attempts = 0;
+ while (WiFi.status() != WL_CONNECTED && attempts < 20) {
+ delay(500);
+ displayCenteredText("CONNECTING TO " + ssid);
+ attempts++;
}
- cursorY += lineHeight; // Add line height after printing the message
- return cursorY; // Return the new cursor Y position for the next line
-}
+ // Handle the connection result
+ if (WiFi.status() == WL_CONNECTED) {
+ Serial.println("Connected to WiFi network: " + ssid);
+ displayCenteredText("CONNECTED TO " + ssid);
-void turnOffScreen() {
- Serial.println("Screen turned off");
- tft.writecommand(TFT_DISPOFF); // Turn off display
- tft.writecommand(TFT_SLPIN); // Put display into sleep mode
- digitalWrite(TFT_BL, LOW); // Turn off the backlight
- screenOn = false;
-}
+ // Sync time with NTP server
+ updateTimeFromNTP();
-void turnOnScreen() {
- Serial.println("Screen turned on");
- digitalWrite(TFT_BL, HIGH); // Turn on the backlight
- tft.writecommand(TFT_SLPOUT); // Wake up display from sleep mode
- tft.writecommand(TFT_DISPON); // Turn on display
- screenOn = true;
+ // Store the WiFi credentials upon successful connection
+ preferences.putString("ssid", ssid);
+ preferences.putString("password", password);
+ Serial.println("Stored WiFi credentials updated");
+ } else {
+ Serial.println("Failed to connect to WiFi network: " + ssid);
+ displayCenteredText("WIFI CONNECTION FAILED");
+
+ // Clear stored credentials on failure
+ preferences.remove("ssid");
+ preferences.remove("password");
+ Serial.println("Stored WiFi credentials removed");
+
+ scanWiFiNetworks(); // Rescan for networks
+ }
}
-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;
- for (size_t i = 0; i < lines.size(); ++i) {
- const String& line = lines[i];
- bool mention = mentions[i];
-
- tft.setCursor(0, cursorY);
+void scanWiFiNetworks() {
+ Serial.println("Scanning for WiFi networks...");
+ displayCenteredText("SCANNING WIFI");
+ delay(1000); // Do we need this delay?
- 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_YELLOW);
- 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);
+ int n = WiFi.scanNetworks();
- tft.setTextColor(nickColors[senderNick]);
- tft.print(senderNick);
- tft.setTextColor(TFT_WHITE);
- cursorY = renderFormattedMessage(":" + message, cursorY, CHAR_HEIGHT, mention);
- }
+ 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);
- displayInputLine();
+ // Loop through the networks and store them in the wifiNetworks vector
+ 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";
+ net.ssid = WiFi.SSID(i);
+ wifiNetworks.push_back(net);
+ }
+
+ displayWiFiNetworks(); // Display the scanned networks
}
-void addLine(String senderNick, String message, String type, uint16_t errorColor = TFT_WHITE, uint16_t reasonColor = TFT_WHITE) {
- 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();
+void handlePasswordInput(char key) {
+ if (key == '\n' || key == '\r') { // Enter
+ password = inputBuffer;
+ inputBuffer = "";
+ connectToWiFi();
+ } else if (key == '\b') { // Backspace
+ if (inputBuffer.length() > 0) {
+ inputBuffer.remove(inputBuffer.length() - 1);
+ displayPasswordInputLine();
}
- formattedMessage = "NICK " + oldNick + " -> " + newNick;
- } else if (type == "kick") {
- formattedMessage = "KICK " + senderNick + message;
- } else if (type == "mode") {
- formattedMessage = "MODE " + message;
- } else if (type == "action") {
- formattedMessage = "* " + senderNick + " " + message;
- } else if (type == "error") {
- formattedMessage = "ERROR " + message;
- senderNick = "ERROR"; // Use ERROR as senderNick to highlight it in red
} else {
- formattedMessage = senderNick + ": " + message;
- }
-
- int linesRequired = calculateLinesRequired(formattedMessage);
-
- while (lines.size() + linesRequired > MAX_LINES) {
- lines.erase(lines.begin());
- mentions.erase(mentions.begin());
+ inputBuffer += key;
+ displayPasswordInputLine();
}
+}
- if (type == "error") {
- lines.push_back("ERROR " + message);
- mentions.push_back(false);
- } else {
- lines.push_back(formattedMessage);
- mentions.push_back(false);
- }
- displayLines();
+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 displayDeviceInfo() {
- tft.fillScreen(TFT_BLACK);
- printDeviceInfo();
+
+void updateSelectedNetwork(int delta) {
+ int newIndex = selectedNetworkIndex + delta;
+ if (newIndex >= 0 && newIndex < wifiNetworks.size()) {
+ selectedNetworkIndex = newIndex;
+ displayWiFiNetworks();
+ }
}
-void loop() {
- if (debugMode) {
- if (millis() - debugStartTime > 10000) { // 10 seconds
- debugMode = false;
- // Clear the screen and return to the IRC interface
- tft.fillScreen(TFT_BLACK);
- displayLines();
- }
- } else {
- if (ssid.isEmpty()) {
- char incoming = getKeyboardInput();
- if (incoming != 0) {
- handleWiFiSelection(incoming);
- lastActivityTime = millis(); // Reset activity timer
- }
- } else {
- if (millis() - lastStatusUpdateTime > STATUS_UPDATE_INTERVAL) {
- updateStatusBar();
- lastStatusUpdateTime = millis();
- }
- if (client.connected()) {
- handleIRC();
- } else {
- if (WiFi.status() == WL_CONNECTED) {
- displayCenteredText("CONNECTING TO " + String(server));
- if (connectToIRC()) {
- displayCenteredText("CONNECTED TO " + String(server));
- sendIRC("NICK " + String(nick));
- sendIRC("USER " + String(user) + " 0 * :" + String(realname));
- } else {
- displayCenteredText("CONNECTION FAILED");
- delay(1000);
- }
- } else {
- displayCenteredText("RECONNECTING TO WIFI");
- WiFi.begin(ssid.c_str(), password.c_str());
- }
- }
+void displayWiFiNetworks() {
+ tft.fillRect(0, STATUS_BAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - STATUS_BAR_HEIGHT, TFT_BLACK); // Clear the screen (except the status bar area)
- if (readyToJoinChannel && millis() >= joinChannelTime) {
- tft.fillScreen(TFT_BLACK);
- updateStatusBar();
- sendIRC("JOIN " + String(channel));
- readyToJoinChannel = false;
- }
+ // Set the header for the WiFi networks
+ tft.setTextSize(1);
+ tft.setTextColor(TFT_CYAN);
+ tft.setCursor(0, STATUS_BAR_HEIGHT);
+ tft.printf("# CH RSSI ENC SSID\n");
+ tft.setTextColor(TFT_WHITE);
- char incoming = getKeyboardInput();
- if (incoming != 0) {
- handleKeyboardInput(incoming);
- lastActivityTime = millis(); // Reset activity timer
- }
+ // Calculate the starting index and max displayed lines
+ int maxDisplayedLines = (SCREEN_HEIGHT - STATUS_BAR_HEIGHT - CHAR_HEIGHT) / (CHAR_HEIGHT + LINE_SPACING);
+ int startIdx = selectedNetworkIndex >= maxDisplayedLines ? selectedNetworkIndex - maxDisplayedLines + 1 : 0;
- // Check for inactivity
- if (screenOn && millis() - lastActivityTime > INACTIVITY_TIMEOUT) {
- turnOffScreen(); // Turn off screen and backlight
- }
- }
+ // Display the WiFi networks on the screen
+ for (int i = startIdx; i < wifiNetworks.size() && i < startIdx + maxDisplayedLines; ++i) {
+ displayWiFiNetwork(i, i - startIdx + 1); // +1 to account for the header
}
}
-bool connectToIRC() {
- Serial.println("Connecting to IRC...");
- if (useSSL) {
- client.setInsecure();
- return client.connect(server, port);
- } else {
- WiFiClient nonSecureClient;
- return nonSecureClient.connect(server, port);
- }
-}
-void connectToWiFi() {
- WiFi.begin(ssid.c_str(), password.c_str());
- Serial.println("Connecting to WiFi...");
- int attempts = 0;
- while (WiFi.status() != WL_CONNECTED && attempts < 10) { // Try to connect for up to 10 seconds
- delay(500);
- displayCenteredText("CONNECTING TO " + ssid);
- attempts++;
- }
- if (WiFi.status() == WL_CONNECTED) {
- Serial.println("Connected to WiFi network: " + ssid);
- displayCenteredText("CONNECTED TO " + ssid);
- delay(1000);
- updateTimeFromNTP();
+void displayWiFiNetwork(int index, int displayIndex) {
+ int y = STATUS_BAR_HEIGHT + displayIndex * (CHAR_HEIGHT + LINE_SPACING);
+ tft.setCursor(0, y);
- // Save WiFi credentials to preferences
- preferences.putString("ssid", ssid);
- preferences.putString("password", password);
- } else {
- displayCenteredText("WIFI CONNECTION FAILED");
- Serial.println("Failed to connect to WiFi.");
-
- // Clear stored credentials
- preferences.remove("ssid");
- preferences.remove("password");
+ // Set the text color based on the selected network
+ if (index == selectedNetworkIndex)
+ tft.setTextColor(TFT_GREEN, TFT_BLACK);
+ else
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
- // Display WiFi networks for selection
- scanWiFiNetworks();
- displayWiFiNetworks();
- }
-}
+ WiFiNetwork net = wifiNetworks[index];
+ // Index and channel number
+ uint16_t rssiColor = getColorFromPercentage((int32_t)net.rssi);
+ tft.printf("%-2d %-3d ", net.index, net.channel);
-void sendIRC(String command) {
- if (client.println(command))
- Serial.println("IRC: >>> " + command);
- else
- Serial.println("Failed to send: " + command);
+ // RSSI
+ tft.setTextColor(rssiColor, TFT_BLACK);
+ tft.printf("%-5d ", net.rssi);
+
+ // Encryption and SSID
+ tft.setTextColor(index == selectedNetworkIndex ? TFT_GREEN : TFT_WHITE, TFT_BLACK);
+ tft.printf("%-8s %s", net.encryption.c_str(), net.ssid.c_str());
}
+
+void handleWiFiSelection(char key) {
+ if (key == 'u') {
+ updateSelectedNetwork(-1);
+ } else if (key == 'd') {
+ updateSelectedNetwork(1);
+ } else if (key == '\n' || key == '\r') {
+ 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();
+ // Switch to password input mode
+ while (true) {
+ char incoming = getKeyboardInput();
+ if (incoming != 0) {
+ handlePasswordInput(incoming);
+ if (incoming == '\n' || incoming == '\r') {
+ break;
+ }
+ }
+ }
+ } else {
+ password = "";
+ connectToWiFi();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+
+
+
+// IRC functions ----------------------------------------------------------------------------------
+bool connectToIRC() {
+ if (useSSL) {
+ Serial.println("Connecting to IRC with TLS...");
+ client.setInsecure();
+ return client.connect(server, port);
+ } else {
+ Serial.println("Connecting to IRC...");
+ WiFiClient nonSecureClient;
+ return nonSecureClient.connect(server, port);
+ }
+}
+
+
+void sendIRC(String command) {
+ 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");
+ }
+}
+
+
void handleIRC() {
while (client.available()) {
String line = client.readStringUntil('\n');
@@ -538,192 +451,78 @@ void handleIRC() {
sendIRC(pingResponse);
} else {
parseAndDisplay(line);
- lastActivityTime = millis(); // Reset activity timer
+ lastActivityTime = millis();
}
}
}
-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(nick) != -1;
- if (message.startsWith("\x01ACTION ") && 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" && line.indexOf(channel) != -1) {
- 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 == "") { // Our own NICK changes
- addLine(nick, " -> " + newNick, "nick");
- nick = newNick;
- } else { // Other peoples NICK change
- String oldNick = prefix.substring(0, prefix.indexOf('!'));
- if (oldNick == nick) {
- nick = newNick; // Update global nick when we get confirmation
- }
- addLine(oldNick, " -> " + newNick, "nick");
- }
- } 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") { // ERR_ERRONEUSNICKNAME
- addLine("ERROR", "ERR_ERRONEUSNICKNAME", "error", TFT_RED, TFT_DARKGREY);
- } else if (command == "433") { // ERR_NICKNAMEINUSE
- addLine("ERROR", "ERR_NICKNAMEINUSE", "error", TFT_RED, TFT_DARKGREY);
- }
- }
-}
+// Indepentent functions (does not rely on any other functions) -----------------------------------
+int calculateLinesRequired(String message) {
+ int linesRequired = 1;
+ int lineWidth = 0;
-void handleKeyboardInput(char key) {
- if (key == '\n' || key == '\r') { // Enter
- if (inputBuffer.startsWith("/nick ")) {
- String newNick = inputBuffer.substring(6);
- sendIRC("NICK " + newNick);
- inputBuffer = "";
- displayInputLine();
- } else if (inputBuffer.startsWith("/debug")) {
- debugMode = true;
- debugStartTime = millis();
- displayDeviceInfo();
- inputBuffer = "";
- } else if (inputBuffer.startsWith("/raw ")) {
- String rawCommand = inputBuffer.substring(5);
- sendRawCommand(rawCommand);
- } else if (inputBuffer.startsWith("/me ")) {
- String actionMessage = inputBuffer.substring(4);
- sendIRC("PRIVMSG " + String(channel) + " :\001ACTION " + actionMessage + "\001");
- addLine(nick, actionMessage, "action");
- inputBuffer = "";
- } else {
- sendIRC("PRIVMSG " + String(channel) + " :" + inputBuffer);
- addLine(nick, inputBuffer, "message");
- }
- inputBuffer = "";
- displayInputLine();
- lastActivityTime = millis();
- if (!screenOn) {
- turnOnScreen();
- }
- } else if (key == '\b') { // Backspace
- if (inputBuffer.length() > 0) {
- inputBuffer.remove(inputBuffer.length() - 1);
- displayInputLine();
- lastActivityTime = millis();
- if (!screenOn) {
- turnOnScreen();
- }
- }
- } else {
- inputBuffer += key;
- displayInputLine();
- lastActivityTime = millis();
- if (!screenOn) {
- turnOnScreen();
+ for (char c : message) {
+ lineWidth += tft.textWidth(String(c));
+ if (lineWidth > SCREEN_WIDTH) {
+ linesRequired++;
+ lineWidth = tft.textWidth(String(c));
}
}
-}
-void sendRawCommand(String command) {
- if (client.connected()) {
- sendIRC(command);
- Serial.println("Sent raw command: " + command);
- } else {
- Serial.println("Failed to send raw command: Not connected to IRC");
- }
+ return linesRequired;
}
-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 displayXBM() {
+void displayCenteredText(String text) {
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);
+ 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);
- int inputWidth = SCREEN_WIDTH - tft.textWidth("> ");
- String displayInput = inputBuffer;
- int displayWidth = tft.textWidth(displayInput);
+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";
+}
- // Scrolling input text
- while (displayWidth > inputWidth) {
- displayInput = displayInput.substring(1);
- displayWidth = tft.textWidth(displayInput);
- }
- tft.print("> " + displayInput);
+uint32_t generateRandomColor() {
+ return tft.color565(random(0, 255), random(0, 255), random(0, 255));
}
-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);
+
+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;
}
-int calculateLinesRequired(String message) {
- int linesRequired = 1;
- int lineWidth = 0;
- for (char c : message) {
- lineWidth += tft.textWidth(String(c));
- if (lineWidth > SCREEN_WIDTH) {
- linesRequired++;
- lineWidth = tft.textWidth(String(c));
+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 linesRequired;
+ return 0;
}
+
uint16_t getColorFromCode(int colorCode) {
switch (colorCode) {
case 0: return TFT_WHITE;
@@ -830,148 +629,465 @@ uint16_t getColorFromCode(int colorCode) {
}
-uint32_t generateRandomColor() {
- return tft.color565(random(0, 255), random(0, 255), random(0, 255));
+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 updateTimeFromNTP() {
+ Serial.println("Syncing time with NTP server...");
+ configTime(-5 * 3600, 0, "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?
+}
+
+// ------------------------------------------------------------------------------------------------
+
+
+
+// Logic and Interface functions ------------------------------------------------------------------
+int renderFormattedMessage(String message, int cursorY, int lineHeight, bool highlightNick = false) {
+ uint16_t fgColor = TFT_WHITE;
+ uint16_t bgColor = TFT_BLACK;
+ bool bold = false;
+ bool underline = false;
+ bool nickHighlighted = false; // Track if the nick has been highlighted
+
+ for (unsigned int i = 0; i < message.length(); i++) {
+ char c = message[i];
+ if (c == '\x02') { // Bold
+ bold = !bold;
+
+ // Bold text is not supported by the current font...it makes the entire screen bold
+ //tft.setTextFont(bold ? 2 : 1);
+ } else if (c == '\x1F') { // Underline
+ underline = !underline;
+ // need to add this still
+ } else if (c == '\x03') { // Color
+ 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') { // Reset
+ fgColor = TFT_WHITE;
+ bgColor = TFT_BLACK;
+ bold = false;
+ underline = false;
+ tft.setTextColor(fgColor, bgColor);
+ tft.setTextFont(1);
+ } else {
+ if (highlightNick && !nickHighlighted && message.substring(i).startsWith(nick)) {
+ tft.setTextColor(TFT_YELLOW, bgColor); // Set both foreground and background color
+ for (char nc : nick) {
+ tft.print(nc);
+ i++;
+ }
+ i--; // Adjust for the loop increment
+ tft.setTextColor(TFT_WHITE, bgColor); // Reset to white foreground with current background
+ nickHighlighted = true;
+ } else {
+ if (tft.getCursorX() + tft.textWidth(String(c)) > SCREEN_WIDTH) {
+ cursorY += lineHeight;
+ tft.setCursor(0, cursorY);
+ }
+ if (c == ' ') {
+ // Draw background for spaces
+ int spaceWidth = tft.textWidth(" ");
+ tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor);
+ tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY());
+ } else {
+ // Ensure that background color is applied to characters
+ tft.fillRect(tft.getCursorX(), tft.getCursorY(), tft.textWidth(String(c)), lineHeight, bgColor);
+ tft.setTextColor(fgColor, bgColor);
+ tft.print(c);
+ }
+ }
+ }
+ }
+
+ // Ensure trailing spaces are displayed with background color
+ 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; // Add line height after printing the message
+ return cursorY; // Return the new cursor Y position for the next line
+}
+
+
+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;
+ 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_YELLOW);
+ 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 addLine(String senderNick, String message, String type, uint16_t errorColor = TFT_WHITE, uint16_t reasonColor = TFT_WHITE) {
+ 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;
+ } else if (type == "action") {
+ formattedMessage = "* " + senderNick + " " + message;
+ } else if (type == "error") {
+ formattedMessage = "ERROR " + message;
+ senderNick = "ERROR"; // Use ERROR as senderNick to highlight it in red
+ } 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(false);
+ }
+
+ displayLines();
+}
+
+
+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(nick) != -1;
+
+ 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" && line.indexOf(channel) != -1) {
+ 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 == "") { // Our own NICK changes
+ addLine(nick, " -> " + newNick, "nick");
+ nick = newNick;
+ } else { // Other peoples NICK change
+ String oldNick = prefix.substring(0, prefix.indexOf('!'));
+ if (oldNick == nick)
+ nick = newNick; // Update global nick when we get confirmation
+ addLine(oldNick, " -> " + newNick, "nick");
+ }
+ } 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") { // ERR_ERRONEUSNICKNAME
+ addLine("ERROR", "ERR_ERRONEUSNICKNAME", "error", TFT_RED, TFT_DARKGREY);
+ } else if (command == "433") { // ERR_NICKNAMEINUSE
+ addLine("ERROR", "ERR_NICKNAMEINUSE", "error", TFT_RED, TFT_DARKGREY);
+ }
+ }
}
-void handlePasswordInput(char key) {
+
+void handleKeyboardInput(char key) {
if (key == '\n' || key == '\r') { // Enter
- password = inputBuffer;
+ if (inputBuffer.startsWith("/nick ")) {
+ String newNick = inputBuffer.substring(6);
+ sendIRC("NICK " + newNick);
+ inputBuffer = "";
+ displayInputLine();
+ } else if (inputBuffer.startsWith("/info")) {
+ infoScreen = true;
+ infoScreenStartTime = millis();
+ 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(nick, actionMessage, "action");
+ inputBuffer = "";
+ } else {
+ sendIRC("PRIVMSG " + String(channel) + " :" + inputBuffer);
+ addLine(nick, inputBuffer, "message");
+ }
inputBuffer = "";
- connectToWiFi();
+ displayInputLine();
+ lastActivityTime = millis();
+ if (!screenOn)
+ turnOnScreen();
} else if (key == '\b') { // Backspace
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
- displayPasswordInputLine();
+ displayInputLine();
+ lastActivityTime = millis();
+ if (!screenOn)
+ turnOnScreen();
}
} else {
inputBuffer += key;
- displayPasswordInputLine();
+ displayInputLine();
+ lastActivityTime = millis();
+ if (!screenOn) {
+ turnOnScreen();
+ }
}
}
-void displayPasswordInputLine() {
+
+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);
- tft.print("> " + inputBuffer);
-}
-
-void updateSelectedNetwork(int delta) {
- int newIndex = selectedNetworkIndex + delta;
- if (newIndex >= 0 && newIndex < wifiNetworks.size()) {
- selectedNetworkIndex = newIndex;
- displayWiFiNetworks();
- }
-}
-
-void scanWiFiNetworks() {
- Serial.println("Scanning for WiFi networks...");
- int n = WiFi.scanNetworks();
- 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";
- net.ssid = WiFi.SSID(i);
- wifiNetworks.push_back(net);
- }
-}
-
-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); // +1 to account for the header
- }
-}
-void displayWiFiNetwork(int index, int displayIndex) {
- int y = STATUS_BAR_HEIGHT + displayIndex * (CHAR_HEIGHT + LINE_SPACING);
- tft.setCursor(0, y);
+ int inputWidth = SCREEN_WIDTH - tft.textWidth("> ");
+ String displayInput = inputBuffer;
+ int displayWidth = tft.textWidth(displayInput);
- if (index == selectedNetworkIndex) {
- tft.setTextColor(TFT_GREEN, TFT_BLACK);
- } else {
- tft.setTextColor(TFT_WHITE, TFT_BLACK);
+ // Allow the input to scroll if it exceeds the screen width
+ while (displayWidth > inputWidth) {
+ displayInput = displayInput.substring(1);
+ displayWidth = tft.textWidth(displayInput);
}
- 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); // Set RSSI color
- 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());
+ tft.print("> " + displayInput);
}
-void handleWiFiSelection(char key) {
- if (key == 'u') {
- updateSelectedNetwork(-1);
- } else if (key == 'd') {
- updateSelectedNetwork(1);
- } else if (key == '\n' || key == '\r') {
- 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();
- // Switch to password input mode
- while (true) {
- char incoming = getKeyboardInput();
- if (incoming != 0) {
- handlePasswordInput(incoming);
- if (incoming == '\n' || incoming == '\r') {
- break;
- }
- }
- }
- } else {
- password = "";
- connectToWiFi();
- }
- }
-}
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);
+
+ // Display the time
struct tm timeinfo;
char timeStr[9];
if (!getLocalTime(&timeinfo)) {
- // Default time if NTP sync fails
- sprintf(timeStr, "12:00 AM");
+ sprintf(timeStr, "12:00 AM"); // Default time if NTP sync fails
} else {
int hour = timeinfo.tm_hour;
char ampm[] = "AM";
if (hour == 0) {
hour = 12;
} else if (hour >= 12) {
- if (hour > 12) {
+ if (hour > 12)
hour -= 12;
- }
- strcpy(ampm, "PM");
+ strcpy(ampm, "PM"); // DREADED STRCPY X_X
}
sprintf(timeStr, "%02d:%02d %s", hour, timeinfo.tm_min, ampm);
}
@@ -979,6 +1095,7 @@ void updateStatusBar() {
tft.setTextColor(TFT_WHITE, darkerGrey);
tft.drawString(timeStr, 0, STATUS_BAR_HEIGHT / 2);
+ // Display the WiFi signal strength
char wifiStr[15];
int wifiSignal = 0;
if (WiFi.status() != WL_CONNECTED) {
@@ -1003,6 +1120,7 @@ void updateStatusBar() {
tft.drawString(wifiStr + 6, SCREEN_WIDTH - 100, STATUS_BAR_HEIGHT / 2);
}
+ // Display the battery level
int batteryLevel = BL.getBatteryChargeLevel();
char batteryStr[15];
sprintf(batteryStr, "Batt: %d%%", batteryLevel);
@@ -1013,50 +1131,17 @@ void updateStatusBar() {
tft.drawString(batteryStr + 5, SCREEN_WIDTH - 5, STATUS_BAR_HEIGHT / 2);
}
-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 updateTimeFromNTP() {
- Serial.println("Syncing time with NTP server...");
- configTime(-5 * 3600, 0, "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.");
-}
-
-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";
-}
void printDeviceInfo() {
+ tft.fillScreen(TFT_BLACK);
+
// 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(); // Unique ID
+ 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";
diff --git a/src/pins.h b/src/pins.h
@@ -1,8 +1,7 @@
-
#pragma once
-// board peripheral power control pin needs to be set to HIGH when using the peripheral
+// Board pin definitions ------------------------
#define BOARD_POWERON 10
#define BOARD_I2S_WS 5
@@ -46,9 +45,17 @@
#define BOARD_BL_PIN 42
+#define GPS_RX_PIN 44
+#define GPS_TX_PIN 43
+
+
+// Other definitions ----------------------------
+#define SCREEN_WIDTH 320
+#define SCREEN_HEIGHT 240
+// Battery definitions
+#define CONV_FACTOR 1.8 // Conversion factor for the ADC to voltage conversion
+#define READS 20 // Number of readings for averaging
#define LILYGO_KB_SLAVE_ADDRESS 0x55
-#define GPS_RX_PIN 44
-#define GPS_TX_PIN 43
-\ No newline at end of file
| |