#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>
#define VIBRATION_SENSOR_PIN 5
#define LED_PIN 13
#define LED_COUNT 3
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
volatile bool vibrationDetected = false;
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* csvUrl = "https://raw.githubusercontent.com/rloucks/test8ball/main/8ball2.txt";
String cachedCSVData;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
unsigned long lastLedUpdate = 0; // Variable to keep track of the last LED update time
const unsigned long ledInterval = 10; // Interval between LED updates in milliseconds
void vibrationISR() {
vibrationDetected = true;
}
void setup() {
pinMode(VIBRATION_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(VIBRATION_SENSOR_PIN), vibrationISR, RISING);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
Serial.begin(9600);
connectToWiFi();
cacheCSVData();
}
void loop() {
display8Ball();
displayShakeAnimation();
waitForVibration();
fetchAndDisplayData();
}
void connectToWiFi() {
Serial.println("Connecting to WiFi...");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 20);
display.print("Connecting.");
display.display();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println(".");
display.print(".");
display.display();
}
Serial.println("WiFi connected!");
}
void cacheCSVData() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 20);
display.print("Filling the ball with sweet data");
display.display();
HTTPClient http;
http.begin(csvUrl);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
cachedCSVData = http.getString();
Serial.println("CSV data cached successfully.");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 20);
display.print("All totes mcgoats!");
display.display();
delay(500);
} else {
Serial.println("Failed to cache CSV data.");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 20);
display.print("Failed, Sadly");
display.display();
}
http.end();
}
void displayShakeAnimation() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(20, 20);
display.println("Shake the 8Ball");
display.display();
rainbowCycle();
}
void waitForVibration() {
while (!vibrationDetected) {
delay(10);
}
vibrationDetected = false; // Reset flag
delay(100);
}
void fetchAndDisplayData() {
// Use the cached CSV data instead of fetching it from the server
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, cachedCSVData);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str()); // Print error message to Serial Monitor
return;
}
JsonArray array = doc.as<JsonArray>();
int rowCount = array.size();
int randomIndex = random(0, rowCount);
JsonObject row = array[randomIndex];
String voice = row["Voice"].as<String>();
String mood = row["Mood"].as<String>();
String response = row["Response"].as<String>();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 10);
if (mood == "Positive") {
fadePositive();
display.println("Yes...");
}
if (mood == "Negative") {
fadeNegative();
display.println("No...");
}
if (mood == "Neutral") {
fadeNeutral();
display.println("Maybe...");
}
display.display();
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(response);
display.display();
delay(8000);
}
void rainbowCycle() {
unsigned long currentTime = millis(); // Get the current time
if (currentTime - lastLedUpdate >= ledInterval) { // Check if it's time to update LEDs
lastLedUpdate = currentTime; // Update last LED update time
static uint16_t j = 0;
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
j++;
if (j >= 256 * 5) { // 5 cycles of all colors on wheel
j = 0;
}
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void fadeNegative() {
unsigned long currentTime = millis(); // Get the current time
if (currentTime - lastLedUpdate >= ledInterval) { // Check if it's time to update LEDs
lastLedUpdate = currentTime; // Update last LED update time
static int i = 0;
uint32_t color = strip.Color(i, i / 2, 0); // Red to Orange gradient
for (int j = 0; j < LED_COUNT; j++) {
strip.setPixelColor(j, color);
}
strip.show();
i++;
if (i > 255) {
i = 0;
}
}
}
void fadePositive() {
unsigned long currentTime = millis(); // Get the current time
if (currentTime - lastLedUpdate >= ledInterval) { // Check if it's time to update LEDs
lastLedUpdate = currentTime; // Update last LED update time
static int i = 0;
uint32_t color = strip.Color(0, i, 255 - i); // Green to Blue gradient
for (int j = 0; j < LED_COUNT; j++) {
strip.setPixelColor(j, color);
}
strip.show();
i++;
if (i > 255) {
i = 0;
}
}
}
void fadeNeutral() {
unsigned long currentTime = millis(); // Get the current time
if (currentTime - lastLedUpdate >= ledInterval) { // Check if it's time to update LEDs
lastLedUpdate = currentTime; // Update last LED update time
static int i = 0;
uint32_t color = strip.Color(255, 255, i); // Yellow to Purple gradient
for (int j = 0; j < LED_COUNT; j++) {
strip.setPixelColor(j, color);
}
strip.show();
i++;
if (i > 255) {
i = 0;
}
}
}
void display8Ball() { // Number of times to shake the 8ball
int shakeIterations = 5;
for (int i = 0; i < shakeIterations; i++) {
// Clear the display
display.clearDisplay();
// Draw the white circle representing the 8ball at different positions
display.fillCircle(SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / 2 - 2, SSD1306_WHITE);
// Draw a smaller black circle in the center
display.fillCircle(SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT / 2, (SCREEN_HEIGHT / 2) / 2, SSD1306_BLACK);
// Set the font size and color for the number 8
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
// Draw the number 8 in the center of the black circle
display.setCursor(SCREEN_WIDTH / 2 - 18, SCREEN_HEIGHT / 2 - 8);
display.print("8");
// Display the content
display.display();
// Delay to see the 8ball
delay(100);
// Clear the display
display.clearDisplay();
// Draw the white circle representing the 8ball at different positions
display.fillCircle(SCREEN_WIDTH / 2 + 10, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / 2 - 2, SSD1306_WHITE);
// Draw a smaller black circle in the center
display.fillCircle(SCREEN_WIDTH / 2 + 10, SCREEN_HEIGHT / 2, (SCREEN_HEIGHT / 2) / 2, SSD1306_BLACK);
// Set the font size and color for the number 8
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
// Draw the number 8 in the center of the black circle
display.setCursor(SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT / 2 - 8);
display.print("8");
// Display the content
display.display();
// Delay to see the 8ball
delay(100);
}
}