Files
zhengxinandCursor 851e347cf5 初始提交卡包 USB 工程
纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。

Co-authored-by: Cursor <[email protected]>
2026-10-13 21:54:50 +08:00

764 lines
26 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pet_init.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <sys/time.h>
#include <stdio.h>
#include "esp_netif_sntp.h"
#include "freertos/queue.h"
#include "esp_random.h"
#define TAG "PET"
static TaskHandle_t pet_task_handle = NULL;
static void pet_time_sync_task(void *arg);
static void pet_user_write_task(void *arg);
QueueHandle_t user_interact_Queue = NULL;
bool is_ntp_initalized = false;
static void feed_ready_handler(TimerHandle_t xTimer);
TimerHandle_t feed_timer=NULL;
static void travel_ready_handler(TimerHandle_t xTimer);
TimerHandle_t travel_timer=NULL;
uint8_t remaining_hunger=0;
uint8_t remaining_cleanliness=0;
uint8_t remaining_mood=0;
static pet_status_t s_pet_status = {
.hunger = 0, //饱腹值
.cleanliness = 0, //清洁度
.mood = 0, //心情
.intimacy = 0, //亲密度
.stage = PET_STAGE_BABY, //成长状态
.vehicle = PET_VEHICLE_WALK, //出行方式
.last_timestamp = 0, //时间
.last_time = {0},
.travel_time =0, //当前旅行所需时间
.is_travelling = false, //是否在旅游
.travel_timestamp = 0, //触发旅游出行的时间(可计算)
.can_travel = false, //能否旅游状态(可计算)
.feed_time = 14400, //喂食所需时间(固定值)
.feed_timestamp = 0, //上一次喂食时间
.is_feed_cd = false, //是否在喂食冷却
};
/* 城市中文名数组 */
const char *city_names[CITY_COUNT] = {
"公园", "街道", "河流", "古镇", "山道", "水库", "聚落", "果园", "惠州", "东莞", "佛山", "中山", "江门", "清远", "肇庆", "河源", "梅州",
"重庆", "西安", "广州", "南京", "三亚", "北京", "长沙", "张家界", "湘西", "凤凰古镇", "上海", "香格里拉", "丽江", "大理", "西藏",
"厦门", "景德镇", "平潭", "青岛", "北海", "万宁", "淄博", "延吉", "柳州", "宜昌", "漠河", "桂林", "杭州", "西塘", "青海",
"西双版纳", "敦煌", "云南",
};
/* 宠物阶段介绍文案 */
static const char baby_introduction[] = "我还小小的呢,先在附近逛逛就很开心啦!";
static const char teen_introduction[] = "我长大啦!现在可以去更远的城市看一看咯!";
static const char adult_introduction[] = "谢谢你一直陪着我!现在,天涯海角我都能去到啦!";
//static const char travel_return[] = "我回来啦!快看我带回来的好东西~";
//static const char hunger[] = "我回来啦!快看我带回来的好东西~";
static const char *pet_stage_introduction[3] = {
baby_introduction,
teen_introduction,
adult_introduction,
};
/* 全局状态标志 */
bool g_pet_need_first_sync = false; //第一次初始化NVS
bool g_pet_ntp_synced = false; //开机第一次同步NTP
bool g_pet_animation_ready = false; //授时完成等待显示
bool go_travelling() {
// 1. 获取一个32位硬件随机数
uint32_t random_num = esp_random();
// 2. 将随机数映射到 0-99 的范围,判断是否小于20
// 由于随机数在32位空间均匀分布[reference:5],取余100即可得到0-99的均匀分布
if (random_num % 100 < 20) {
return true; // 旅行,award = 1
} else {
return false; // 不旅行,award = 0
}
}
void print_date_time(void) {
time_t now;
struct tm timeinfo;
time(&now); // 获取当前时间戳(秒)
localtime_r(&now, &timeinfo); // 转换为本地时间(需先设置时区)
// 提取年、月、日
int year = timeinfo.tm_year + 1900; // tm_year 从 1900 开始计数
int month = timeinfo.tm_mon + 1; // tm_mon 范围 0~11,需加 1
int day = timeinfo.tm_mday; // tm_mday 为当月第几天(1~31)
// 也可以同时获取时、分、秒
int hour = timeinfo.tm_hour;
int minute = timeinfo.tm_min;
int second = timeinfo.tm_sec;
// 打印日期和时间
ESP_LOGI("NTP", "日期: %04d-%02d-%02d", year, month, day);
ESP_LOGI("NTP", "时间: %02d:%02d:%02d", hour, minute, second);
// 或者格式化输出完整时间
char buffer[64];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeinfo);
ESP_LOGI("NTP", "当前本地时间: %s", buffer);
}
bool obtain_time(void)
{
ESP_LOGI("NTP", "Initializing SNTP");
if(!is_ntp_initalized){
// 1. 设置SNTP服务器
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
// 国内用户可更换为: "cn.pool.ntp.org" 或 "ntp.aliyun.com"
// 2. 初始化并启动SNTP服务
esp_netif_sntp_init(&config);
is_ntp_initalized = true;
}
// 3. 等待时间同步完成 (超时10秒)
if (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(10000)) == ESP_OK) {
ESP_LOGI("NTP", "Time synchronized successfully");
} else {
ESP_LOGE("NTP", "Failed to synchronize time");
return false; // 同步失败,返回 false
}
// 4. 设置时区 (中国时区 UTC+8)
setenv("TZ", "CST-8", 1);
tzset();
// 5. 获取并打印当前时间
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI("NTP", "Current time: %s", strftime_buf);
return true; // 同步成功,返回 true
}
static uint8_t clamp_u8(uint8_t value, uint8_t min, uint8_t max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
static uint16_t clamp_u16(uint16_t value, uint16_t min, uint16_t max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
static void pet_save_status_to_nvs(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open("pet_data", NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "打开 NVS pet_data 失败: %s", esp_err_to_name(err));
return;
}
err = nvs_set_u8(handle, "hunger", s_pet_status.hunger);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 hunger 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u8(handle, "cleanliness", s_pet_status.cleanliness);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 cleanliness 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u8(handle, "mood", s_pet_status.mood);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 mood 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u16(handle, "intimacy", s_pet_status.intimacy);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 intimacy 失败: %s", esp_err_to_name(err));
}
/* 保存最后时间戳(u64) */
err = nvs_set_u64(handle, "last_ts", s_pet_status.last_timestamp);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 last_ts 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u8(handle, "is_travelling", s_pet_status.is_travelling);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 is_travelling 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u8(handle, "is_feed_cd", s_pet_status.is_feed_cd);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 is_feed_cd 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u64(handle, "travel_ts", s_pet_status.travel_timestamp);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 travel_timestamp 失败: %s", esp_err_to_name(err));
}
err = nvs_set_u64(handle, "feed_timestamp", s_pet_status.feed_timestamp);
if (err != ESP_OK) {
ESP_LOGW(TAG, "保存 feed_timestamp 失败: %s", esp_err_to_name(err));
}
err = nvs_commit(handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "NVS 提交失败: %s", esp_err_to_name(err));
}
nvs_close(handle);
}
static void pet_load_status_from_nvs(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open("pet_data", NVS_READWRITE, &handle);
uint8_t hunger = 0;
uint8_t cleanliness = 0;
uint8_t mood = 0;
uint16_t intimacy = 0;
uint8_t is_travelling = 0;
uint8_t is_feed_cd = 0;
esp_err_t err_hunger = nvs_get_u8(handle, "hunger", &hunger);
esp_err_t err_cleanliness = nvs_get_u8(handle, "cleanliness", &cleanliness);
esp_err_t err_mood = nvs_get_u8(handle, "mood", &mood);
esp_err_t err_intimacy = nvs_get_u16(handle, "intimacy", &intimacy);
esp_err_t err_last_ts = nvs_get_u64(handle, "last_ts", &s_pet_status.last_timestamp);
esp_err_t err_is_travelling = nvs_get_u8(handle,"is_travelling",&is_travelling);
if (err_is_travelling == ESP_OK) {
s_pet_status.is_travelling = is_travelling;
};
esp_err_t err_travel_timestamp = nvs_get_u64(handle,"travel_ts",&s_pet_status.travel_timestamp);
esp_err_t err_is_feed_cd = nvs_get_u8(handle,"is_feed_cd",&is_feed_cd);
if (err_is_feed_cd == ESP_OK) {
s_pet_status.is_feed_cd = is_feed_cd;
};
esp_err_t err_feed_timestamp = nvs_get_u64(handle,"feed_timestamp",&s_pet_status.feed_timestamp);
if (err_hunger == ESP_OK && err_cleanliness == ESP_OK && err_mood == ESP_OK && err_intimacy == ESP_OK && err_last_ts == ESP_OK) {
ESP_LOGW(TAG, "打开 NVSdata 成功,进入读取分支 ");
s_pet_status.hunger = clamp_u8(hunger, 0, 100);
s_pet_status.cleanliness = clamp_u8(cleanliness, 0, 100);
s_pet_status.mood = clamp_u8(mood, 0, 100);
s_pet_status.intimacy = clamp_u16(intimacy, 0, 300);
/* 将 u64 转换为 struct tm(本地时区) */
time_t t = (time_t)s_pet_status.last_timestamp;
localtime_r(&t, &s_pet_status.last_time);
g_pet_need_first_sync = false;
nvs_close(handle);
}else{
ESP_LOGW(TAG, "打开 NVSdata 失败,进入初始化分支 ");
s_pet_status.hunger = 0;
s_pet_status.cleanliness = 0;
s_pet_status.mood = 0;
s_pet_status.intimacy = 0;
nvs_set_u8(handle, "hunger", s_pet_status.hunger);
nvs_set_u8(handle, "cleanliness", s_pet_status.cleanliness);
nvs_set_u8(handle, "mood", s_pet_status.mood);
nvs_set_u16(handle, "intimacy", s_pet_status.intimacy);
nvs_set_u8(handle,"is_travelling",s_pet_status.is_travelling);
nvs_set_u64(handle,"travel_ts",s_pet_status.travel_timestamp);
nvs_set_u8(handle,"is_feed_cd",s_pet_status.is_feed_cd);
nvs_set_u64(handle,"feed_timestamp",s_pet_status.feed_timestamp);
err = nvs_commit(handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "初始化 NVS pet_data 提交失败: %s", esp_err_to_name(err));
}
g_pet_need_first_sync = true; // 标记需要第一次时间同步
nvs_close(handle);
}
return;
}
pet_stage_t pet_get_stage(uint16_t intimacy)
{
if (intimacy >= 200) {
return PET_STAGE_ADULT;
}
if (intimacy >= 100) {
return PET_STAGE_TEEN;
}
return PET_STAGE_BABY;
}
static void pet_update_stage(void)
{
s_pet_status.stage = pet_get_stage(s_pet_status.intimacy);
}
static void pet_update_can_travel(void)
{
if (s_pet_status.is_travelling) {
s_pet_status.can_travel = false;
return;
}
if (s_pet_status.mood >= 60 && s_pet_status.intimacy >= 30 && s_pet_status.hunger >= 30) {
s_pet_status.can_travel = true;
} else {
s_pet_status.can_travel = false;
}
}
static void pet_update_travel_time(void)
{
switch (s_pet_status.vehicle) {
case PET_VEHICLE_WALK:
s_pet_status.travel_time = 30 * 60; /* 30 minutes */
break;
case PET_VEHICLE_BIKE:
s_pet_status.travel_time = 60 * 60; /* 60 minutes */
break;
case PET_VEHICLE_MOTORCYCLE:
s_pet_status.travel_time = 3 * 60 * 60; /* 3 hours */
break;
case PET_VEHICLE_CAR:
s_pet_status.travel_time = 6 * 60 * 60; /* 6 hours */
break;
case PET_VEHICLE_PLANE:
s_pet_status.travel_time = 12 * 60 * 60; /* 12 hours */
break;
default:
s_pet_status.travel_time = 30 * 60;
break;
}
}
pet_vehicle_t pet_get_vehicle_stage(uint16_t intimacy)
{
if (intimacy >= 200) {
return PET_VEHICLE_PLANE;
}
if (intimacy >= 150) {
return PET_VEHICLE_CAR;
}
if (intimacy >= 100) {
return PET_VEHICLE_MOTORCYCLE;
}
if (intimacy >= 50) {
return PET_VEHICLE_BIKE;
}
return PET_VEHICLE_WALK;
}
static void pet_update_vehicle_stage(void)
{
s_pet_status.vehicle = pet_get_vehicle_stage(s_pet_status.intimacy);
}
static const char *pet_stage_name(pet_stage_t stage)
{
switch (stage) {
case PET_STAGE_BABY:
return "幼崽";
case PET_STAGE_TEEN:
return "少年";
case PET_STAGE_ADULT:
return "成年";
default:
return "未知";
}
}
static const char *pet_stage_vehicle_name(pet_vehicle_t stage)
{
switch (stage) {
case PET_VEHICLE_WALK:
return "步行";
case PET_VEHICLE_BIKE:
return "单车";
case PET_VEHICLE_MOTORCYCLE:
return "摩托";
case PET_VEHICLE_CAR:
return "汽车";
case PET_VEHICLE_PLANE:
return "飞机";
default:
return "未知";
}
}
static void pet_log_status(const char *action)
{
ESP_LOGI(TAG, "%s: 饱腹=%u, 清洁=%u, 心情=%u, 亲密度=%u, 形态=%s, 出行方式=%s",
action,
s_pet_status.hunger,
s_pet_status.cleanliness,
s_pet_status.mood,
s_pet_status.intimacy,
pet_stage_name(s_pet_status.stage),
pet_stage_vehicle_name(s_pet_status.vehicle));
}
void pet_init(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open("pet_data", NVS_READWRITE, &handle);
nvs_set_u8(handle, "hunger", 30);
nvs_set_u8(handle, "cleanliness", 30);
nvs_set_u8(handle, "mood", 30);
nvs_set_u8(handle,"is_travelling",0);
nvs_set_u64(handle,"travel_ts",s_pet_status.travel_timestamp);
nvs_set_u8(handle,"is_feed_cd",0);
nvs_set_u64(handle,"feed_timestamp",s_pet_status.feed_timestamp);
err = nvs_commit(handle);
nvs_close(handle);
pet_load_status_from_nvs();
pet_update_stage();
pet_update_vehicle_stage();
ESP_LOGI(TAG, "宠物加载完成,当前形态=%s,出行方式=%s",
pet_stage_name(s_pet_status.stage),
pet_stage_vehicle_name(s_pet_status.vehicle));
pet_log_status("加载后状态");
pet_task_stack = heap_caps_malloc(PET_TASK_STACK_SIZE, MALLOC_CAP_INTERNAL);
if (pet_task_stack == NULL)
{
ESP_LOGE("LVGL", "Failed to allocate PET task stack");
return;
}
write_nvs_task_stack = heap_caps_malloc(HOUR_WRITE_STACK_SIZE, MALLOC_CAP_INTERNAL);
if (write_nvs_task_stack == NULL)
{
ESP_LOGE("LVGL", "Failed to allocate PET_WRITE task stack");
return;
}
user_interact_Queue = xQueueCreate(5, sizeof(int));
if (user_interact_Queue == NULL) {
ESP_LOGE(TAG, "Failed to create user_interact_Queue");
return;
}
xTaskCreateStaticPinnedToCore(pet_time_sync_task, "PET", PET_TASK_STACK_SIZE, NULL, PET_TASK_PRIORITY, pet_task_stack, &pet_task_tcb, 1);
xTaskCreateStaticPinnedToCore(pet_user_write_task, "PET_WRITE", HOUR_WRITE_STACK_SIZE, NULL, HOUR_WRITE_TASK_PRIORITY, write_nvs_task_stack, &write_nvs_task_tcb, 1);
}
const pet_status_t *pet_get_status(void)
{
return &s_pet_status;
}
/*交互动作数值计算*/
void pet_feed(void)
{
s_pet_status.hunger = clamp_u8(s_pet_status.hunger + 30, 0, 100);
s_pet_status.mood = clamp_u8(s_pet_status.mood + 5, 0, 100);
s_pet_status.intimacy = clamp_u16(s_pet_status.intimacy + 2, 0, 300);
pet_update_stage();
pet_update_vehicle_stage();
time_t now;
time(&now);
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
pet_log_status("喂食");
}
void pet_bath(void)
{
s_pet_status.cleanliness = clamp_u8(s_pet_status.cleanliness + 100, 0, 100);
s_pet_status.mood = clamp_u8(s_pet_status.mood + 5, 0, 100);
s_pet_status.intimacy = clamp_u16(s_pet_status.intimacy + 2, 0, 300);
pet_update_stage();
pet_update_vehicle_stage();
time_t now;
time(&now);
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
pet_log_status("洗澡");
}
void pet_interact(void)
{
s_pet_status.mood = clamp_u8(s_pet_status.mood + 5, 0, 100);
s_pet_status.intimacy = clamp_u16(s_pet_status.intimacy + 1, 0, 300);
pet_update_stage();
pet_update_vehicle_stage();
time_t now;
time(&now);
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
pet_log_status("点击互动");
}
void pet_travel_return(void)
{
s_pet_status.mood = clamp_u8(s_pet_status.mood + 20, 0, 100);
s_pet_status.intimacy = clamp_u16(s_pet_status.intimacy + 5, 0, 300);
pet_update_stage();
pet_update_vehicle_stage();
time_t now;
time(&now);
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
pet_log_status("旅行归来");
}
void cal_deacy(uint64_t elapsed){
if(elapsed> 172800){
s_pet_status.hunger = 0;
s_pet_status.cleanliness = 0;
s_pet_status.mood = 0;
}
else{
uint8_t decay_hunger = elapsed / 450;
uint8_t decay_cleanliness = elapsed / 1200;
uint8_t decay_mood = elapsed / 1500;
uint8_t remaining_hunger = elapsed % 450;
uint8_t remaining_cleanliness = elapsed % 1200;
uint8_t remaining_mood = elapsed % 1500;
s_pet_status.hunger = (decay_hunger >= s_pet_status.hunger) ? 0 : s_pet_status.hunger - decay_hunger;
s_pet_status.cleanliness = (decay_cleanliness >= s_pet_status.cleanliness) ? 0 : s_pet_status.cleanliness - decay_cleanliness;
s_pet_status.mood = (decay_mood >= s_pet_status.mood) ? 0 : s_pet_status.mood - decay_mood;
pet_log_status("加载后状态");
}
}
static void compare_time_cd(uint64_t now){
if(s_pet_status.is_feed_cd){
uint64_t elapsed = now - s_pet_status.feed_timestamp;
if(elapsed > 14400){
s_pet_status.is_feed_cd = false;
s_pet_status.feed_timestamp = 0;
}
}
if(s_pet_status.is_travelling){
uint64_t elapsed = now - s_pet_status.travel_timestamp;
if(elapsed > s_pet_status.travel_time){
s_pet_status.is_travelling = false;
s_pet_status.travel_timestamp = 0;
}
}
return;
}
static void feed_ready_handler(TimerHandle_t xTimer) {
s_pet_status.is_feed_cd = false;
s_pet_status.feed_timestamp = 0;
ESP_LOGW("PET", "进入定时器结算");
pet_save_status_to_nvs();
feed_timer = NULL;
xTimerDelete(xTimer, 0);
}
static void travel_ready_handler(TimerHandle_t xTimer) {
s_pet_status.is_travelling = false;
s_pet_status.travel_timestamp = 0;
ESP_LOGW("PET", "进入定时器结算");
pet_save_status_to_nvs();
travel_timer = NULL;
xTimerDelete(xTimer, 0);
}
/*
* 时间同步任务骨架
* - 如果无网络/未能 NTP,同步失败后进入休眠/等待
* - 同步成功后设置 g_pet_animation_ready
* - 如果是第一次初始化(g_pet_need_first_sync==true),写入当前时间到 NVS
* - 否则根据过去时间计算状态衰减(此处留占位注释,具体公式可在 TODO 中实现)
*/
static void pet_time_sync(void){
if (!g_wifi_state){
// 无网络则休眠,等待外部事件唤醒或下次循环
ESP_LOGE("PET", "无WIFI进入待机状态");
vTaskDelay(pdMS_TO_TICKS(5000));
}
else{
g_pet_ntp_synced = obtain_time();
if(!g_pet_ntp_synced){
ESP_LOGE("PET", "有WIFI但网络同步失败,进入休眠状态");
vTaskDelay(pdMS_TO_TICKS(5000));
}
else{
ESP_LOGW("PET", "有WIFI且时钟同步成功,开启动画窗口");
time_t now;
time(&now);
if (g_pet_need_first_sync) {
ESP_LOGW("PET", "第一次同步,写入当前时间到 NVS");
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
g_pet_need_first_sync = false;
g_pet_animation_ready = true;
}else {
ESP_LOGW("PET", "不是第一次同步,计算过去时间并衰减属性");
uint64_t elapsed = (uint64_t)now - s_pet_status.last_timestamp;
ESP_LOGW("PET", "相比之前过去了%llu秒",elapsed);
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
compare_time_cd((uint64_t)now);
char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%c", &s_pet_status.last_time);//覆盖上次启动时间
ESP_LOGI("NTP", "同步时间: %s", strftime_buf);
cal_deacy(elapsed);
pet_save_status_to_nvs();
pet_update_stage();
pet_update_vehicle_stage();
g_pet_animation_ready = true;
}
}
}
}
static void pet_time_timer(void){
const TickType_t delay_ms = pdMS_TO_TICKS(60000*5); // 5 分钟间隔
TickType_t last_wake = xTaskGetTickCount();
uint32_t tick_count = 0;
int times_hunger;
int times_cleanliness;
int times_mood;
while (1) {
vTaskDelayUntil(&last_wake, delay_ms);
tick_count++;
ESP_LOGI("NTP", "五分钟间隔");
time_t now;
time(&now);
//uint64_t elapsed = (uint64_t)now - s_pet_status.last_timestamp;
uint8_t compensate_tickcount_hunger= remaining_hunger / 300; //掉电补偿
uint8_t compensate_tickcount_cleanliness= remaining_cleanliness / 300; //掉电补偿
uint8_t compensate_tickcount_mood= remaining_mood / 300;
int cmd1=1;int cmd2=2;int cmd3=3;int cmd4=4;
if(s_pet_status.is_feed_cd){
uint64_t elapsed = (uint64_t)now - s_pet_status.feed_timestamp;
if(elapsed > s_pet_status.feed_time - 300){ //最后五分钟
feed_timer = xTimerCreate("feed_ready_can_use", pdMS_TO_TICKS((s_pet_status.feed_time-elapsed)*1000),
pdFALSE, (void*)0,
feed_ready_handler);
xTimerStart(feed_timer, 0);
ESP_LOGW("PET", "即将完成喂食冷却,启用高精度定时");
}
}
if(s_pet_status.is_travelling){
uint64_t elapsed = (uint64_t)now - s_pet_status.travel_timestamp;
if(elapsed > s_pet_status.travel_time - 300){ //最后五分钟
travel_timer = xTimerCreate("travel_ready_done", pdMS_TO_TICKS((s_pet_status.travel_time-elapsed)*1000),
pdFALSE, (void*)0,
travel_ready_handler);
xTimerStart(travel_timer, 0);
ESP_LOGW("PET", "即将完成旅行冷却,启用高精度定时");
}
}
/* (15 分钟) 饱腹 -2 */
if ((tick_count + compensate_tickcount_hunger) % 3 == 0) {
if (s_pet_status.hunger > 2) s_pet_status.hunger -= 2;
else s_pet_status.hunger = 0;
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
remaining_hunger = 0;
ESP_LOGW("PET", "饱腹-2");
}
/* (20 分钟) 清洁 -1 */
if ((tick_count + compensate_tickcount_cleanliness) % 4 == 0) {
if (s_pet_status.cleanliness > 0) s_pet_status.cleanliness -= 1;
else s_pet_status.cleanliness = 0;
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
ESP_LOGW("PET", "清洁-1");
}
/* (25 分钟) 心情 -1 */
if ((tick_count + compensate_tickcount_cleanliness)% 5 == 0) {
if (s_pet_status.mood > 0) s_pet_status.mood -= 1;
else s_pet_status.mood = 0;
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
ESP_LOGW("PET", "心情-1");
}
if(tick_count % 2 ==0 && go_travelling() && s_pet_status.can_travel){//缺少场景模式判断
xQueueSend(user_interact_Queue, &cmd4,0);
}
/* 每 12 次(60 分钟) 写入 NVS */
if ((tick_count % 12) == 0) {
s_pet_status.last_timestamp = (uint64_t)now;
localtime_r(&now, &s_pet_status.last_time);
pet_save_status_to_nvs();
ESP_LOGI(TAG, "Hourly NVS write (tick %u)", tick_count);
}
}
}
static void pet_time_sync_task(void *arg)
{
while (1){
uxTaskGetStackHighWaterMark(NULL);
if(g_pet_animation_ready == false){
pet_time_sync();
}else{
pet_time_timer();
}
}
}
static void pet_user_write_task(void *arg)
{
int received_cmd = 0;
while (1) {
/* 阻塞直到收到数据,等待用户命令 */
uxTaskGetStackHighWaterMark(NULL);
if (xQueueReceive(user_interact_Queue, &received_cmd, portMAX_DELAY) == pdPASS) {
ESP_LOGI(TAG, "pet_user_write_task: received command %d", received_cmd);
switch (received_cmd) {
case 1:
pet_feed();
ESP_LOGI(TAG, "命令1执行");
break;
case 2:
pet_bath();
ESP_LOGI(TAG, "命令2执行");
break;
case 3:
pet_interact();
ESP_LOGI(TAG, "命令3执行");
break;
case 4:
pet_travel_return();
//pet_start_travelling();
ESP_LOGI(TAG, "命令4执行");
break;
default:
ESP_LOGW(TAG, "未知用户命令 %d", received_cmd);
break;
}
}
}
}
void pet_start_travelling(void)
{
s_pet_status.mood = clamp_u8(s_pet_status.mood + 20, 0, 100);
}