Files
Video_Game_Mini_Maps/main/main.cpp
valentineautos 16ffc2ab10 initial commit
2025-12-05 09:22:55 +00:00

301 lines
9.8 KiB
C++

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_memory_utils.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "lvgl.h"
#include "bsp/esp-bsp.h"
#include "bsp/display.h"
#include "bsp_board_extra.h"
#include "driver/twai.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "freertos/FreeRTOS.h"
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <cmath>
#include "CANBus_Driver.h"
#include "gps_locator.h"
#include "images/maps_bg.h"
#include "images/car_icon.h"
#include "images/north_pointer.h"
#include "images/no_satellite.h"
#define DEG_TO_RAD(deg) ((deg) * M_PI / 180.0)
#define RAD_TO_DEG(rad) ((rad) * 180.0 / M_PI)
// declarations
LV_IMG_DECLARE(maps_bg)
LV_IMG_DECLARE(car_icon);
LV_IMG_DECLARE(north_pointer);
LV_IMG_DECLARE(no_satellite);
// screens
lv_obj_t *main_scr;
// global elements
lv_obj_t *no_satellite_bg;
lv_obj_t *map_container;
lv_obj_t *car_icon_img;
lv_obj_t *north_pointer_img;
void (*can_message_handler)(twai_message_t *message) = NULL;
// CONTROL VARIABLE INIT
#define MIN_MOVE_DISTANCE 2.0 // distance in meters to trigger icon rotation
float current_latitude = 0.0;
float current_longitude = 0.0;
float new_latitude = 0.0;
float new_longitude = 0.0;
int current_angle = 0;
int new_angle = 0;
bool receiving_data = false; // has the first data been received
volatile bool data_ready = false; // new incoming data
bool init_anim_complete = false; // needle sweep completed - tbc
bool location_initialized = false; // has the initial GPS location been set
// general color palettes
const lv_color_t PALETTE_BLACK = LV_COLOR_MAKE(0, 0, 0);
const lv_color_t PALETTE_BLUE = LV_COLOR_MAKE(31, 104, 135); // fuel arc main
const lv_color_t PALETTE_BLUE_NEON = LV_COLOR_MAKE(83, 252, 254); // fuel arc indicator
const lv_color_t PALETTE_DARK_GREY = LV_COLOR_MAKE(24, 24, 24); // highlight button background
const lv_color_t PALETTE_RED = LV_COLOR_MAKE(130, 35, 53); // redline
const lv_color_t PALETTE_GREEN = LV_COLOR_MAKE(123, 207, 21); // buttons and text
const lv_color_t PALETTE_GREY = LV_COLOR_MAKE(120, 120, 120); // button background
const lv_color_t PALETTE_WHITE = LV_COLOR_MAKE(255, 255, 255);
// NFSU2 pickable colors
const lv_color_t PALETTE_NFS_WHITE = LV_COLOR_MAKE(255, 255, 255);
const lv_color_t PALETTE_NFS_BLUE = LV_COLOR_MAKE(52, 154, 227);
const lv_color_t PALETTE_NFS_CYAN = LV_COLOR_MAKE(34, 199, 239);
const lv_color_t PALETTE_NFS_GREEN = LV_COLOR_MAKE(93, 239, 39);
const lv_color_t PALETTE_NFS_CITRUS = LV_COLOR_MAKE(221, 221, 37);
const lv_color_t PALETTE_NFS_LIME = LV_COLOR_MAKE(148, 248, 38);
const lv_color_t PALETTE_NFS_ORANGE = LV_COLOR_MAKE(244, 153, 37);
const lv_color_t PALETTE_NFS_RED = LV_COLOR_MAKE(255, 42, 22);
const lv_color_t PALETTE_NFS_PURPLE = LV_COLOR_MAKE(136, 86, 255);
const lv_color_t PALETTE_NFS_GREY = LV_COLOR_MAKE(175, 181, 191);
const lv_color_t PALETTE_NFS_BLUE2 = LV_COLOR_MAKE(27, 173, 252);
const lv_color_t PALETTE_NFS_YELLOW = LV_COLOR_MAKE(229, 223, 33);
// get bearing angle between two coordinates
double angle_from_coordinate(double lat1, double long1, double lat2, double long2) {
double lat1_rad = DEG_TO_RAD(lat1);
double lat2_rad = DEG_TO_RAD(lat2);
double dlon_rad = DEG_TO_RAD(long2 - long1);
double y = sin(dlon_rad) * cos(lat2_rad);
double x = cos(lat1_rad)*sin(lat2_rad) - sin(lat1_rad)*cos(lat2_rad)*cos(dlon_rad);
double bearing_rad = atan2(y, x);
double bearing_deg = fmod(RAD_TO_DEG(bearing_rad) + 360.0, 360.0);
int bearing_lvgl = (int)round(bearing_deg * 10.0);
return bearing_lvgl;
}
// calculate distance between two coordinates in meters
double distance_between(double lat1, double lon1, double lat2, double lon2) {
const double R = 6371000.0; // Earth radius in meters
double dLat = DEG_TO_RAD(lat2 - lat1);
double dLon = DEG_TO_RAD(lon2 - lon1);
double a = sin(dLat/2) * sin(dLat/2) +
cos(DEG_TO_RAD(lat1)) * cos(DEG_TO_RAD(lat2)) *
sin(dLon/2) * sin(dLon/2);
double c = 2 * atan2(sqrt(a), sqrt(1-a));
return R * c;
}
// normalize angle to shortest rotation direction
float normalize_angle(float from, float to) {
float diff = to - from;
while (diff > 1800) diff -= 3600;
while (diff < -1800) diff += 3600;
return from + diff;
}
static void anim_set_r_cb(void * obj, int32_t v) {
lv_img_set_angle((lv_obj_t *)obj, v);
}
void update_values(void) {
if (location_initialized) {
GPSLocator::move_location((double)new_latitude, (double)new_longitude);
} else {
GPSLocator::show_initial_location((double)new_latitude, (double)new_longitude);
location_initialized = true;
}
// TODO - animations in / out and lost connection handling
lv_obj_set_style_opa(no_satellite_bg, LV_OPA_0, 0);
lv_obj_set_style_opa(map_container, LV_OPA_COVER, 0);
lv_obj_set_style_opa(car_icon_img, LV_OPA_COVER, 0);
double dist = distance_between(current_latitude, current_longitude, new_latitude, new_longitude);
if (dist > MIN_MOVE_DISTANCE) {
new_angle = angle_from_coordinate(current_latitude, current_longitude, new_latitude, new_longitude);
float anim_target_angle = normalize_angle(current_angle, new_angle);
lv_anim_t aa;
lv_anim_init(&aa);
lv_anim_set_var(&aa, car_icon_img);
lv_anim_set_time(&aa, STEP_ANIMATION_DURATION);
lv_anim_set_exec_cb(&aa, anim_set_r_cb);
lv_anim_set_values(&aa, current_angle, anim_target_angle);
lv_anim_start(&aa);
current_angle = anim_target_angle;
}
current_latitude = new_latitude;
current_longitude = new_longitude;
}
void make_screen(void) {
main_scr = lv_obj_create(NULL);
lv_obj_set_size(main_scr, 800, 800);
lv_obj_set_style_bg_color(main_scr, PALETTE_BLACK, 0);
lv_obj_set_style_pad_all(main_scr, 0, 0);
lv_obj_set_style_border_width(main_scr, 0, 0);
no_satellite_bg = lv_img_create(main_scr);
lv_image_set_src(no_satellite_bg, &no_satellite);
lv_obj_set_size(no_satellite_bg, 500, 500);
lv_obj_align(no_satellite_bg, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_opa(no_satellite_bg, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(no_satellite_bg, PALETTE_BLACK, 0);
lv_obj_set_style_pad_all(no_satellite_bg, 0, 0);
lv_obj_set_style_border_width(no_satellite_bg, 0, 0);
map_container = lv_obj_create(main_scr);
lv_obj_set_size(map_container, 500, 500);
lv_obj_align(map_container, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_opa(map_container, LV_OPA_0, 0);
lv_obj_set_style_bg_color(map_container, PALETTE_BLACK, 0);
lv_obj_set_style_pad_all(map_container, 0, 0);
lv_obj_set_style_border_width(map_container, 0, 0);
lv_obj_set_style_outline_color(map_container, PALETTE_GREY, 0);
lv_obj_t *maps_bg_img = lv_img_create(main_scr);
lv_image_set_src(maps_bg_img, &maps_bg);
lv_obj_align(maps_bg_img, LV_ALIGN_CENTER, 0, 0);
}
void make_ui(void) {
car_icon_img = lv_img_create(main_scr);
lv_image_set_src(car_icon_img, &car_icon);
lv_obj_set_style_opa(car_icon_img, LV_OPA_0, 0);
lv_obj_align(car_icon_img, LV_ALIGN_CENTER, 0, 5);
lv_obj_set_style_transform_pivot_x(car_icon_img, 24, 0);
lv_obj_set_style_transform_pivot_y(car_icon_img, 21, 0);
north_pointer_img = lv_img_create(main_scr);
lv_image_set_src(north_pointer_img, &north_pointer);
lv_obj_align(north_pointer_img, LV_ALIGN_CENTER, 0, -280);
lv_obj_set_style_transform_pivot_x(north_pointer_img, 43, 0);
lv_obj_set_style_transform_pivot_y(north_pointer_img, (280 + 30), 0);
}
void update_location(uint8_t* data) {
new_latitude = 0.0f;
new_longitude = 0.0f;
memcpy(&new_latitude, data, sizeof(float));
memcpy(&new_longitude, data + 4, sizeof(float));
printf("Received GPS Data - Latitude: %f, Longitude: %f\n", new_latitude, new_longitude);
}
// NOTE
// My personal system for data passing uses a CANBus between devices
// The GPS module sends messages with ID 0x430 containing latitude and longitude as floats
void custom_can_message_handler(twai_message_t *message) {
printf("Received CAN message with ID: 0x%03X\n", message->identifier);
switch (message->identifier) {
case 0x430:
update_location(message->data);
data_ready = true;
break;
default:
break;
}
receiving_data = true;
}
void mount_sd(void) {
esp_err_t err = bsp_sdcard_mount();
if (err != ESP_OK) {
printf("Failed to mount SD card, error: %s\n", esp_err_to_name(err));
}
}
// decouple movement updates from CAN receive task
void lvgl_timer(lv_timer_t * timer) {
if (data_ready) {
data_ready = false;
update_values();
}
}
extern "C" void app_main(void) {
mount_sd();
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 = true,
.sw_rotate = false,
}
};
bsp_display_start_with_config(&cfg);
bsp_display_backlight_on();
bsp_display_brightness_set(100);
can_message_handler = custom_can_message_handler;
canbus_init();
start_can_tasks();
bsp_display_lock(0);
make_screen();
if (!GPSLocator::init(map_container)) {
printf("Failed to initialize map\n");
return;
}
make_ui();
lv_screen_load(main_scr);
bsp_display_unlock();
lv_timer_t * timer = lv_timer_create(lvgl_timer, 10, NULL);
(void)timer;
}