IoT HUB
Published © GPL3+

Gemini on display using Xiao ESP32 C3 by Seeed Studio

Gemini on OLED! Ask questions using serial monitor from Gemini via Tiny Xiao ESP32-C3 & get smart replies on mini 0.96 display.

IntermediateFull instructions provided1 hour373
Gemini on display using Xiao ESP32 C3 by Seeed Studio

Things used in this project

Hardware components

XIAO ESP32C3
Seeed Studio XIAO ESP32C3
×1
Oled Display
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×2
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Pin Connection

Connect all the components as shown in this image

Code

Gemini on display- Code

C/C++
Just paste this code on your IDE and upload it to your Xiao ESP32 C3. Follow the connections and your Gemini will be ready
/*Basic code for using Gemini AI on mini display with the help of tiny powerful devlopment board- Xiao ESP32 C3 by Seeed Studio.
You can also make this by uploading this code to you Xiao ESP32 C3 board and by following the other steps on mentioned hackster.io*/

//Devloped exclusively by IoT HUB

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>
#include <vector>

#define SCROLL_DOWN_PIN 9 
#define SCROLL_UP_PIN   8

const char *ssid = "YOUR WIFI SSID";
const char *password = "YOUR SSID PASSWORD";

const String apiKey = "Your Gemini API";
const String geminiUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=" + apiKey;

// OLED (SDA=6, SCL=7 for Xiao ESP32-C3)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/7, /* data=*/6);

std::vector<String> wrappedAnswerLines;
std::vector<String> wrappedQuestionLines;
String displayedQuestion = "";
int currentPage = 0;
bool lastUpState = HIGH, lastDownState = HIGH;

// Clean intro animation with top and bottom line bouncing
void showStartupAnimation() {
  unsigned long startTime = millis();
  u8g2.setFont(u8g2_font_6x10_tr);

  while (millis() - startTime < 2000) {
    int offset = 10 * sin((millis() - startTime) / 300.0);
    u8g2.clearBuffer();
    u8g2.drawStr(25 + offset, 18, "Developed By -");
    u8g2.drawStr(35 - offset, 48, "IoT HUB");
    u8g2.sendBuffer();
    delay(50);
  }
}

// Loading animation: blinking dots
void showLoadingAnimationWhileWaiting() {
  int dotState = 0;
  unsigned long lastUpdate = 0;

  while (WiFi.status() == WL_CONNECTED && !Serial.available()) {
    if (millis() - lastUpdate > 300) {
      lastUpdate = millis();
      dotState = (dotState + 1) % 4;
      u8g2.clearBuffer();
      u8g2.setFont(u8g2_font_6x10_tr);
      String loadingText = "Loading";
      for (int i = 0; i < dotState; i++) loadingText += ".";
      u8g2.drawStr(40, 32, loadingText.c_str());
      u8g2.sendBuffer();
    }
    delay(10);
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(SCROLL_DOWN_PIN, INPUT_PULLUP);
  pinMode(SCROLL_UP_PIN, INPUT_PULLUP);

  u8g2.begin();
  u8g2.setFont(u8g2_font_6x10_tr);

  showStartupAnimation();

  u8g2.clearBuffer();
  u8g2.drawStr(0, 15, "Connecting to WiFi...");
  u8g2.sendBuffer();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nConnected to WiFi");
  u8g2.clearBuffer();
  u8g2.drawStr(0, 15, "Connected to WiFi!");
  u8g2.drawStr(0, 30, "ASK A QUESTION:");
  u8g2.sendBuffer();

  Serial.println("Ask a question:");
}

void loop() {
  bool downState = digitalRead(SCROLL_DOWN_PIN);
  bool upState = digitalRead(SCROLL_UP_PIN);

  if (downState == LOW && lastDownState == HIGH) {
    scrollAnswerDown(); delay(200);
  }
  if (upState == LOW && lastUpState == HIGH) {
    scrollAnswerUp(); delay(200);
  }
  lastDownState = downState;
  lastUpState = upState;

  if (Serial.available()) {
    String userInput = Serial.readStringUntil('\n');
    userInput.trim();

    if (userInput.length() > 0) {
      displayedQuestion = userInput;
      displayedQuestion.toUpperCase();  // Force ALL CAPS

      showLoadingAnimationWhileWaiting();

      String answer = askGemini(userInput);

      if (answer.length() > 0) {
        Serial.println("Answer: " + answer);
        prepareAnswerLines(answer);
        prepareQuestionLines(displayedQuestion);
      } else {
        Serial.println("Failed to get an answer.");
        prepareAnswerLines("Error: No answer.");
        prepareQuestionLines(displayedQuestion);
      }

      currentPage = 0;
      displayCurrentPage();
      Serial.println("\nAsk another question:");
    }
  }
}

void scrollAnswerDown() {
  if (wrappedAnswerLines.empty()) return;
  int totalPages = (wrappedAnswerLines.size() + 2) / 3;
  currentPage = (currentPage + 1) % totalPages;
  displayCurrentPage();
}

void scrollAnswerUp() {
  if (wrappedAnswerLines.empty()) return;
  int totalPages = (wrappedAnswerLines.size() + 2) / 3;
  currentPage = (currentPage - 1 + totalPages) % totalPages;
  displayCurrentPage();
}

void wrapTextToLines(String text, std::vector<String>& linesOut, int maxLen = 22) {
  linesOut.clear();
  String word, line;

  for (int i = 0; i < text.length(); i++) {
    char c = text[i];
    if (c == ' ' || c == '\n') {
      if (line.length() + word.length() > maxLen) {
        linesOut.push_back(line);
        line = word + " ";
      } else {
        line += word + " ";
      }
      word = "";
      if (c == '\n' && line.length() > 0) {
        linesOut.push_back(line);
        line = "";
      }
    } else {
      word += c;
    }
  }
  if (word.length()) {
    if (line.length() + word.length() > maxLen) {
      linesOut.push_back(line);
      line = word;
    } else {
      line += word;
    }
  }
  if (line.length()) linesOut.push_back(line);
}

void prepareAnswerLines(String text) {
  wrapTextToLines(text, wrappedAnswerLines, 22);
}

void prepareQuestionLines(String text) {
  wrapTextToLines(text, wrappedQuestionLines, 22);
}

void displayCurrentPage() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x10_tr);

  // Show question (up to 2 lines)
  if (wrappedQuestionLines.size() > 0)
    u8g2.drawStr(0, 10, wrappedQuestionLines[0].c_str());
  if (wrappedQuestionLines.size() > 1)
    u8g2.drawStr(0, 20, wrappedQuestionLines[1].c_str());

  // Show answer (3 lines)
  int startLine = currentPage * 3;
  for (int i = 0; i < 3; i++) {
    int idx = startLine + i;
    if (idx < wrappedAnswerLines.size()) {
      u8g2.drawStr(0, 35 + i * 10, wrappedAnswerLines[idx].c_str());
    }
  }

  u8g2.sendBuffer();
}

String askGemini(String question) {
  HTTPClient http;
  http.begin(geminiUrl);
  http.addHeader("Content-Type", "application/json");

  String payload = "{\"contents\":[{\"parts\":[{\"text\":\"" + question + "\"}]}]}";
  int httpCode = http.POST(payload);
  String response = http.getString();
  String result = "";

  if (httpCode == 200) {
    DynamicJsonDocument doc(4096);
    if (!deserializeJson(doc, response)) {
      JsonArray candidates = doc["candidates"];
      if (candidates.size() > 0) {
        result = candidates[0]["content"]["parts"][0]["text"].as<String>();
      } else {
        result = "No response.";
      }
    } else {
      result = "JSON parse error.";
    }
  } else {
    Serial.print("Request error: ");
    Serial.println(httpCode);
  }

  http.end();
  return result;
}


//An original *IoT HUB* creation

Credits

IoT HUB
4 projects • 3 followers
I’m a student who loves robotics, hacking, creating things with dev boards like ESP32/Arduino, and discovering tricks to simplify tech.

Comments