#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include "BluetoothSerial.h"
// SH110X OLED Display
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SH110X_I2C_ADDRESS 0x3C
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int contrastValue = 50;
// GPIO pins for buttons
#define OK_BUTTON_PIN 32
BluetoothSerial SerialBT;
bool confirmRequestDone = false;
bool pairingEnabled = false;
bool btConnected = false; // Flag to check Bluetooth connection status
//#define AUTO_PAIR // Uncomment to automatically authenticate ESP32 side
// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif
const char *deviceName = "ESP32_BT_CHAT";
// The following lines define the method of pairing
const bool INPUT_CAPABILITY = true; // Defines if ESP32 device has input method (Serial terminal, keyboard or similar)
const bool OUTPUT_CAPABILITY = true; // Defines if ESP32 device has output method (Serial terminal, display or similar)
void BTConfirmRequestCallback(uint32_t numVal) {
confirmRequestDone = false;
#ifndef AUTO_PAIR
// Display the PIN on the OLED
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 10);
display.printf("PIN:%06lu", numVal); // Show the generated 6-digit PIN
display.setTextSize(1);
display.setCursor(15, 50);
display.println("Press OK to pair");
display.display();
Serial.printf("The PIN is: %06lu. If it matches the number displayed on the other device, write 'Y' or 'y':\n", numVal);
while (true) {
// Check if OK button is pressed
if (digitalRead(OK_BUTTON_PIN) == HIGH) { // Active-low button
delay(100); // Debounce delay
if (digitalRead(OK_BUTTON_PIN) == HIGH) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(20, 30);
display.println("Pairing allowed");
display.display();
delay(1000);
Serial.println("OK Button pressed, confirming pairing...");
SerialBT.confirmReply(true);
break;
}
}
// Check if data is available on the Serial Monitor
if (Serial.available()) {
int dat = Serial.read();
if (dat == 'Y' || dat == 'y') {
Serial.println("Serial input received, confirming pairing...");
SerialBT.confirmReply(true);
} else {
Serial.println("Invalid input, rejecting pairing...");
SerialBT.confirmReply(false);
}
break;
}
delay(1); // Feed the watchdog
}
#else
SerialBT.confirmReply(true);
#endif
}
void BTAuthCompleteCallback(boolean success) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(10, 30);
if (success) {
display.println("Pairing successful!");
Serial.println("Pairing success!!");
} else {
display.setCursor(20, 30);
display.println("Pairing failed!");
Serial.println("Pairing failed, rejected by user!!");
}
display.display();
}
void handlePairingTask() {
if (Serial.available()) {
SerialBT.write(Serial.read());
}
if (SerialBT.available()) {
Serial.write(SerialBT.read());
}
delay(20);
}
void chatTitle() {
// Draw the title
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(5, 0); // Title is always at the top
display.print("== BLUETOOTH CHAT ==");
display.display();
}
#define MAX_MESSAGES 5 // Maximum number of visible message lines (not messages)
// Buffer to hold chat messages (split into lines)
String chatBuffer[MAX_MESSAGES];
int messageCount = 0; // Number of message lines currently in the buffer
int yPos = 14; // Start yPos below the title
const int maxCharsPerLine = 20; // Adjust this based on your OLED width
const int lineHeight = 10; // Height of each text line
void splitAndAddMessage(const String& fullMessage) {
//const int lineHeight = 10; // Height of each text line
int start = 0;
// Split message into multiple lines if it exceeds maxCharsPerLine
while (start < fullMessage.length()) {
// Extract substring for the current line
String line = fullMessage.substring(start, start + maxCharsPerLine);
// Add the line to the buffer
if (messageCount >= MAX_MESSAGES) {
// Shift all previous lines up to make room for the new line
for (int i = 0; i < MAX_MESSAGES - 1; i++) {
chatBuffer[i] = chatBuffer[i + 1];
}
messageCount--;
}
chatBuffer[messageCount++] = line; // Add the new line
start += maxCharsPerLine; // Move to the next part of the message
}
}
void displayChat(const char* sender, const String& message) {
// Format the message with sender's name
String fullMessage = String(sender) + ":" + message;
// Split the message into lines and add to the buffer
splitAndAddMessage(fullMessage);
// Clear the display and redraw
display.clearDisplay();
// Draw the title
chatTitle(); // Redraw the title after clearing the display
// Draw the chat messages from the buffer
for (int i = 0; i < messageCount; i++) {
display.setCursor(0, yPos + (i * lineHeight));
display.print(chatBuffer[i]);
}
display.display(); // Refresh the display with updated content
}
enum DisplayState {
DISPLAY_NONE,
DISPLAY_LIKE,
DISPLAY_SHARE,
DISPLAY_SUBSCRIBE
};
DisplayState currentDisplayState = DISPLAY_NONE;
unsigned long displayStartTime = 0;
const unsigned long displayDuration = 1000; // Duration to show each message
void serial_response() {
static String mobileMessage = ""; // Buffer for mobile messages
static String esp32Message = ""; // Buffer for ESP32 messages
// Read from Serial Monitor (ESP32 to Mobile)
while (Serial.available()) {
char c = Serial.read();
esp32Message += c; // Append to ESP32 message buffer
if (c == '\n') { // End of message
String trimmedESP32Message = esp32Message; // Create a copy for trimming
trimmedESP32Message.trim(); // Trim the message
if (trimmedESP32Message == "ClearChat") {
// Clear screen and delete old messages
display.clearDisplay();
chatTitle(); // Redraw the title after clearing the display
for (int i = 0; i < MAX_MESSAGES; i++) {
chatBuffer[i] = ""; // Clear message buffer
}
messageCount = 0; // Reset message count
Serial.println("Screen cleared from ESP");
} else if (trimmedESP32Message == "Like") {
// Start displaying "LIKE", "SHARE", "SUBSCRIBE"
currentDisplayState = DISPLAY_LIKE;
displayStartTime = millis();
display.clearDisplay();
display.setTextSize(2);
display.setCursor(35, 25); // Adjust the position as needed
display.print("LIKE!");
display.display();
Serial.println("LIKE received from ESP");
} else {
SerialBT.print(esp32Message); // Send to mobile
displayChat("ESP", esp32Message); // Display on OLED
Serial.print("ESP: "); // Debug output
Serial.println(esp32Message);
}
esp32Message = ""; // Clear buffer
}
}
// Read from Bluetooth (Mobile to ESP32)
while (SerialBT.available()) {
char c = SerialBT.read();
mobileMessage += c; // Append to mobile message buffer
if (c == '\n') { // End of message
String trimmedMobileMessage = mobileMessage; // Create a copy for trimming
trimmedMobileMessage.trim(); // Trim the message
if (trimmedMobileMessage == "ClearChat") {
// Clear screen and delete old messages
display.clearDisplay();
chatTitle(); // Redraw the title after clearing the display
for (int i = 0; i < MAX_MESSAGES; i++) {
chatBuffer[i] = ""; // Clear message buffer
}
messageCount = 0; // Reset message count
Serial.println("Screen cleared from MOB");
} else if (trimmedMobileMessage == "Like") {
// Start displaying "LIKE", "SHARE", "SUBSCRIBE"
currentDisplayState = DISPLAY_LIKE;
displayStartTime = millis();
display.clearDisplay();
display.setTextSize(2);
display.setCursor(35, 25); // Adjust the position as needed
display.print("LIKE!");
display.display();
Serial.println("LIKE received from MOB");
} else {
Serial.print("MOB: "); // Debug output
Serial.println(mobileMessage);
displayChat("MOB", mobileMessage); // Display on OLED
}
mobileMessage = ""; // Clear buffer
}
}
// Handle display states
if (currentDisplayState != DISPLAY_NONE && millis() - displayStartTime >= displayDuration) {
switch (currentDisplayState) {
case DISPLAY_LIKE:
currentDisplayState = DISPLAY_SHARE;
displayStartTime = millis();
display.clearDisplay();
display.setTextSize(2);
display.setCursor(30, 25); // Adjust the position as needed
display.print("SHARE!");
display.display();
break;
case DISPLAY_SHARE:
currentDisplayState = DISPLAY_SUBSCRIBE;
displayStartTime = millis();
display.clearDisplay();
display.setTextSize(2);
display.setCursor(5, 25); // Adjust the position as needed
display.print("SUBSCRIBE!");
display.display();
break;
case DISPLAY_SUBSCRIBE:
currentDisplayState = DISPLAY_NONE; // End of the cycle
//chatTitle(); // Redraw the title
break;
default:
break;
}
}
delay(10); // Prevent flooding
}
void setup() {
Serial.begin(115200);
// Initialize SH1106G OLED Display
if (!display.begin(SH110X_I2C_ADDRESS, true)) {
Serial.println(F("Error initializing OLED display"));
while (true);
} else {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.setCursor(17, 25);
display.print("PAKFONES");
display.display();
delay(1000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(34, 25);
display.print("LOADING");
// Display animation dots
for (int i = 0; i < 3; i++) {
delay(600); // 0.6 second delay
display.print(".");
display.display();
}
}
// Set the display brightness using the Adafruit library
display.setContrast(contrastValue);
pinMode(OK_BUTTON_PIN, INPUT_PULLDOWN);
// Initialize Bluetooth
SerialBT.enableSSP(); // Enable Secure Simple Pairing (SSP)
SerialBT.onConfirmRequest(BTConfirmRequestCallback);
SerialBT.onAuthComplete(BTAuthCompleteCallback);
SerialBT.begin(deviceName); // Initiate Bluetooth device with name in parameter
//SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 10);
display.println("BLUETOOTH CHAT!");
display.setCursor(55, 27);
display.println("BY");
display.setTextSize(2);
display.setCursor(17, 45);
display.print("PAKFONES");
display.display();
Serial.printf("The device started with name \"%s\", now you can pair it with Bluetooth!\n", deviceName);
if (INPUT_CAPABILITY and OUTPUT_CAPABILITY) {
Serial.println("Both devices will display randomly generated code and if they match authenticate pairing on both devices");
} else if (not INPUT_CAPABILITY and not OUTPUT_CAPABILITY) {
Serial.println("Authenticate pairing on the other device. No PIN is used");
} else if (not INPUT_CAPABILITY and OUTPUT_CAPABILITY) {
Serial.println("Authenticate pairing on the other device. No PIN is used");
} else if (INPUT_CAPABILITY and not OUTPUT_CAPABILITY) {
Serial.println("After pairing is initiated you will be required to enter the passkey to the ESP32 device to authenticate\n > The Passkey will displayed on the other device");
}
}
void loop() {
serial_response();
if (SerialBT.hasClient() && !btConnected) {
btConnected = true;
Serial.println("Bluetooth client connected");
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 20);
display.print("BLUETOOTH CHAT");
display.setCursor(20, 35);
display.print("IS READY NOW!");
display.display();
} else if (!SerialBT.hasClient() && btConnected) {
btConnected = false;
Serial.println("Bluetooth client disconnected");
}
if (confirmRequestDone) {
handlePairingTask();
} else {
delay(1);
}
}
Loading
grove-oled-sh1107
grove-oled-sh1107