Compare commits

...

10 Commits

Author SHA1 Message Date
Paul Dino Jones 1bdb72a0ac Adding SDL dependency and support for embbeded framebuffer lcd screen graphics through GUIslice 2023-04-19 08:46:28 +00:00
Paul Dino Jones 1440f28d6e Using b4mad json format 2023-04-17 15:40:36 +00:00
Paul Dino Jones 36d690f1e4 Adding threads for UIs and monitors 2023-04-17 07:04:11 +00:00
Paul Dino Jones e2a0979b95 Updated to latest simapi for more mappings 2023-04-03 06:55:43 +00:00
Paul Dino Jones db7548cb1a Fixed mqtt server string. 2023-02-22 13:50:04 +00:00
Paul Dino Jones ad132c0574 Updated to latest simapi that actually compiles 2023-02-17 11:35:36 +00:00
Paul Dino Jones 9908326ac5 Improved code to send session tag with timestamp to influxdb 2023-02-17 11:26:10 +00:00
Paul Dino Jones c16a758e94 Add datestring as session tag to influxdb data. This still does not account for if a new session is started without restarting Gilles. 2023-02-14 20:53:36 +00:00
Paul Dino Jones f8670ad393 Use ncursesw explicitly 2023-02-13 22:46:38 +00:00
Paul Dino Jones 170ad25020 Added link to slog library 2023-01-31 11:53:33 +00:00
12 changed files with 518 additions and 60 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "src/gilles/simulatorapi/simapi"]
path = src/gilles/simulatorapi/simapi
url = https://github.com/Spacefreak18/simapi
[submodule "src/gilles/GUIslice"]
path = src/gilles/GUIslice
url = https://github.com/spacefreak18/GUIslice.git

View File

@ -24,7 +24,7 @@ add_subdirectory(src/gilles/helper)
add_subdirectory(src/gilles/slog)
add_executable(gilles src/gilles/gilles.c)
target_link_libraries(gilles m ncurses argtable2 config gameloop helper slog simulatorapi eclipse-paho-mqtt-c::paho-mqtt3c)
target_link_libraries(gilles m ncursesw argtable2 config gameloop helper slog simulatorapi eclipse-paho-mqtt-c::paho-mqtt3c pthread json-c SDL SDL_ttf SDL_gfx)
# used for enabling additional compiler options if supported
include(CheckCXXCompilerFlag)

View File

@ -11,7 +11,7 @@ ncurses based telemetry monitor for racing sims
- argtable2
- libconfig
- ncurses
- slog (static)
- [slog](https://github.com/kala13x/slog) (static)
- [simshmbridge](https://github.com/spacefreak18/simshmbridge)
- [simapi](https://github.com/spacefreak18/simapi)

1
src/gilles/GUIslice Submodule

@ -0,0 +1 @@
Subproject commit 8899915eff8f390b62af7f1f9d1b26ca820c06ee

View File

@ -1,10 +1,16 @@
set(gameloop_source_files
gameloop.c
gameloop.h
wheeldash.c
wheeldash.h
../GUIslice/src/GUIslice.c
../GUIslice/src/GUIslice.h
../GUIslice/src/GUIslice_config.h
../GUIslice/src/GUIslice_drv_sdl.h
../GUIslice/src/GUIslice_drv_sdl.c
)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
include_directories("." ${LIBXML_INCLUDE_DIR})
add_library(gameloop STATIC ${gameloop_source_files})

View File

@ -3,6 +3,8 @@
#include <string.h>
#include <ncurses.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <MQTTClient.h>
#include "gameloop.h"
@ -12,6 +14,8 @@
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include "../slog/slog.h"
#include <json-c/json.h>
#define DEFAULT_UPDATE_RATE 100
#define ADDRESS "tcp://localhost:1883"
@ -21,6 +25,8 @@
#define QOS 0
#define TIMEOUT 10000L
char datestring[30];
WINDOW* win1;
WINDOW* win2;
WINDOW* win3;
@ -30,6 +36,8 @@ int winx, winy;
int win23y, win23x;
void handle_winch(int sig)
{
endwin();
@ -91,73 +99,54 @@ int curses_init()
box(win4, 0, 0);
}
int looper(Simulator simulator, Parameters* p)
char * removeSpacesFromStr(char *string)
{
int non_space_count = 0;
for (int i = 0; string[i] != '\0'; i++)
{
if (string[i] != ' ')
{
string[non_space_count] = string[i];
non_space_count++;
}
}
string[non_space_count] = '\0';
return string;
}
void update_date()
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
sprintf(datestring, "%.24s", asctime (timeinfo));
}
void* looper(void* thargs)
{
Parameters* p = (Parameters*) thargs;
SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = malloc(sizeof(SimMap));
int error = siminit(simdata, simmap, simulator);
int error = siminit(simdata, simmap, 1);
if (error != GILLES_ERROR_NONE)
{
slogf("Fatal error getting simulator data");
return error;
//return error;
}
curses_init();
timeout(DEFAULT_UPDATE_RATE);
bool mqtt = p->mqtt;
bool mqtt_connected = false;
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc;
// Create a new MQTT client
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
// Connect to the MQTT server
if (mqtt == true)
{
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
MQTTClient_disconnect(client, 10000);
sloge("Failed to connect, return code %d", rc);
//exit(-1);
}
mqtt_connected = true;
}
int go = true;
char lastsimstatus = false;
while (go == true)
{
simdatamap(simdata, simmap, simulator);
if (mqtt_connected == true)
{
char payloads[6][20];
sprintf(payloads[0], "gas, lap=%i, %04f", simdata->lap, simdata->gas);
sprintf(payloads[1], "brake, lap=%i, %04f", simdata->lap, simdata->brake);
sprintf(payloads[2], "steer, lap=%i, %04f", simdata->lap, simdata->brake);
sprintf(payloads[3], "gear, lap=%i, %04i", simdata->lap, simdata->gear);
sprintf(payloads[4], "speed, lap=%i, %04i", simdata->lap, simdata->velocity);
for (int k =0; k < 6; k++)
{
pubmsg.payload = payloads[k];
pubmsg.payloadlen = strlen(payloads[k]);
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
}
}
simdatamap(simdata, simmap, 1);
wclear(win1);
wclear(win2);
@ -564,6 +553,151 @@ int looper(Simulator simulator, Parameters* p)
delwin(win1);
endwin();
free(simdata);
free(simmap);
//return 0;
}
void* b4madmqtt(void* thargs)
{
Parameters* p = (Parameters*) thargs;
SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = malloc(sizeof(SimMap));
long unix_time_start;
char time_buff[11];
int error = siminit(simdata, simmap, 1);
if (error != GILLES_ERROR_NONE)
{
slogf("Fatal error getting simulator data");
//return error;
}
bool mqtt = p->mqtt;
bool mqtt_connected = false;
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc;
// Create a new MQTT client
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
// Connect to the MQTT server
if (mqtt == true)
{
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
MQTTClient_disconnect(client, 10000);
sloge("Failed to connect, return code %d", rc);
return NULL;
//exit(-1);
}
mqtt_connected = true;
}
int go = false;
if (mqtt_connected == true)
{
go = true;
}
char lastsimstatus = false;
while (go == true && p->program_state == 1)
{
char simstatus = (simdata->simstatus > 0) ? true : false;
if (simdata->simstatus > 0 && simstatus != lastsimstatus)
{
//update_date();
unix_time_start = (unsigned long) time(NULL);
sprintf(time_buff, "%lu", unix_time_start);
//sprintf(time_buff, "%lu", (unsigned long)time(NULL));
//newdatestring = removeSpacesFromStr(datestring);
}
lastsimstatus = simstatus;
simdatamap(simdata, simmap, 1);
if (mqtt_connected == true && simdata->simstatus > 0)
{
json_object *root = json_object_new_object();
//if (!root)
// return;
json_object *child = json_object_new_object();
struct timeval tv;
gettimeofday(&tv, NULL);
unsigned long long millisecondsSinceEpoch = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;
json_object_object_add(root, "time", json_object_new_int64(millisecondsSinceEpoch)); //unix time milliseconds
const char* topic_root = "racing/gilles/TuxRacerX";
//const char* game_name = "Assetto Corsa (64 bit)";
const char* game_name = "assetto_64_bit";
const char* session_type = "Practice";
json_object_object_add(child, "CarModel", json_object_new_string(simdata->car));
json_object_object_add(child, "GameName", json_object_new_string(game_name));
json_object_object_add(child, "SessionId", json_object_new_int(unix_time_start));
json_object_object_add(child, "SessionTypeName", json_object_new_string("Practice"));
json_object_object_add(child, "TrackCode", json_object_new_string(simdata->track));
json_object_object_add(child, "Clutch", json_object_new_double(simdata->clutch));
json_object_object_add(child, "Brake", json_object_new_double(simdata->brake));
json_object_object_add(child, "Throtte", json_object_new_double(simdata->gas));
json_object_object_add(child, "HandBrake", json_object_new_double(simdata->handbrake));
json_object_object_add(child, "SteeringAngle", json_object_new_double(simdata->steer));
json_object_object_add(child, "Rpms", json_object_new_int(simdata->rpms));
json_object_object_add(child, "Gear", json_object_new_int(simdata->gear));
json_object_object_add(child, "SpeedMs", json_object_new_double(simdata->velocity * 0.2777778));
json_object_object_add(child, "DistanceRoundTrack", json_object_new_double(simdata->trackdistancearound));
json_object_object_add(child, "WorldPosition_x", json_object_new_double(simdata->worldposx));
json_object_object_add(child, "WorldPosition_y", json_object_new_double(simdata->worldposy));
json_object_object_add(child, "WorldPosition_z", json_object_new_double(simdata->worldposz));
json_object_object_add(child, "CurrentLap", json_object_new_int(simdata->playerlaps));
json_object_object_add(child, "CurrentLapTime", json_object_new_int(simdata->currentlapinseconds));
json_object_object_add(child, "LapTimePrevious", json_object_new_int(simdata->lastlapinseconds));
json_object_object_add(child, "CurrentLapIsValid", json_object_new_int(simdata->lapisvalid));
json_object_object_add(child, "PreviousLapWasValid", json_object_new_int(1));
json_object_object_add(root, "telemetry", child);
slogi(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
// TODO: generate this topic string only once
char* topic = ( char* ) malloc(1 + strlen(topic_root) + strlen("/") + strlen(game_name) + strlen("/") + strlen(session_type)
+ strlen("/") + strlen(simdata->car) + strlen("/") + strlen(simdata->track) + strlen("/") + 11);
strcpy(topic, topic_root);
strcat(topic, "/");
strcat(topic, time_buff);
strcat(topic, "/");
strcat(topic, game_name);
strcat(topic, "/");
strcat(topic, simdata->track);
strcat(topic, "/");
strcat(topic, simdata->car);
strcat(topic, "/");
strcat(topic, session_type);
char* payload1;
sprintf(payload1, json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
pubmsg.payload = payload1;
//pubmsg.payload = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY);
//pubmsg.payloadlen = strlen(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
pubmsg.payloadlen = strlen(payload1);
pubmsg.qos= QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, topic, &pubmsg, &token);
}
}
if (mqtt_connected == true)
{
MQTTClient_disconnect(client, 10000);
@ -572,6 +706,6 @@ int looper(Simulator simulator, Parameters* p)
free(simdata);
free(simmap);
return 0;
return NULL;
//return 0;
}

View File

@ -1,4 +1,5 @@
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
int looper (Simulator simulator, Parameters* p);
void* looper(void* params);
void* b4madmqtt(void* params);

View File

@ -0,0 +1,287 @@
#include <unistd.h>
#include "SDL/SDL.h"
#include "SDL/SDL_getenv.h"
// Define the primary surface for display
SDL_Surface* scrMain = NULL;
//<File !Start!>
// FILE: [FerrariF1Wheel.c]
// Created by GUIslice Builder version: [0.17.b20]
//
// GUIslice Builder Generated File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<File !End!>
//
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
#include "../GUIslice/src/GUIslice.h"
#include "../GUIslice/src/GUIslice_drv.h"
// Include any extended elements
//<Includes !Start!>
//<Includes !End!>
// ------------------------------------------------
// Defines for resources
// ------------------------------------------------
#define MAX_PATH 255
//<PathStorage !Start!>
//<PathStorage !End!>
// ------------------------------------------------
// Headers and Defines for fonts
// ------------------------------------------------
//<Fonts !Start!>
#define FONT_FREE_MONOB96 "/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf"
//<Fonts !End!>
// ------------------------------------------------
// Defines for resources
// ------------------------------------------------
//<Resources !Start!>
//<Resources !End!>
// ------------------------------------------------
// Enumerations for pages, elements, fonts, images
// ------------------------------------------------
//<Enum !Start!>
enum {E_PG_MAIN};
enum {E_GEAR_TEXT};
// Must use separate enum for fonts with MAX_FONT at end to use gslc_FontSet.
enum {E_FREEMONOBOLD96PT,MAX_FONT};
//<Enum !End!>
// ------------------------------------------------
// Instantiate the GUI
// ------------------------------------------------
// ------------------------------------------------
// Define the maximum number of elements and pages
// ------------------------------------------------
//<ElementDefines !Start!>
#define MAX_PAGE 1
#define MAX_ELEM_PG_MAIN 1 // # Elems total on page
#define MAX_ELEM_PG_MAIN_RAM MAX_ELEM_PG_MAIN // # Elems in RAM
//<ElementDefines !End!>
// ------------------------------------------------
// Create element storage
// ------------------------------------------------
// GUI Elements
gslc_tsGui m_gui;
gslc_tsDriver m_drv;
gslc_tsFont m_asFont[MAX_FONT];
gslc_tsPage m_asPage[MAX_PAGE];
//<GUI_Extra_Elements !Start!>
gslc_tsElem m_asPage1Elem[MAX_ELEM_PG_MAIN_RAM];
gslc_tsElemRef m_asPage1ElemRef[MAX_ELEM_PG_MAIN];
gslc_tsElemRef* pElemRef = NULL;
#define MAX_STR 100
//<GUI_Extra_Elements !End!>
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
bool m_bQuit = false;
// Save some element references for direct access
//<Save_References !Start!>
//<Save_References !End!>
// Configure environment variables suitable for display
// - These may need modification to match your system
// environment and display type
// - Defaults for GSLC_DEV_FB and GSLC_DEV_TOUCH are in GUIslice_config.h
// - Note that the environment variable settings can
// also be set directly within the shell via export
// (or init script).
// - eg. export TSLIB_FBDEVICE=/dev/fb1
void UserInitEnv()
{
#if defined(DRV_DISP_SDL1) || defined(DRV_DISP_SDL2)
setenv((char*)"FRAMEBUFFER",GSLC_DEV_FB,1);
setenv((char*)"SDL_FBDEV",GSLC_DEV_FB,1);
setenv((char*)"SDL_VIDEODRIVER",GSLC_DEV_VID_DRV,1);
#endif
#if defined(DRV_TOUCH_TSLIB)
setenv((char*)"TSLIB_FBDEVICE",GSLC_DEV_FB,1);
setenv((char*)"TSLIB_TSDEVICE",GSLC_DEV_TOUCH,1);
setenv((char*)"TSLIB_CALIBFILE",(char*)"/etc/pointercal",1);
setenv((char*)"TSLIB_CONFFILE",(char*)"/etc/ts.conf",1);
setenv((char*)"TSLIB_PLUGINDIR",(char*)"/usr/local/lib/ts",1);
#endif
}
// Define debug message function
static int16_t DebugOut(char ch) { fputc(ch,stderr); return 0; }
// ------------------------------------------------
// Callback Methods
// ------------------------------------------------
//<Button Callback !Start!>
//<Button Callback !End!>
//<Checkbox Callback !Start!>
//<Checkbox Callback !End!>
//<Keypad Callback !Start!>
//<Keypad Callback !End!>
//<Spinner Callback !Start!>
//<Spinner Callback !End!>
//<Listbox Callback !Start!>
//<Listbox Callback !End!>
//<Draw Callback !Start!>
//<Draw Callback !End!>
//<Slider Callback !Start!>
//<Slider Callback !End!>
//<Tick Callback !Start!>
//<Tick Callback !End!>
// ------------------------------------------------
// Create page elements
// - strPath: Path to executable passed in to locate resource files
// ------------------------------------------------
bool InitGUI(char *strPath)
{
//gslc_tsElemRef* pElemRef = NULL;
//<InitGUI !Start!>
gslc_PageAdd(&m_gui,E_PG_MAIN,m_asPage1Elem,MAX_ELEM_PG_MAIN_RAM,m_asPage1ElemRef,MAX_ELEM_PG_MAIN);
// NOTE: The current page defaults to the first page added. Here we explicitly
// ensure that the main page is the correct page no matter the add order.
gslc_SetPageCur(&m_gui,E_PG_MAIN);
// Set Background to a flat color
gslc_SetBkgndColor(&m_gui,GSLC_COL_BLACK);
// -----------------------------------
// PAGE: E_PG_MAIN
// Create E_GEAR_TEXT text label
pElemRef = gslc_ElemCreateTxt(&m_gui,E_GEAR_TEXT,E_PG_MAIN,(gslc_tsRect){140,90,33,49},
(char*)"N",0,E_FREEMONOBOLD96PT);
gslc_ElemSetFillEn(&m_gui,pElemRef,false);
//<InitGUI !End!>
return true;
}
void* wheel()
{
// --------------------------------------
// Initialization
// --------------------------------------
// Update the environment variables for SDL to
// work correctly with the external display on
// LINUX frame buffer 1 (fb1).
putenv((char*) "FRAMEBUFFER=/dev/fb1");
putenv((char*) "SDL_FBDEV=/dev/fb1");
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "ERROR in SDL_Init(): %s\n", SDL_GetError());
return 0;
}
atexit(SDL_Quit);
// Fetch the best video mode
// - Note that the Raspberry Pi generally defaults
// to a 16bits/pixel framebuffer
const SDL_VideoInfo* vInfo = SDL_GetVideoInfo();
if (!vInfo) {
fprintf(stderr, "ERROR in SDL_GetVideoInfo(): %s\n", SDL_GetError());
return 0;
}
int nResX = vInfo->current_w;
int nResY = vInfo->current_h;
int nDepth = vInfo->vfmt->BitsPerPixel;
// Configure the video mode
// - SDL_SWSURFACE appears to be most robust mode
int nFlags = SDL_SWSURFACE | SDL_FULLSCREEN;
scrMain = SDL_SetVideoMode(nResX, nResY, nDepth, nFlags);
if (scrMain == 0) {
fprintf(stderr, "ERROR in SDL_SetVideoMode(): %s\n", SDL_GetError());
return 0;
}
bool bOk = true;
char acTxt[MAX_STR];
// ------------------------------------------------
// Initialize
// ------------------------------------------------
gslc_InitDebug(&DebugOut);
UserInitEnv();
if (!gslc_Init(&m_gui,&m_drv,m_asPage,MAX_PAGE,m_asFont,MAX_FONT)) { exit(1); }
// ------------------------------------------------
// Load Fonts
// ------------------------------------------------
//<Load_Fonts !Start!>
//
bOk = gslc_FontSet(&m_gui,E_FREEMONOBOLD96PT,GSLC_FONTREF_FNAME,FONT_FREE_MONOB96,96);
if (!bOk) { fprintf(stderr,"ERROR: FontAdd failed: %s\n",FONT_FREE_MONOB96); exit(1); }
//<Load_Fonts !End!>
// ------------------------------------------------
// Create graphic elements
// ------------------------------------------------
InitGUI("."); // Pass executable path to find resource files
// ------------------------------------------------
// Start up display on main page
// ------------------------------------------------
//<Startup !Start!>
//<Startup !End!>
// ------------------------------------------------
// Main event loop
// ------------------------------------------------
m_bQuit = false;
while (!m_bQuit) {
// ----------------------------------------------
// Update GUI Elements
// ----------------------------------------------
//TODO - Add update code for any text, gauges, or sliders
// ----------------------------------------------
// Periodically call GUIslice update function
// ----------------------------------------------
gslc_Update(&m_gui);
sleep(2);
gslc_ElemSetTxtStr(&m_gui, pElemRef, "2");
gslc_Update(&m_gui);
sleep(2);
m_bQuit = true;
} // bQuit
// ------------------------------------------------
// Close down display
// ------------------------------------------------
gslc_Quit(&m_gui);
return 0;
}

View File

@ -0,0 +1 @@
void* wheel(void* params);

View File

@ -4,6 +4,7 @@
#include <string.h>
#include <unistd.h>
#include <libconfig.h>
#include <pthread.h>
#include "gameloop/gameloop.h"
#include "helper/parameters.h"
@ -46,6 +47,7 @@ int main(int argc, char** argv)
goto cleanup_final;
}
gs->program_action = p->program_action;
p->program_state = 1;
char* home_dir_str = gethome();
create_user_dir("/.config/");
@ -80,7 +82,29 @@ int main(int argc, char** argv)
slog_disable(SLOG_DEBUG);
}
looper(1, p);
pthread_t ui_thread;
pthread_t mqtt_thread;
if (pthread_create(&ui_thread, NULL, &looper, p) != 0)
{
printf("Uh-oh!\n");
return -1;
}
if (p->mqtt == true)
{
if (pthread_create(&mqtt_thread, NULL, &b4madmqtt, p) != 0)
{
printf("Uh-oh!\n");
return -1;
}
}
pthread_join(ui_thread, NULL);
p->program_state = -1;
if (p->mqtt == true)
{
pthread_join(mqtt_thread, NULL);
}
free(config_file_str);
free(cache_dir_str);

View File

@ -6,6 +6,7 @@
typedef struct
{
int program_action;
int program_state;
const char* sim_string;
bool mqtt;
int verbosity_count;

@ -1 +1 @@
Subproject commit 85c063eac3c8a30f8806976b675d4d77aa8d5176
Subproject commit 618920440e0eb60f6209bdb4cb734b366c32780c