|
| 1 | +/* |
| 2 | + Inkplate10 OpenAI Image slideshow |
| 3 | + Compatible with Soldered Inkplate 10 |
| 4 | +
|
| 5 | + For this example you will need only USB cable and Inkplate 10. |
| 6 | + Select "Soldered Inkplate10" from Tools -> Board menu. |
| 7 | + Don't have "Soldered Inkplate10" option? Follow our tutorial and add it:https://soldered.com/documentation/inkplate/10/quick-start-guide/ |
| 8 | +
|
| 9 | + Overview: |
| 10 | + This example demonstrates how to send a prompt to the GPT DALL-E API in order to generate an image which will be displayed on the Inkplate |
| 11 | + It generates an image every 30mins, after which it goes to deep sleep. |
| 12 | +
|
| 13 | + Before You Start: |
| 14 | + - Enter your WiFi credentials carefully (they are case-sensitive). |
| 15 | + - After creating an OpenAI API key, enter it in the openai_key variable |
| 16 | +*/ |
| 17 | + |
| 18 | +#include <WiFiClientSecure.h> // Enables secure (HTTPS) communication over WiFi |
| 19 | +#include <ArduinoJson.h> // Library for JSON parsing and creation |
| 20 | +#include "Inkplate.h" // Driver for the Inkplate e-paper display |
| 21 | + |
| 22 | +// Time in minutes the device will sleep between updates (30 mins in this case) |
| 23 | +#define SLEEP_DURATION_IN_MINS 30*60 |
| 24 | + |
| 25 | +// Format of the image to be downloaded and displayed (PNG is recommended for best compatibility) |
| 26 | +Image::Format imageFormat = Image::PNG; |
| 27 | + |
| 28 | +// WiFi credentials (replace with your own SSID and password) |
| 29 | +const char* ssid = "YOUR_SSID_HERE"; |
| 30 | +const char* password = "YOUR_PASSWORD_HERE"; |
| 31 | + |
| 32 | +// OpenAI API key (replace with your OpenAI API key) |
| 33 | +const char* openai_key = "YOUR_API_KEY_HERE"; |
| 34 | + |
| 35 | + |
| 36 | +// Create a secure WiFi client to communicate with OpenAI over HTTPS |
| 37 | +WiFiClientSecure client; |
| 38 | + |
| 39 | +// Create an Inkplate display object (1-bit mode = faster, lower-power updates) |
| 40 | +Inkplate display(INKPLATE_1BIT); |
| 41 | + |
| 42 | +// Set the image prompt to send to OpenAI |
| 43 | +String imagePrompt = "Generate a cyberpunk city with a lot of vertical layers"; |
| 44 | + |
| 45 | +void setup() { |
| 46 | + display.begin(); // Initialize the Inkplate hardware and begin communication |
| 47 | + |
| 48 | + Serial.begin(115200); // Start serial communication for debugging |
| 49 | + |
| 50 | + display.setTextSize(3); // Set text size for on-screen messages |
| 51 | + |
| 52 | + // Display message while attempting to connect to WiFi |
| 53 | + display.print("Connecting to WiFi..."); |
| 54 | + WiFi.begin(ssid, password); |
| 55 | + while (WiFi.status() != WL_CONNECTED) { |
| 56 | + delay(500); // Wait and retry |
| 57 | + display.print("."); // Show progress |
| 58 | + display.partialUpdate(); // Update only the changed part of the screen for speed |
| 59 | + } |
| 60 | + |
| 61 | + // WiFi connection successful |
| 62 | + display.println("\nConnected!"); |
| 63 | + display.display(); // Push display buffer to screen |
| 64 | + display.clearDisplay(); // Clear the screen for next display content |
| 65 | + |
| 66 | + // Notify the user on the display |
| 67 | + display.println("Generating prompt..."); |
| 68 | + display.partialUpdate(); // Show message quickly |
| 69 | + |
| 70 | + // Call function to get image URL from OpenAI based on the prompt |
| 71 | + String imageUrl = getImageUrlFromPrompt(imagePrompt); |
| 72 | + |
| 73 | + display.println("Prompt generated!"); |
| 74 | + display.partialUpdate(); // Update message after URL is fetched |
| 75 | + |
| 76 | + // Switch display to 3-bit grayscale mode for higher image quality |
| 77 | + // WARNING: Partial updates are not supported in this mode! |
| 78 | + display.setDisplayMode(INKPLATE_3BIT); |
| 79 | + display.setTextColor(BLACK); |
| 80 | + display.println("Downloading and displaying image (May take a while...)"); |
| 81 | + display.display(); // Show status |
| 82 | + display.clearDisplay(); // Clear screen before showing the image |
| 83 | + |
| 84 | + if (imageUrl != "") { |
| 85 | + Serial.println("Image URL: " + imageUrl); |
| 86 | + |
| 87 | + // Draw the image centered on the screen |
| 88 | + // Image assumed to be 1024x1024; offset to center it |
| 89 | + bool result = display.drawImage(imageUrl, imageFormat, display.width()/2 - 512, display.height()/2 - 512, true, false); |
| 90 | + |
| 91 | + if(result == 0) { |
| 92 | + // Show an error if image decoding fails |
| 93 | + display.println("Image decode error."); |
| 94 | + } |
| 95 | + |
| 96 | + display.display(); // Push image to display |
| 97 | + } |
| 98 | + else { |
| 99 | + // Show an error message if the image URL wasn't retrieved |
| 100 | + display.println("Failed to get image URL."); |
| 101 | + display.display(); |
| 102 | + } |
| 103 | + |
| 104 | + // Schedule the next wakeup time using the real-time clock |
| 105 | + display.rtcSetAlarmEpoch(display.rtcGetEpoch() + SLEEP_DURATION_IN_MINS, RTC_ALARM_MATCH_DHHMMSS); |
| 106 | + |
| 107 | + // Enable external wakeup on GPIO 39 (typically tied to RTC alarm) |
| 108 | + esp_sleep_enable_ext0_wakeup(GPIO_NUM_39, 0); |
| 109 | + |
| 110 | + // Enter deep sleep mode to conserve power |
| 111 | + esp_deep_sleep_start(); |
| 112 | +} |
| 113 | + |
| 114 | +void loop() { |
| 115 | + // This loop will never run because device goes into deep sleep after setup() |
| 116 | +} |
| 117 | + |
| 118 | +// Sends the image prompt to OpenAI's DALL·E endpoint and extracts the image URL |
| 119 | +String getImageUrlFromPrompt(String prompt) |
| 120 | +{ |
| 121 | + client.setInsecure(); // Disable certificate validation (OK for dev/test, not secure for prod) |
| 122 | + |
| 123 | + // Attempt to connect to OpenAI's server |
| 124 | + if (!client.connect("api.openai.com", 443)) { |
| 125 | + Serial.println("Connection to OpenAI for image failed!"); |
| 126 | + return ""; |
| 127 | + } |
| 128 | + |
| 129 | + // Create JSON request body |
| 130 | + DynamicJsonDocument doc(1024); |
| 131 | + doc["model"] = "dall-e-3"; //GPT model we are using, delete this line to use DALL-E 2 |
| 132 | + doc["prompt"] = prompt; |
| 133 | + doc["style"] = "vivid"; |
| 134 | + doc["n"] = 1; |
| 135 | + doc["size"] = "1024x1024"; // or 1792x1024, 1024x1792 |
| 136 | + |
| 137 | + String requestBody; |
| 138 | + serializeJson(doc, requestBody); // Convert JSON object to string |
| 139 | + |
| 140 | + // Compose and send HTTPS POST request |
| 141 | + client.println("POST /v1/images/generations HTTP/1.1"); |
| 142 | + client.println("Host: api.openai.com"); |
| 143 | + client.println("Authorization: Bearer " + String(openai_key)); // Add API key to header |
| 144 | + client.println("Content-Type: application/json"); |
| 145 | + client.print("Content-Length: "); |
| 146 | + client.println(requestBody.length()); |
| 147 | + client.println("Connection: close"); |
| 148 | + client.println(); |
| 149 | + client.println(requestBody); |
| 150 | + |
| 151 | + // Wait up to 40 seconds for a response |
| 152 | + unsigned long timeout = millis(); |
| 153 | + while (!client.available() && millis() - timeout < 40000) { |
| 154 | + delay(100); |
| 155 | + } |
| 156 | + |
| 157 | + // Timeout handling |
| 158 | + if (!client.available()) { |
| 159 | + Serial.println("OpenAI image request timed out."); |
| 160 | + return ""; |
| 161 | + } |
| 162 | + |
| 163 | + // Skip over HTTP response headers |
| 164 | + while (client.connected()) { |
| 165 | + String line = client.readStringUntil('\n'); |
| 166 | + if (line == "\r") break; // End of headers |
| 167 | + } |
| 168 | + |
| 169 | + // Read the HTTP response body |
| 170 | + String response = client.readString(); |
| 171 | + Serial.println("Response:"); |
| 172 | + Serial.println(response); |
| 173 | + |
| 174 | + // Parse JSON response to extract image URL |
| 175 | + DynamicJsonDocument resDoc(4096); |
| 176 | + DeserializationError err = deserializeJson(resDoc, response); |
| 177 | + if (err) { |
| 178 | + Serial.print("Image JSON parse failed: "); |
| 179 | + Serial.println(err.c_str()); |
| 180 | + return ""; |
| 181 | + } |
| 182 | + |
| 183 | + // Extract and return the first image URL from the response |
| 184 | + if (resDoc["data"] && resDoc["data"][0]["url"]) { |
| 185 | + String url = resDoc["data"][0]["url"].as<String>(); |
| 186 | + return url; |
| 187 | + } |
| 188 | + |
| 189 | + Serial.println("No image URL found in OpenAI response"); |
| 190 | + return ""; // Return empty string if no image URL was found |
| 191 | +} |
0 commit comments