commit 0969724f16cbe4b81c1be5d4c9f0b30d02857b03 Author: drjones Date: Wed May 20 10:04:29 2026 -0700 chore: import local project into Gitea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81d430d --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# OS / tooling +.DS_Store +Thumbs.db + +# Editors +.cursor/ + +# Secrets +.env +.env.* +!.env.example +!.env.template + +# Python +__pycache__/ +*.py[cod] +.venv/ +venv/ + +# Node / frontend +node_modules/ +dist/ + +# Typical embedded / tooling noise +*.log + +# Builds (adjust per subtree if needed) +**/build/.ninja_deps +**/build/.ninja_log + diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..cea4d3f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..10ed7f4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "f:/dev_shit/p4 pannel", + "program": "f:/dev_shit/p4 pannel/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bb879da --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..99b726f --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# P4 voice panel + +Bundles **`voice_assistant/`** firmware plus `.vscode`/`.cursor` configs and **`example.code`**. + +Treat this as companion UI work to **`p4 chatbot 83125/`** unless you refactor modules. + +Recommended flow: + +```bash +cd voice_assistant +``` + +Follow manifests inside (`CMakeLists.txt`, `sdkconfig.defaults`, Arduino project files, etc.—whichever framework that folder uses). Keep generated `build/` / `.pio/` directories excluded from commits. diff --git a/example.code b/example.code new file mode 100644 index 0000000..f4bc29f --- /dev/null +++ b/example.code @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + +// OLED config +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +#define I2C_SDA 21 +#define I2C_SCL 22 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +// WiFi +const char* ssid = "your wifi id"; +const char* password = "your wifi password"; +const String gemini_api_key = "your api key"; + +// MAX9814 microphone input pin +#define MIC_PIN 34 + +void setup() { + Serial.begin(115200); + delay(1000); + + Wire.begin(I2C_SDA, I2C_SCL); + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial.println("OLED init failed!"); + while (1); + } + + displayMessage("Connecting WiFi..."); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + displayMessage("WiFi connected!"); + pinMode(MIC_PIN, INPUT); + delay(500); + displayMessage("Ready! Speak loud..."); +} + +void loop() { + static unsigned long lastTrigger = 0; + + int micValue = analogRead(MIC_PIN); + Serial.println(micValue); + + if (micValue > 1500 && millis() - lastTrigger > 5000) { + lastTrigger = millis(); + + displayMessage("Detecting..."); + + String response = askGemini("who are you."); + displayMultilineText(response); + } + + delay(100); +} + +void displayMessage(String msg) { + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println(msg); + display.display(); +} + +void displayMultilineText(String text) { + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + + int maxCharsPerLine = 21; + int lineHeight = 10; + int y = 0; + + while (text.length() > 0 && y < SCREEN_HEIGHT) { + String line = text.substring(0, maxCharsPerLine); + text = text.substring(min((unsigned int)maxCharsPerLine, text.length())); + display.setCursor(0, y); + display.println(line); + y += lineHeight; + } + + display.display(); +} + +// Gemini API call +String askGemini(String prompt) { + HTTPClient http; + String endpoint = "https://generativelanguage.googleapis.com/v1/models/gemini-1.5-flash-002:generateContent?key=" + gemini_api_key; + http.begin(endpoint); + http.addHeader("Content-Type", "application/json"); + + prompt.replace("\"", "\\\""); + + String requestBody = "{ \"contents\": [ { \"parts\": [ { \"text\": \"" + prompt + "\" } ] } ] }"; + int code = http.POST(requestBody); + String reply = ""; + + if (code == 200) { + String payload = http.getString(); + StaticJsonDocument<2048> doc; + DeserializationError error = deserializeJson(doc, payload); + + if (!error) { + reply = doc["candidates"][0]["content"]["parts"][0]["text"].as(); + reply.replace("\\n", "\n"); + } else { + Serial.println("JSON Error: " + String(error.c_str())); + reply = "I am Gemini, your AI assistant."; + } + } else { + Serial.println("Gemini API error: " + String(code)); + // Fallback reply when API fails + reply = "I am Gemini, your AI assistant."; + } + + http.end(); + return reply; +} \ No newline at end of file diff --git a/voice_assistant/voice_assistant.ino b/voice_assistant/voice_assistant.ino new file mode 100644 index 0000000..025db02 --- /dev/null +++ b/voice_assistant/voice_assistant.ino @@ -0,0 +1,148 @@ +// Include necessary libraries +#include +#include +#include +#include +#include + +// WiFi credentials +const char* ssid = "thetempleofdoom"; +const char* password = "myhandsonmyself"; + +// Gemini API key +const char* geminiApiKey = "AIzaSyAzFzg_MAcwoH3O-nfriBjlFw5x6UBU5hk"; + +// Pin for the button +// IMPORTANT: Please verify the pin number for your button and update the value below. +const int buttonPin = 0; + +// Display pins +// IMPORTANT: Please verify the pin numbers for your display and update the values below. +#define TFT_CS 5 +#define TFT_DC 4 +#define TFT_RST -1 // Reset pin (often not needed) + +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST); + +void setup() { + // Initialize serial communication + Serial.begin(115200); + + // Initialize the button pin + pinMode(buttonPin, INPUT_PULLUP); + + // Connect to WiFi + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(1000); + Serial.println("Connecting to WiFi..."); + } + Serial.println("Connected to WiFi"); + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + + // Initialize the display + tft.begin(); + tft.setRotation(3); + tft.fillScreen(ILI9341_BLACK); + tft.setTextColor(ILI9341_GREEN); + tft.setTextSize(2); + tft.println("Hacker Voice Assistant"); + tft.println("--------------------"); + tft.setTextSize(1); + tft.print("IP: "); + tft.println(WiFi.localIP()); + tft.setTextSize(2); + tft.println("\nReady for command..."); +} + +void loop() { + // Check if the button is pressed + if (digitalRead(buttonPin) == LOW) { + // Start a voice session + startVoiceSession(); + // After the session, clear the screen and show the ready message again + tft.fillScreen(ILI9341_BLACK); + tft.setCursor(0,0); + tft.setTextSize(2); + tft.println("Hacker Voice Assistant"); + tft.println("--------------------"); + tft.setTextSize(1); + tft.print("IP: "); + tft.println(WiFi.localIP()); + tft.setTextSize(2); + tft.println("\nReady for command..."); + } +} + +void startVoiceSession() { + // Clear the screen for the new session + tft.fillScreen(ILI9341_BLACK); + tft.setCursor(0,0); + tft.setTextSize(2); + + // TODO: Add voice recording and processing code here + Serial.println("Button pressed, starting voice session..."); + displayHackerText("Listening..."); + + // For now, we'll just send a test query to Gemini + delay(2000); // Simulate recording time + displayHackerText("Processing..."); + String query = "Hello, Gemini!"; + String response = sendToGemini(query); + + // Display the response + displayHackerText("User: " + query); + displayHackerText("Gemini: " + response); + Serial.println("Gemini response: " + response); +} + +String sendToGemini(String query) { + HTTPClient http; + String url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key="; + url += geminiApiKey; + + http.begin(url); + http.addHeader("Content-Type", "application/json"); + + StaticJsonDocument<200> doc; + JsonObject content = doc.createNestedObject("contents"); + JsonArray parts = content.createNestedArray("parts"); + JsonObject part = parts.createNestedObject(); + part["text"] = query; + + String requestBody; + serializeJson(doc, requestBody); + + int httpResponseCode = http.POST(requestBody); + + if (httpResponseCode > 0) { + String payload = http.getString(); + StaticJsonDocument<1024> responseDoc; + DeserializationError error = deserializeJson(responseDoc, payload); + + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + return "Error: Failed to parse response."; + } + + if (responseDoc.containsKey("candidates")) { + const char* responseText = responseDoc["candidates"][0]["content"]["parts"][0]["text"]; + return String(responseText); + } else { + return "Error: Invalid response from Gemini."; + } + } else { + Serial.print("Error on sending POST: "); + Serial.println(httpResponseCode); + return "Error: Unable to get response from Gemini."; + } + + http.end(); + return ""; +} + +void displayHackerText(String text) { + tft.println(text); +}