chore: import local project into Gitea
This commit is contained in:
BIN
._.gitignore
Normal file
BIN
._.gitignore
Normal file
Binary file not shown.
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# 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
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Vendor SDK checkouts
|
||||||
|
esp-idf/
|
||||||
|
**/esp-idf/
|
||||||
|
managed_components/
|
||||||
|
|
||||||
|
# Local generated configuration
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
||||||
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"cmake.sourceDirectory": "F:/dev_shit/p4 chatbot 83125/p4_gemini_chat",
|
||||||
|
"idf.espIdfPathWin": "C:/Users/india/Desktop/esp-idf/",
|
||||||
|
"idf.toolsPathWin": "C:\\Users\\india\\.espressif",
|
||||||
|
"idf.pythonInstallPath": "C:\\Users\\india\\.espressif\\tools\\idf-python\\3.11.2\\python.exe",
|
||||||
|
"idf.portWin": "COM3"
|
||||||
|
}
|
||||||
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# p4 Gemini chat (ESP-IDF)
|
||||||
|
|
||||||
|
Parent tree for Gemini-enabled UI on Espressif’s **ESP32-P4-class** toolchain.
|
||||||
|
|
||||||
|
Main firmware: **`p4_gemini_chat/`**
|
||||||
|
|
||||||
|
| Path | Notes |
|
||||||
|
|------|-------|
|
||||||
|
| `p4_gemini_chat/` | CMake + `main/` sources, `sdkconfig`, `managed_components` |
|
||||||
|
| `components/` | Shared component libraries |
|
||||||
|
| `ai brains/` | Prompt / persona assets |
|
||||||
|
| `build/` | Local IDF artifacts (keep untracked) |
|
||||||
|
|
||||||
|
## Build flow
|
||||||
|
|
||||||
|
Install **ESP-IDF** matching Espressif P4 toolchain requirements, activate environment, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd p4_gemini_chat
|
||||||
|
idf.py set-target esp32p4 # confirm precise target alias with Espressif release notes
|
||||||
|
idf.py fullclean build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
Export `GEMINI_API_KEY` (or whichever env var main sources expect) securely—never bake keys into firmware images.
|
||||||
2
components/bsp/CMakeLists.txt
Normal file
2
components/bsp/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "bsp.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
23
components/bsp/bsp.c
Normal file
23
components/bsp/bsp.c
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "bsp/esp-bsp.h"
|
||||||
|
#include "bsp.h"
|
||||||
|
|
||||||
|
void bsp_display_start(void)
|
||||||
|
{
|
||||||
|
bsp_display_cfg_t cfg = {
|
||||||
|
.lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
|
||||||
|
.buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
|
||||||
|
.double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
|
||||||
|
.flags = {
|
||||||
|
.buff_dma = true,
|
||||||
|
.buff_spiram = false,
|
||||||
|
.sw_rotate = false,
|
||||||
|
}};
|
||||||
|
bsp_display_start_with_config(&cfg);
|
||||||
|
bsp_display_backlight_on();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsp_touch_init(void)
|
||||||
|
{
|
||||||
|
// The touch driver is initialized as part of bsp_display_start()
|
||||||
|
// in the esp-bsp component, so this function can be left empty.
|
||||||
|
}
|
||||||
12
components/bsp/bsp.h
Normal file
12
components/bsp/bsp.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void bsp_display_start(void);
|
||||||
|
void bsp_touch_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
5
p4_gemini_chat/.vscode/settings.json
vendored
Normal file
5
p4_gemini_chat/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"idf.openOcdConfigs": [
|
||||||
|
"board/esp32p4-builtin.cfg"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
p4_gemini_chat/CMakeLists.txt
Normal file
3
p4_gemini_chat/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(p4_gemini_chat)
|
||||||
13
p4_gemini_chat/Kconfig.projbuild
Normal file
13
p4_gemini_chat/Kconfig.projbuild
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
menu "Gemini Chat Config"
|
||||||
|
config GEMINI_API_KEY
|
||||||
|
string "Gemini API Key"
|
||||||
|
help
|
||||||
|
Paste the API key from Google AI Studio.
|
||||||
|
config WIFI_SSID
|
||||||
|
string "WiFi SSID"
|
||||||
|
config WIFI_PASS
|
||||||
|
string "WiFi Password"
|
||||||
|
config GEMINI_MODEL
|
||||||
|
string "Gemini model name"
|
||||||
|
default "gemini-2.5-flash"
|
||||||
|
endmenu
|
||||||
2
p4_gemini_chat/build.bat
Normal file
2
p4_gemini_chat/build.bat
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
call C:/Users/india/Desktop/esp-idf/export.bat
|
||||||
|
idf.py build
|
||||||
18
p4_gemini_chat/dependencies.lock
Normal file
18
p4_gemini_chat/dependencies.lock
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
dependencies:
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 5.4.1
|
||||||
|
lvgl/lvgl:
|
||||||
|
component_hash: 948bff879a345149b83065535bbc4a026ce9f47498a22881e432a264b9098015
|
||||||
|
dependencies: []
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 8.3.11
|
||||||
|
direct_dependencies:
|
||||||
|
- idf
|
||||||
|
- lvgl/lvgl
|
||||||
|
manifest_hash: ba5fbe24af6b54c310e2fcf43426ef3f04e9de1ac47206b696b1bb81856e9340
|
||||||
|
target: esp32
|
||||||
|
version: 2.0.0
|
||||||
4
p4_gemini_chat/main/CMakeLists.txt
Normal file
4
p4_gemini_chat/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "main.c"
|
||||||
|
REQUIRES esp_http_client esp_wifi nvs_flash json lvgl esp_timer
|
||||||
|
)
|
||||||
17
p4_gemini_chat/main/idf_component.yml
Normal file
17
p4_gemini_chat/main/idf_component.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
## Required IDF version
|
||||||
|
idf:
|
||||||
|
version: '>=4.1.0'
|
||||||
|
# # Put list of dependencies here
|
||||||
|
# # For components maintained by Espressif:
|
||||||
|
# component: "~1.0.0"
|
||||||
|
# # For 3rd party components:
|
||||||
|
# username/component: ">=1.0.0,<2.0.0"
|
||||||
|
# username2/component2:
|
||||||
|
# version: "~1.0.0"
|
||||||
|
# # For transient dependencies `public` flag can be set.
|
||||||
|
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||||
|
# # All dependencies of `main` are public by default.
|
||||||
|
# public: true
|
||||||
|
lvgl/lvgl: ~8.3.11
|
||||||
201
p4_gemini_chat/main/main.c
Normal file
201
p4_gemini_chat/main/main.c
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
#include "esp_event.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_http_client.h"
|
||||||
|
#include "esp_crt_bundle.h"
|
||||||
|
#include "cJSON.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "bsp.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAG "P4-GEMINI"
|
||||||
|
|
||||||
|
// ----- Kconfig bindings
|
||||||
|
#define WIFI_SSID CONFIG_WIFI_SSID
|
||||||
|
#define WIFI_PASS CONFIG_WIFI_PASS
|
||||||
|
#define GEMINI_API_KEY CONFIG_GEMINI_API_KEY
|
||||||
|
#define GEMINI_MODEL CONFIG_GEMINI_MODEL
|
||||||
|
|
||||||
|
// Simple UI elements
|
||||||
|
static lv_obj_t *ta; // text area (user input)
|
||||||
|
static lv_obj_t *logbox; // label for responses
|
||||||
|
|
||||||
|
// Cheap LVGL tick (adjust to your BSP pattern)
|
||||||
|
static void lv_tick_cb(void* arg){ lv_tick_inc(5); }
|
||||||
|
|
||||||
|
static void wifi_init(void) {
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
esp_netif_create_default_wifi_sta();
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
wifi_config_t wifi_config = {0};
|
||||||
|
strncpy((char*)wifi_config.sta.ssid, WIFI_SSID, sizeof(wifi_config.sta.ssid)-1);
|
||||||
|
strncpy((char*)wifi_config.sta.password, WIFI_PASS, sizeof(wifi_config.sta.password)-1);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_connect());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build {"contents":[{"role":"user","parts":[{"text":"..."}]}]}
|
||||||
|
static char* build_gemini_body(const char* user_text) {
|
||||||
|
cJSON *root = cJSON_CreateObject();
|
||||||
|
cJSON *contents = cJSON_AddArrayToObject(root, "contents");
|
||||||
|
cJSON *item = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(item, "role", "user");
|
||||||
|
cJSON *parts = cJSON_AddArrayToObject(item, "parts");
|
||||||
|
cJSON *part0 = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(part0, "text", user_text);
|
||||||
|
cJSON_AddItemToArray(parts, part0);
|
||||||
|
cJSON_AddItemToArray(contents, item);
|
||||||
|
char *out = cJSON_PrintUnformatted(root);
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse response.response.candidates[0].content.parts[0].text
|
||||||
|
static char* parse_gemini_reply(const char* json) {
|
||||||
|
cJSON *root = cJSON_Parse(json);
|
||||||
|
if(!root) return NULL;
|
||||||
|
cJSON *resp = cJSON_GetObjectItem(root, "candidates");
|
||||||
|
if(!cJSON_IsArray(resp) || cJSON_GetArraySize(resp)==0){ cJSON_Delete(root); return NULL; }
|
||||||
|
cJSON *c0 = cJSON_GetArrayItem(resp, 0);
|
||||||
|
cJSON *content = cJSON_GetObjectItem(c0, "content");
|
||||||
|
cJSON *parts = content ? cJSON_GetObjectItem(content, "parts") : NULL;
|
||||||
|
if(!parts || !cJSON_IsArray(parts) || cJSON_GetArraySize(parts)==0){ cJSON_Delete(root); return NULL; }
|
||||||
|
cJSON *p0 = cJSON_GetArrayItem(parts, 0);
|
||||||
|
cJSON *text = cJSON_GetObjectItem(p0, "text");
|
||||||
|
char *dup = text && cJSON_IsString(text) ? strdup(text->valuestring) : NULL;
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t http_event(esp_http_client_event_t *evt){
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* gemini_generate(const char* prompt){
|
||||||
|
// Use TLS bundle (no manual root CA pin)
|
||||||
|
esp_http_client_config_t cfg = {
|
||||||
|
.url = "https://generativelanguage.googleapis.com/v1beta/models/" GEMINI_MODEL ":generateContent?key=" GEMINI_API_KEY,
|
||||||
|
.method = HTTP_METHOD_POST,
|
||||||
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||||
|
.event_handler = http_event,
|
||||||
|
.timeout_ms = 20000,
|
||||||
|
};
|
||||||
|
esp_http_client_handle_t client = esp_http_client_init(&cfg);
|
||||||
|
|
||||||
|
char *body = build_gemini_body(prompt);
|
||||||
|
esp_http_client_set_header(client, "Content-Type", "application/json");
|
||||||
|
ESP_ERROR_CHECK(esp_http_client_open(client, strlen(body)));
|
||||||
|
int w = esp_http_client_write(client, body, strlen(body));
|
||||||
|
free(body);
|
||||||
|
if(w <= 0){ esp_http_client_cleanup(client); return strdup("[write failed]"); }
|
||||||
|
|
||||||
|
esp_http_client_fetch_headers(client);
|
||||||
|
int status = esp_http_client_get_status_code(client);
|
||||||
|
ESP_LOGI(TAG, "HTTP Status = %d", status);
|
||||||
|
|
||||||
|
int len = esp_http_client_get_content_length(client);
|
||||||
|
if(len < 0) len = 4096; // fallback
|
||||||
|
char *buf = calloc(1, len+1);
|
||||||
|
int r = esp_http_client_read_response(client, buf, len);
|
||||||
|
esp_http_client_close(client);
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
|
||||||
|
if(r <= 0){
|
||||||
|
free(buf);
|
||||||
|
return strdup("[no response]");
|
||||||
|
}
|
||||||
|
|
||||||
|
char *reply = parse_gemini_reply(buf);
|
||||||
|
if(!reply) {
|
||||||
|
ESP_LOGE(TAG, "Failed to parse JSON response: %s", buf);
|
||||||
|
reply = strdup("[parse error]");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task to call Gemini API
|
||||||
|
static void gemini_task(void *pvParameters) {
|
||||||
|
char *prompt = (char *)pvParameters;
|
||||||
|
char *ans = gemini_generate(prompt);
|
||||||
|
|
||||||
|
// Update UI from the main thread context if possible, for now direct update
|
||||||
|
// For robust applications, use a queue to pass data to the main loop
|
||||||
|
lv_label_set_text_fmt(logbox, "You:\n%s\n\nAI:\n%s", prompt, ans ? ans : "(null)");
|
||||||
|
|
||||||
|
free(prompt); // Free the duplicated prompt string
|
||||||
|
free(ans);
|
||||||
|
vTaskDelete(NULL); // Delete the task
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI handler: send prompt -> show reply
|
||||||
|
static void on_send(lv_event_t *e){
|
||||||
|
const char *user = lv_textarea_get_text(ta);
|
||||||
|
if(!user || strlen(user)==0) return;
|
||||||
|
lv_label_set_text_fmt(logbox, "You:\n%s\n\nAI:\n[thinking...]", user);
|
||||||
|
|
||||||
|
// Duplicate the user text for the task
|
||||||
|
char *prompt_copy = strdup(user);
|
||||||
|
|
||||||
|
// Create a task to handle the Gemini call
|
||||||
|
xTaskCreate(gemini_task, "gemini_task", 8192, prompt_copy, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ui_init(void){
|
||||||
|
// You already have BSP display/touch init in your Waveshare demos; call that first.
|
||||||
|
// Here we only do the LVGL widgets.
|
||||||
|
|
||||||
|
// Basic container
|
||||||
|
lv_obj_t *scr = lv_scr_act();
|
||||||
|
ta = lv_textarea_create(scr);
|
||||||
|
lv_obj_set_size(ta, 700, 120);
|
||||||
|
lv_obj_set_pos(ta, 10, 10);
|
||||||
|
lv_textarea_set_placeholder_text(ta, "Ask something…");
|
||||||
|
|
||||||
|
lv_obj_t *btn = lv_btn_create(scr);
|
||||||
|
lv_obj_set_size(btn, 140, 60);
|
||||||
|
lv_obj_set_pos(btn, 10, 140);
|
||||||
|
lv_obj_add_event_cb(btn, on_send, LV_EVENT_CLICKED, NULL);
|
||||||
|
lv_obj_t *lbl = lv_label_create(btn);
|
||||||
|
lv_label_set_text(lbl, "Send");
|
||||||
|
lv_obj_center(lbl);
|
||||||
|
|
||||||
|
logbox = lv_label_create(scr);
|
||||||
|
lv_obj_set_size(logbox, 700, 520);
|
||||||
|
lv_obj_set_pos(logbox, 10, 210);
|
||||||
|
lv_label_set_long_mode(logbox, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_label_set_text(logbox, "Ready.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void){
|
||||||
|
// NVS (Wi-Fi stores creds; not strictly needed if Kconfig only)
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
// Wi-Fi
|
||||||
|
wifi_init();
|
||||||
|
|
||||||
|
// Initialize display and touch
|
||||||
|
bsp_display_start();
|
||||||
|
|
||||||
|
// LVGL core tick
|
||||||
|
lv_init();
|
||||||
|
const esp_timer_create_args_t tcfg = { .callback = lv_tick_cb, .name = "lv_tick" };
|
||||||
|
esp_timer_handle_t tmr; esp_timer_create(&tcfg, &tmr);
|
||||||
|
esp_timer_start_periodic(tmr, 5000); // 5ms
|
||||||
|
|
||||||
|
ui_init();
|
||||||
|
|
||||||
|
// Simple LVGL loop (replace with your panel’s event/task loop)
|
||||||
|
while (1) { lv_timer_handler(); vTaskDelay(pdMS_TO_TICKS(5)); }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user