Index
Now that our hardware (Microphone, Speaker, SD Card) is verified, we need to give our ESP32 the “keys” to access the AI brains in the cloud. Without valid credentials, the sophisticated code we write later will just be rejected by the servers.
This lesson focuses on acquiring your API Keys and running a connectivity test using the robust code you provided.
Getting Your Credentials
We need three specific strings of text to make this work. Treat these like passwords—never share them publicly.
A. OpenAI API Key (The Brain)
- Go to platform.openai.com and log in.
- Click on Dashboard -> API Keys in the left menu.
- Click + Create new secret key.
- Name it (e.g., “ESP32 Assistant”) and copy the string starting with
sk-....- Note: You must have a small credit balance (e.g., $5) in your OpenAI account for this to work. It is not free.
B. ElevenLabs API Key (The Voice)
- Go to elevenlabs.io and log in.
- Click on your Profile Icon (top right) -> Profile + API Key.
- Click the “Eye” icon to reveal your API Key and copy it.
C. ElevenLabs Voice ID
- Go to the VoiceLab tab.
- Choose a voice (e.g., “Adam” or a cloned voice).
- Click the ID label or the copy icon next to the voice name to copy the Voice ID (a short string of random characters).
The Connectivity Test Code
This code acts as a “sanity check.” It connects to Wi-Fi, sends a simple “Hello” to ChatGPT, and requests a sample audio file from ElevenLabs. If this passes, your credentials are valid.
Prerequisite: You must install the ArduinoJson library via the Library Manager in Arduino IDE.
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h> // Proper JSON library
// ============ CONFIG - UPDATE THESE! ============
const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASSWORD = "YOUR_PASSWORD";
// API Keys
const char* OPENAI_API_KEY = "sk-proj-siqa2E7Qx";
const char* ELEVENLABS_API_KEY = "sk_1f3a72616";
const char* VOICE_ID = "21m00Tcm4TlvDq8ikWAM";
// ============ SIMPLE TEST ============
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("\n\n╔══════════════════════════════╗");
Serial.println( "║ PROPER API TEST - START ║");
Serial.println( "╚══════════════════════════════╝\n");
Serial.println("Using ArduinoJson v6 for proper parsing");
// 1. Connect to WiFi
Serial.println("📶 STEP 1: Connecting to WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int wifiAttempts = 0;
while (WiFi.status() != WL_CONNECTED && wifiAttempts < 20) {
delay(500);
Serial.print(".");
wifiAttempts++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\n❌ WiFi Failed! Check credentials");
while(1);
}
Serial.println("\n✅ WiFi Connected!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
Serial.printf("Free Heap: %d bytes\n", ESP.getFreeHeap());
// 2. Test OpenAI with proper JSON parsing
Serial.println("\n🤖 STEP 2: Testing OpenAI (with ArduinoJson)...");
testOpenAI();
// 3. Test ElevenLabs
Serial.println("\n🎵 STEP 3: Testing ElevenLabs...");
testElevenLabs();
Serial.println("\n\n╔══════════════════════════════╗");
Serial.println( "║ ALL TESTS COMPLETE! ║");
Serial.println( "╚══════════════════════════════╝\n");
}
void loop() {
// Nothing to do here
delay(1000);
}
// ============ TEST FUNCTIONS WITH ARDUINOJSON ============
void testOpenAI() {
Serial.println(" Asking ChatGPT: 'How are you?'");
HTTPClient http;
http.begin("https://api.openai.com/v1/chat/completions");
http.addHeader("Authorization", "Bearer " + String(OPENAI_API_KEY));
http.addHeader("Content-Type", "application/json");
// Create JSON payload with ArduinoJson
StaticJsonDocument<512> requestDoc;
requestDoc["model"] = "gpt-3.5-turbo";
JsonArray messages = requestDoc.createNestedArray("messages");
JsonObject message = messages.createNestedObject();
message["role"] = "user";
message["content"] = "How are you?";
requestDoc["max_tokens"] = 30;
requestDoc["temperature"] = 0.7;
String requestPayload;
serializeJson(requestDoc, requestPayload);
Serial.println(" Sending request...");
int httpCode = http.POST(requestPayload);
if (httpCode == 200) {
String response = http.getString();
Serial.println(" ✅ Got response from OpenAI");
// Parse response with ArduinoJson
StaticJsonDocument<2048> responseDoc;
DeserializationError error = deserializeJson(responseDoc, response);
if (error) {
Serial.print(" ❌ JSON parse failed: ");
Serial.println(error.c_str());
Serial.println(" Raw response (first 500 chars):");
Serial.println(response.substring(0, 500));
} else {
// Extract the reply properly
if (responseDoc.containsKey("choices") &&
responseDoc["choices"].is<JsonArray>() &&
responseDoc["choices"].size() > 0) {
JsonObject choice = responseDoc["choices"][0];
if (choice.containsKey("message") &&
choice["message"].is<JsonObject>()) {
JsonObject messageObj = choice["message"];
if (messageObj.containsKey("content")) {
String reply = messageObj["content"].as<String>();
reply.trim();
Serial.print(" ✅ ChatGPT says: \"");
Serial.print(reply);
Serial.println("\"");
Serial.println(" ✅ OpenAI API is working perfectly!");
} else {
Serial.println(" ❌ No 'content' field in response");
}
} else {
Serial.println(" ❌ No 'message' field in response");
}
} else {
Serial.println(" ❌ No 'choices' array in response");
}
}
} else if (httpCode == 401) {
Serial.println(" ❌ OpenAI API Key Invalid! (401 Unauthorized)");
} else if (httpCode == 429) {
Serial.println(" ⚠️ Rate Limited - try again later (429)");
} else {
Serial.print(" ❌ Error: HTTP ");
Serial.println(httpCode);
Serial.print(" Error: ");
Serial.println(http.errorToString(httpCode));
}
http.end();
}
void testElevenLabs() {
Serial.println(" Testing TTS with text: 'Hello from ElevenLabs'");
HTTPClient http;
String url = "https://api.elevenlabs.io/v1/text-to-speech/" + String(VOICE_ID);
http.begin(url);
http.addHeader("xi-api-key", ELEVENLABS_API_KEY);
http.addHeader("Content-Type", "application/json");
http.addHeader("Accept", "audio/mpeg");
// Create payload with ArduinoJson
StaticJsonDocument<512> requestDoc;
requestDoc["text"] = "Hello from ElevenLabs. This is a test.";
requestDoc["model_id"] = "eleven_multilingual_v2";
JsonObject voiceSettings = requestDoc.createNestedObject("voice_settings");
voiceSettings["stability"] = 0.5;
voiceSettings["similarity_boost"] = 0.5;
String requestPayload;
serializeJson(requestDoc, requestPayload);
Serial.println(" Sending TTS request...");
int httpCode = http.POST(requestPayload);
if (httpCode == 200) {
int size = http.getSize();
if (size > 0) {
Serial.print(" ✅ ElevenLabs TTS Working! ");
Serial.print(size);
Serial.println(" bytes received");
}
} else if (httpCode == 401) {
Serial.println(" ❌ ElevenLabs API Key Invalid! (401)");
// Try to get error message
String response = http.getString();
if (response.length() > 0) {
StaticJsonDocument<512> errorDoc;
DeserializationError error = deserializeJson(errorDoc, response);
if (!error && errorDoc.containsKey("detail")) {
String detail = errorDoc["detail"]["message"].as<String>();
Serial.print(" Error detail: ");
Serial.println(detail);
}
}
} else if (httpCode == 404) {
Serial.println(" ❌ Voice ID not found! (404)");
Serial.print(" Voice ID: ");
Serial.println(VOICE_ID);
} else {
Serial.print(" ❌ Error: HTTP ");
Serial.println(httpCode);
// Try to parse error response
String response = http.getString();
if (response.length() > 0) {
Serial.println(" Response: " + response);
// Try to parse as JSON for better error message
StaticJsonDocument<512> errorDoc;
DeserializationError error = deserializeJson(errorDoc, response);
if (!error) {
if (errorDoc.containsKey("detail")) {
if (errorDoc["detail"].is<JsonObject>()) {
JsonObject detail = errorDoc["detail"];
if (detail.containsKey("message")) {
Serial.print(" Error message: ");
Serial.println(detail["message"].as<String>());
}
} else if (errorDoc["detail"].is<String>()) {
Serial.print(" Error detail: ");
Serial.println(errorDoc["detail"].as<String>());
}
}
}
}
}
http.end();
}
Upload the code and watch the Serial Monitor (115200 baud).

