Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Makestreme
Published © GPL3+

TARS GPT - a Hilarious AI Weather Reporter

TARS is a witty, AI-powered weather reporter with a humorous twist, updating hourly with unique, sarcastic weather forecasts for your desk!

BeginnerFull instructions provided4 hours1,662
TARS GPT - a Hilarious AI Weather Reporter

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1

Software apps and online services

Arduino IDE
Arduino IDE
openweathermap
Google AI Studio

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Circuit diagram

TARS print

Code

Python test code

Python
import requests

# OpenWeatherMap API key
owm_api_key = 'API_key'
owm_url = f'http://api.openweathermap.org/data/2.5/weather?q=Bangalore,IN&appid={owm_api_key}&units=metric'
# Gemini API key
gemini_api_key = 'API_key'
gemini_url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=' + gemini_api_key


# Get weather data from OpenWeatherMap
owm_response = requests.get(owm_url)
if owm_response.status_code == 200:
    owm_data = owm_response.json()
    temperature = owm_data['main']['temp']
    humidity = owm_data['main']['humidity']
    condition = owm_data['weather'][0]['description']
    weather_info = f'Temperature: {temperature}C, Humidity: {humidity}%, Condition: {condition}'
else:
    print("Failed to get weather data from OpenWeatherMap:", owm_response.status_code, owm_response.text)
    weather_info = "Unable to retrieve weather data."


headers = {
    'Content-Type': 'application/json'
}

data = {
    "contents": [
        {
            "parts": [
                {"text": f"You're a humorous AI robot. Describe this weather in a hilarous way. Your answer should be between 50 to 60 characters. Respond with only the humorous sentence and nothing else.: {weather_info}"}
            ]
        }
    ]
}

# Get summary from Gemini API
gemini_response = requests.post(gemini_url, headers=headers, json=data)
if gemini_response.status_code == 200:
    gemini_summary = gemini_response.json()['candidates'][0]['content']['parts'][0]['text']
    print(gemini_summary)
else:
    print("Failed to get response from Gemini API:", gemini_response.status_code, gemini_response.text)

OLED test code

Arduino
#include <U8g2lib.h>
#include <Wire.h>

// Initialize the display with I2C communication
// U8G2_SSD1306_128X64_NONAME_F_HW_I2C means hardware I2C with 128x64 OLED display
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, D2, D1);  // D2 for SDA, D1 for SCL

void setup() {
  // Initialize the display
  u8g2.begin();
}

void loop() {
  // Clear the display buffer
  u8g2.clearBuffer();
  
  // Set drawing color (1 means solid)
  u8g2.setDrawColor(1);
  
  // Draw a solid rectangle at position (10, 10) with width 50 and height 30
  u8g2.drawBox(10, 10, 50, 30);
  
  // Send the buffer to the display
  u8g2.sendBuffer();
  
  // Delay to allow the user to see the rectangle
  delay(2000); // Wait for 2 seconds
}

Main code

Arduino
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h> 
#include <ArduinoJson.h>
#include <Wire.h>
#include <U8g2lib.h>

// OLED display dimensions
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// Create an instance of the U8g2 library for SSD1306 (adjust the constructor based on your display)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);  // Hardware I2C constructor

const char* ssid = "WIFI_SSID";
const char* password = "PASSWORD";
const char* gemini_api_key = "GEMINI_API_KEY";
const char* weather_api_key = "OWM_API_KEY";
const char* gemini_server = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=";
const char* weather_server = "http://api.openweathermap.org/data/2.5/weather";

unsigned long previousMillis = 0;
const long interval = 3600000; // 1 hour in milliseconds

void display_text(String s1, String s2, String s3)
{
// Clear the screen buffer
  u8g2.clearBuffer();

  // Set the font to small monospace
  u8g2.setFont(u8g2_font_helvB08_tr);  // Small monospace font

  // First line: ~5 words (at the top)
  u8g2.setCursor(0, 9); 
  u8g2.print(String(s1));

  // Fourth line: ~5 words
  u8g2.setCursor(0, 40); 
  u8g2.print(String(s2));

  // Fifth line: ~5 words
  u8g2.setCursor(0, 60); 
  u8g2.print(String(s3));

  // Send the buffer to the display
  u8g2.sendBuffer();
}

String getWeatherData() {
  HTTPClient http;
  WiFiClient client;
  
  String url = String(weather_server) + 
               "?q=Bangalore,IN" + 
               "&appid=" + weather_api_key + 
               "&units=metric";  // Use metric units
               
  http.begin(client, url);
  int httpResponseCode = http.GET();
  
  if (httpResponseCode > 0) {
    String payload = http.getString();
    
    // Parse weather JSON
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, payload);
    
    // Extract relevant weather information
    float temp = doc["main"]["feels_like"];
    int humidity = doc["main"]["humidity"];
    String description = doc["weather"][0]["description"];
    
    String weatherSummary = "Temperature: " + 
                           String(temp) + "C, Humidity: " + 
                           String(humidity) + "%, Condition: " + 
                           description;
    
    http.end();
    return weatherSummary;
  }
  
  http.end();
  return "Error getting weather data";
}

void fetchAndProcessData() {
  if (WiFi.status() == WL_CONNECTED) {
    // First get weather data
    String weatherData = getWeatherData();
    Serial.println("Weather Data: " + weatherData);
    
    // Now send to Gemini for summarization
   // Create secure client
    WiFiClientSecure client;
    client.setInsecure(); // Skip certificate verification
    
    // Create HTTP client
    HTTPClient https;

    String url = String(gemini_server) + gemini_api_key;

    // Start HTTPS connection
    if (https.begin(client, url)) {
      // Prepare JSON data
      StaticJsonDocument<200> doc;
      JsonArray contents = doc.createNestedArray("contents");
      JsonObject content = contents.createNestedObject();
      JsonArray parts = content.createNestedArray("parts");
      JsonObject part = parts.createNestedObject();
      part["text"] = "Imagine you're a humorous AI. Describe the feeling of the following weather. Your answer should be around 50 to 60 characters.\
       And only reply with the humorous sentence and nothing else. Weather info: " + String(weatherData);

      // Serialize JSON
      String jsonString;
      serializeJson(doc, jsonString);

      // Set headers
      https.addHeader("Content-Type", "application/json");

      // Make POST request
      int httpCode = https.POST(jsonString);

      // Check response
      if (httpCode > 0) {
        String payload = https.getString();
         // Parse Gemini response
        DynamicJsonDocument doc(1024);
        deserializeJson(doc, payload);
        JsonObject obj = doc["candidates"][0]["content"]["parts"][0];
        String text = obj["text"];
        
        Serial.println("Gemini Summary:");
        Serial.println(text);

        // split the text into 3 parts
        int len = text.length();
        int part1_end = len / 3;
        int part2_end = 2 * len / 3;

        String part1 = text.substring(0, part1_end);        // First third
        String part2 = text.substring(part1_end, part2_end); // Second third
        String part3 = text.substring(part2_end);            // Third part

        display_text(part1, part2, part3);

      } else {
        Serial.println("Error on HTTP request");
      }

      https.end();
    } else {
      Serial.println("HTTPS connection failed");
     }
  }
}

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  Serial.println("Connected to WiFi");
  
  // Run immediately after setup
  delay(30000); //start with a 30 second delay
  fetchAndProcessData();
  previousMillis = millis();  // Update the timer
}

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    fetchAndProcessData();
  }
  delay(1000);
}

Credits

Makestreme
9 projects • 10 followers
I make DIY projects that are fun and interesting! An engineer by profession :) youtube.com/@makestreme
Contact

Comments

Please log in or sign up to comment.