In this blog, we'll explore how to build a chatbot using the Beetle ESP32, an affordable and compact microcontroller. This project will guide you through setting up Wi-Fi connectivity, creating a web server, and interfacing with Deepseek API to process user queries. Let's get started!
Components Required- Beetle ESP32
- Breadboard
- Jumper wires
- Wio Terminal (Optional)
The DFRobot Beetle ESP32 C6 is a mini-sized Arduino IoT development board based on the ESP32-C6 chip. Despite its compact size, it offers a range of features that make it ideal for IoT projects:
- High Performance: Equipped with a 160MHz RISC-V 32-bit processor.
- Multiple Communication Protocols: Supports Wi-Fi 6, Bluetooth 5, Zigbee 3.0, and Thread 1.32.
- Ultra-Low Power Consumption: Features deep sleep mode with a current consumption of just 14uA.
- Battery Management: Integrates lithium battery charging management, making it suitable for wearable applications.
- Compact Size: Measures only 25*20.5mm, making it as small as a coin.
The Wio Terminal is a complete open-source development platform based on the ATSAMD51 microcontroller.
It features a 2.4" LCD screen, onboard sensors, and wireless connectivity options, making it an excellent choice for various applications. Key features include:
- High Performance: Equipped with a 120MHz ARM Cortex-M4F processor
- Display: 2.4" LCD screen with 320x240 resolution
- Connectivity: Supports Wi-Fi, Bluetooth, and LoRa via add-on modules.
- Versatile I/O: Includes GPIO, digital, analog, I2C, UART, and SPI interfaces.
- Built-In Sensors: Features a gyroscope, accelerometer, microphone, and light sensor.
With the Wio Terminal, you can easily expand the functionality of your Beetle ESP32 chatbot project, providing real-time feedback and interactive displays.
Setting Up the HardwareBegin by connecting the LEDs to your Beetle ESP32. Here are the pin connections:
- Connect LED1 to pin 4 with a resistor.
This simple setup will allow us to visualize the chatbot's activity by blinking LEDs.
Next, connect the Wio Terminal to the Beetle ESP32 via UART:
- Connect the TX pin of the Wio Terminal to the RX pin of the Beetle ESP32 C6.
- Connect the RX pin of the Wio Terminal to the TX pin of the Beetle ESP32 C6.
- Make sure to connect the ground (GND) of both devices.
This simple setup will allow us to visualize the chatbot's activity by displaying the API response on the Wio Terminal.
Connecting to Wi-FiTo connect your Beetle ESP32 to a Wi-Fi network, use the following code snippet:
#include <WiFi.h>
const char* ssid = "Your_SSID";
const char* password = "Your_PASSWORD";
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("Connected to WiFi");
}
Replace "Your_SSID"
and "Your_PASSWORD"
with your actual Wi-Fi credentials.
Next, let's set up a web server on the Beetle ESP32:
// HTML content to be served
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DFRobot Chatbot</title>
<style>
body {
background-color: #121212;
color: #ffffff;
font-family: Arial, sans-serif;
text-align: center;
padding: 20px;
}
#inputField, #answerField, #debugPanel {
background-color: #333;
color: #ffffff;
border: 1px solid #555;
border-radius: 10px;
margin: 10px;
padding: 15px;
width: 80%;
max-width: 500px;
display: block;
margin-left: auto;
margin-right: auto;
font-size: 1.1em;
}
#submitButton, #toggleDebugButton {
background-color: #6200ee;
color: #ffffff;
border: none;
border-radius: 10px;
margin: 10px;
padding: 15px 30px;
font-size: 1.2em;
cursor: pointer;
transition: background-color 0.3s ease;
}
#submitButton:hover, #toggleDebugButton:hover {
background-color: #3700b3;
}
#dfrobotLogo {
width: 300px; /* Increased size */
margin-top: 20px;
}
h1, h2 {
color: #bb86fc;
}
</style>
</head>
<body>
<img id="dfrobotLogo" src="https://th.bing.com/th/id/R.54efac417a0601c7ec9a065db1cf3e09?rik=X3ZTCpI2uP71jw&riu=http%3a%2f%2fthepihut.com%2fcdn%2fshop%2fcollections%2fdfrobot.jpg%3fv%3d1625593139%26width%3d2048&ehk=h1A%2bwwWukHZIUGkulQSSazwDhn%2bLm7koxyyOfo701A0%3d&risl=&pid=ImgRaw&r=0" alt="DFRobot Logo">
<h1>DFRobot Beetle ESP32 C3 Chatbot</h1>
<input id="inputField" type="text" placeholder="Enter your text here">
<button id="submitButton" onclick="sendQuestion()">Submit</button>
<div id="answerField"></div>
<button id="toggleDebugButton" onclick="toggleDebugPanel()">Toggle Debug Panel</button>
<h2>Debug Panel</h2>
<div id="debugPanel" style="display:none;"></div>
<script>
async function sendQuestion() {
const userQuestion = document.getElementById("inputField").value;
const response = await fetch("/getQuestion", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ question: userQuestion })
});
const data = await response.json();
document.getElementById("answerField").innerText = data.choices[0].message.content; // Show the answer
document.getElementById("debugPanel").innerText = JSON.stringify(data, null, 2); // Show complete API response
}
function toggleDebugPanel() {
const debugPanel = document.getElementById("debugPanel");
if (debugPanel.style.display === "none") {
debugPanel.style.display = "block";
} else {
debugPanel.style.display = "none";
}
}
</script>
</body>
</html>
)rawliteral";
This code sets up a basic web server that serves as an HTML page where users can input their questions.
To process user questions. First, navigate to the Open router and create a new API key.
Add the following code to your project:
HTTPClient http;
http.begin("https://openrouter.ai/api/v1/chat/completions");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + apiKey);
StaticJsonDocument<512> jsonDoc;
jsonDoc["model"] = "deepseek/deepseek-r1-distill-llama-70b"; // deepseek/deepseek-r1-distill-llama-70b //openai/gpt-4o-mini-2024-07-18
JsonArray messages = jsonDoc.createNestedArray("messages");
JsonObject systemMessage = messages.createNestedObject();
systemMessage["role"] = "system";
systemMessage["content"] = "Answer the user's question concisely and informatively.";
JsonObject userMessage = messages.createNestedObject();
userMessage["role"] = "user";
userMessage["content"] = question;
String requestBody;
serializeJson(jsonDoc, requestBody);
Serial.println("Sending HTTP POST request...");
int httpResponseCode = http.POST(requestBody);
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String response = http.getString();
Serial.print("HTTP Response: ");
Serial.println(response);
StaticJsonDocument<1024> responseDoc;
DeserializationError error = deserializeJson(responseDoc, response);
if (!error) {
String assistantResponse = responseDoc["choices"][0]["message"]["content"].as<String>();
Serial.print("Assistant Response: ");
Serial.println(assistantResponse); // Print the assistant response
Serial1.println(assistantResponse);
return response; // Return entire API response
} else {
return "Failed to parse JSON response.";
}
}
Don't forget to replace "Your_API_Key"
with your actual API key from OpenAI.
Finally, let's add some visual feedback by blinking the LEDs when processing a question:
int led0 = 15;
int led1 = 4;
void setup() {
pinMode(led0, OUTPUT);
pinMode(led1, OUTPUT);
}
void loop() {
digitalWrite(led0, HIGH);
digitalWrite(led1, LOW);
delay(100);
digitalWrite(led0, LOW);
digitalWrite(led1, HIGH);
delay(100);
}
Beetle ESP32 C6 Code:Here is the complete sketch., please change the credentials to yours.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <WebServer.h>
int led0 = 15;
int led1 = 4;
// WiFi credentials
const char* ssid = "";
const char* password = "";
const char* apiKey = "";
// Create WebServer object on port 80
WebServer server(80);
// HTML content to be served
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DFRobot Chatbot</title>
<style>
body {
background-color: #121212;
color: #ffffff;
font-family: Arial, sans-serif;
text-align: center;
padding: 20px;
}
#inputField, #answerField, #debugPanel {
background-color: #333;
color: #ffffff;
border: 1px solid #555;
border-radius: 10px;
margin: 10px;
padding: 15px;
width: 80%;
max-width: 500px;
display: block;
margin-left: auto;
margin-right: auto;
font-size: 1.1em;
}
#submitButton, #toggleDebugButton {
background-color: #6200ee;
color: #ffffff;
border: none;
border-radius: 10px;
margin: 10px;
padding: 15px 30px;
font-size: 1.2em;
cursor: pointer;
transition: background-color 0.3s ease;
}
#submitButton:hover, #toggleDebugButton:hover {
background-color: #3700b3;
}
#dfrobotLogo {
width: 300px; /* Increased size */
margin-top: 20px;
}
h1, h2 {
color: #bb86fc;
}
</style>
</head>
<body>
<img id="dfrobotLogo" src="https://th.bing.com/th/id/R.54efac417a0601c7ec9a065db1cf3e09?rik=X3ZTCpI2uP71jw&riu=http%3a%2f%2fthepihut.com%2fcdn%2fshop%2fcollections%2fdfrobot.jpg%3fv%3d1625593139%26width%3d2048&ehk=h1A%2bwwWukHZIUGkulQSSazwDhn%2bLm7koxyyOfo701A0%3d&risl=&pid=ImgRaw&r=0" alt="DFRobot Logo">
<h1>DFRobot Beetle ESP32 C3 Chatbot</h1>
<input id="inputField" type="text" placeholder="Enter your text here">
<button id="submitButton" onclick="sendQuestion()">Submit</button>
<div id="answerField"></div>
<button id="toggleDebugButton" onclick="toggleDebugPanel()">Toggle Debug Panel</button>
<h2>Debug Panel</h2>
<div id="debugPanel" style="display:none;"></div>
<script>
async function sendQuestion() {
const userQuestion = document.getElementById("inputField").value;
const response = await fetch("/getQuestion", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ question: userQuestion })
});
const data = await response.json();
document.getElementById("answerField").innerText = data.choices[0].message.content; // Show the answer
document.getElementById("debugPanel").innerText = JSON.stringify(data, null, 2); // Show complete API response
}
function toggleDebugPanel() {
const debugPanel = document.getElementById("debugPanel");
if (debugPanel.style.display === "none") {
debugPanel.style.display = "block";
} else {
debugPanel.style.display = "none";
}
}
</script>
</body>
</html>
)rawliteral";
// Function to process the user question and get the response
String processQuestion(String question) {
Serial.print("User Question: ");
Serial.println(question); // Print the user question
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin("https://openrouter.ai/api/v1/chat/completions");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + apiKey);
StaticJsonDocument<512> jsonDoc;
jsonDoc["model"] = "deepseek/deepseek-r1-distill-llama-70b"; // deepseek/deepseek-r1-distill-llama-70b //openai/gpt-4o-mini-2024-07-18
JsonArray messages = jsonDoc.createNestedArray("messages");
JsonObject systemMessage = messages.createNestedObject();
systemMessage["role"] = "system";
systemMessage["content"] = "Answer the user's question concisely and informatively.";
JsonObject userMessage = messages.createNestedObject();
userMessage["role"] = "user";
userMessage["content"] = question;
String requestBody;
serializeJson(jsonDoc, requestBody);
Serial.println("Sending HTTP POST request...");
int httpResponseCode = http.POST(requestBody);
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String response = http.getString();
Serial.print("HTTP Response: ");
Serial.println(response);
StaticJsonDocument<1024> responseDoc;
DeserializationError error = deserializeJson(responseDoc, response);
if (!error) {
String assistantResponse = responseDoc["choices"][0]["message"]["content"].as<String>();
Serial.print("Assistant Response: ");
Serial.println(assistantResponse); // Print the assistant response
Serial1.println(assistantResponse);
return response; // Return entire API response
} else {
return "Failed to parse JSON response.";
}
}
return "WiFi not connected!";
}
void setup() {
// Start Serial Monitor
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, /*rx =*/17, /*tx =*/16);
pinMode(led0, OUTPUT);
pinMode(led1, OUTPUT);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("\nConnected to WiFi");
// Print ESP32 Local IP Address
Serial.print("ESP32 IP Address: ");
Serial.println(WiFi.localIP());
// Serve HTML content
server.on("/", HTTP_GET, []() {
server.send_P(200, "text/html", index_html);
});
// Handle POST request from the web page
server.on("/getQuestion", HTTP_POST, []() {
if (server.hasArg("plain")) {
String body = server.arg("plain");
// Debugging: Print the received body
Serial.print("Received body: ");
Serial.println(body);
// Parse JSON from the received payload
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, body);
if (error) {
Serial.println("Failed to parse JSON from body");
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
return;
}
String userQuestion = doc["question"];
Serial.print("User question: ");
Serial.println(userQuestion);
Serial1.println(userQuestion);
String responseText = processQuestion(userQuestion);
Serial.print("Response text: ");
Serial.println(responseText);
String jsonResponse = responseText; // Use entire API response as JSON response
// Send response to the client
server.send(200, "application/json", jsonResponse);
} else {
server.send(400, "application/json", "{\"error\":\"No body received\"}");
}
});
// Start server
server.begin();
Serial.println("Server started");
}
void loop() {
server.handleClient();
// Blink LED
digitalWrite(led0, HIGH);
digitalWrite(led1, LOW);
delay(100);
digitalWrite(led0, LOW);
digitalWrite(led1, HIGH);
delay(100);
}
Wio Terminal Code :Upload the following code to the Wio Terminal to get the data from the Beetle and print it out on the screen.
#include <SoftwareSerial.h>
#include <TFT_eSPI.h> // Include the graphics library (this includes the sprite functions)
// Create a SoftwareSerial object on pins 2 (RX) and 3 (TX)
SoftwareSerial mySerial(2, 3); // RX, TX
// Initialize the TFT screen
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
// Screen dimensions
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
// Initialize the TFT screen
tft.begin();
tft.setRotation(3); // Set the screen orientation (1 for landscape mode)
tft.fillScreen(TFT_BLACK); // Clear the screen with black color
tft.setTextColor(TFT_WHITE); // Set text color to white
tft.setTextSize(2); // Set text size
// Print initial message to the screen
tft.drawString("Waiting for data...", 10, 10);
}
void loop() {
while (mySerial.available()) {
String str = mySerial.readString(); // Read the incoming data as a string
str.trim();
Serial.println(str);
// Clear the screen and display the new data with text wrapping
tft.fillScreen(TFT_BLACK); // Clear the screen with black color
printWrappedText(str, 10, 10, SCREEN_WIDTH - 20, 2);
mySerial.println("initialization done.");
}
}
// Function to print wrapped text on the screen
void printWrappedText(String str, int x, int y, int lineWidth, int textSize) {
tft.setCursor(x, y);
tft.setTextSize(textSize);
int cursorX = x;
int cursorY = y;
int spaceWidth = tft.textWidth(" ");
int maxLineWidth = lineWidth;
String word;
for (int i = 0; i < str.length(); i++) {
if (str[i] == ' ' || str[i] == '\n') {
int wordWidth = tft.textWidth(word);
if (cursorX + wordWidth > maxLineWidth) {
cursorX = x;
cursorY += tft.fontHeight();
}
tft.drawString(word, cursorX, cursorY);
cursorX += wordWidth + spaceWidth;
if (str[i] == '\n') {
cursorX = x;
cursorY += tft.fontHeight();
}
word = "";
} else {
word += str[i];
}
}
if (word.length() > 0) {
int wordWidth = tft.textWidth(word);
if (cursorX + wordWidth > maxLineWidth) {
cursorX = x;
cursorY += tft.fontHeight();
}
tft.drawString(word, cursorX, cursory);
}
}
Serial Terminal Response:Then open the IP address in the web browser.
Next, enter the prompt.
You can see the response in the Wio terminal screen.
If you want to look at the whole API response, just press the Debug log button on the website. This will show the complete API response for debugging purposes.
With this project, you've created a basic chatbot using the Beetle ESP32 and Wio Terminal. This setup can be further extended by adding more functionalities, such as more interactive web pages, advanced error handling, or integrating other APIs. The DFRobot Beetle ESP32 C6's compact size and versatile features, combined with the powerful Wio Terminal, make it an excellent choice for various IoT applications. Happy coding
Comments
Please log in or sign up to comment.