Files
P4-USB-transfer/main/PET/pet_init.c
T

764 lines
26 KiB
C
Raw Normal View History

2026-10-13 21:54:50 +08:00
#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);
}