How to read structure field values from writecallback in open62541 - c

Currently, I'm working on read and write callback in open62541, I created a structure variable XV type, It's fields are X and V in XML file
X is a type of double and V is a type of float
if I write the value using the client tool it's entering into the write callback function and I'm able to print the X value alone using the print cout<<"Field value is "<< *(double*)data->value.data<<endl;, but I'm unable to fetch the second value that is V,
I took the below code as a reference, in that in the afterWriteTime callback function I tried
cout<<"Field value is "<< *(double*)data->value.data<<endl;this print statemet
it's printing only the X's value alone, how can I get two values from the callback function
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
static void
updateCurrentTime(UA_Server *server) {
UA_DateTime now = UA_DateTime_now();
UA_Variant value;
UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_Server_writeValue(server, currentNodeId, value);
}
static void
addCurrentTimeVariable(UA_Server *server) {
UA_DateTime now = 0;
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - value callback");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-value-callback");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr, NULL, NULL);
updateCurrentTime(server);
}
/**
* Variable Value Callback
* ^^^^^^^^^^^^^^^^^^^^^^^
*
* When a value changes continuously, such as the system time, updating the
* value in a tight loop would take up a lot of resources. Value callbacks allow
* to synchronize a variable value with an external representation. They attach
* callbacks to the variable that are executed before every read and after every
* write operation. */
static void
beforeReadTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeid, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
updateCurrentTime(server);
}
static void
afterWriteTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"The variable was updated");
cout<<"Field value is "<< *(double*)data->value.data<<endl;
}
static void
addValueCallbackToCurrentTimeVariable(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
UA_ValueCallback callback ;
callback.onRead = beforeReadTime;
callback.onWrite = afterWriteTime;
UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
}
/**
* Variable Data Sources
* ^^^^^^^^^^^^^^^^^^^^^
*
* With value callbacks, the value is still stored in the variable node.
* So-called data sources go one step further. The server redirects every read
* and write request to a callback function. Upon reading, the callback provides
* a copy of the current value. Internally, the data source needs to implement
* its own memory management. */
static UA_StatusCode
readCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
UA_DataValue *dataValue) {
UA_DateTime now = UA_DateTime_now();
UA_Variant_setScalarCopy(&dataValue->value, &now,
&UA_TYPES[UA_TYPES_DATETIME]);
dataValue->hasValue = true;
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
writeCurrentTime(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Changing the system time is not implemented");
return UA_STATUSCODE_BADINTERNALERROR;
}
static void
addCurrentTimeDataSourceVariable(UA_Server *server) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_DataSource timeDataSource;
timeDataSource.read = readCurrentTime;
timeDataSource.write = writeCurrentTime;
UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
parentReferenceNodeId, currentName,
variableTypeNodeId, attr,
timeDataSource, NULL, NULL);
}
static UA_DataValue *externalValue;
static void
addCurrentTimeExternalDataSource(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-external-source");
UA_ValueBackend valueBackend;
valueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL;
valueBackend.backend.external.value = &externalValue;
UA_Server_setVariableNode_valueBackend(server, currentNodeId, valueBackend);
}
/** It follows the main server code, making use of the above definitions. */
static volatile UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
addCurrentTimeVariable(server);
addValueCallbackToCurrentTimeVariable(server);
addCurrentTimeDataSourceVariable(server);
addCurrentTimeExternalDataSource(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
Footer
© 2022 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Docs
Contact GitHub
Pricing
API
Training
Blog
About
open62541/tutorial_server_datasource.c at master · open62541/open62541 · GitHub

Related

How to trigger creation of savefile

I'm trying to create a libretro core. It will be a standalone game, so I'm setting RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME to true. The documentation suggests that retro_get_memory_* can be used to have data saved without needing to explicitly query RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
The save directory should be used to
store SRAM, memory cards, high scores, etc, if the libretro core
cannot use the regular memory interface (retro_get_memory_data()).
How should the core trigger the saving of data using this interface? Or am I misunderstanding the documentation?
I would expect the frontend to call retro_get_memory_{data,size}, read from the exposed buffer when stopping the core, persist the data to disk, and write it back to the exposed buffer the next time the core starts. Instead I observe:
If I don't provide a content file, the frontend never calls retro_get_memory_{data,size}.
If I provide a content file (which is unused), the frontend calls retro_get_memory_{data,size} after retro_load_game but doesn't write to disk.
Note that this question is about save files (automatically persisted data, usually capturing the player's progress), not save states (snapshots of the game state triggered by the user) which are implemented by the *serialize* methods.
Here is a simple example to reproduce the issue (based on this sample):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libretro.h"
static unsigned char c = 0;
void* retro_get_memory_data(unsigned id) {
fprintf(stderr, "retro_get_memory_data(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? &c : NULL;
}
size_t retro_get_memory_size(unsigned id) {
fprintf(stderr, "retro_get_memory_size(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? 1 : 0;
}
#define WIDTH 320
#define HEIGHT 240
static uint32_t* frame_buf;
void retro_init(void) { frame_buf = calloc(WIDTH * HEIGHT, sizeof(uint32_t)); }
void retro_deinit(void) {
free(frame_buf);
frame_buf = NULL;
}
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
void retro_get_system_info(struct retro_system_info* info) {
memset(info, 0, sizeof(*info));
info->library_name = "SaveTest";
info->library_version = "v1";
info->need_fullpath = false;
info->valid_extensions = NULL; // Anything is fine, we don't care.
}
static retro_video_refresh_t video_cb;
static retro_environment_t environ_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
void retro_set_environment(retro_environment_t cb) {
environ_cb = cb;
bool no_content = true;
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);
}
void retro_get_system_av_info(struct retro_system_av_info* info) {
float aspect = (float)WIDTH / HEIGHT;
info->timing = (struct retro_system_timing){
.fps = 60.0,
.sample_rate = 0.0,
};
info->geometry = (struct retro_game_geometry){
.base_width = WIDTH,
.base_height = HEIGHT,
.max_width = WIDTH,
.max_height = HEIGHT,
.aspect_ratio = aspect,
};
}
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
bool retro_load_game(const struct retro_game_info* info) {
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
fprintf(stderr, "XRGB8888 is not supported.\n");
return false;
}
(void)info;
return true;
}
bool button(unsigned id) {
return input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, id);
}
void retro_run(void) {
input_poll_cb();
if (button(RETRO_DEVICE_ID_JOYPAD_LEFT) && c > 0) --c;
if (button(RETRO_DEVICE_ID_JOYPAD_RIGHT) && c < 255) ++c;
uint32_t color = (255 - c) | (c << 8);
uint32_t* buf = frame_buf;
for (unsigned i = WIDTH * HEIGHT; i > 0; --i) {
*buf = color;
++buf;
}
video_cb(frame_buf, WIDTH, HEIGHT, WIDTH * sizeof(uint32_t));
}
void retro_unload_game(void) {}
size_t retro_serialize_size(void) { return 1; }
bool retro_serialize(void* data, size_t size) {
fprintf(stderr, "serialize(%p, %lu) <= %u\n", data, size, c);
*(char*)data = c;
return true;
}
bool retro_unserialize(const void* data, size_t size) {
c = *(char*)data;
fprintf(stderr, "unserialize(%p, %lu) => %u\n", data, size, c);
return true;
}
void retro_set_controller_port_device(unsigned port, unsigned device) {}
void retro_set_audio_sample(retro_audio_sample_t cb) {}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {}
void retro_reset(void) {}
bool retro_load_game_special(unsigned type, const struct retro_game_info* info,
size_t num) {
return false;
}
void retro_cheat_reset(void) {}
void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
It is undocumented but intended that auto save does not trigger for a core without content.
Moreover auto save was not triggering for cores which had content but could support no content. This was unintended and recently fixed.
Reference: https://github.com/libretro/RetroArch/issues/9300

How to get pointer address in function that called through this pointer

I read this article but it doesn't answer my question.
file: hero.h
typedef struct {
int id;
void (*setId)();
int (*getId)();
} Hero, *HeroPtr;
file: hero.c
#include "hero.h"
static void setId(int id);
Hero obj = {
.setId = setId,
.getId = getId,
};
void setId(int id) {
HeroPtr hero_obj = 0x0; //TODO how get address: hero_obj1 (1 > ) OR
// hero_obj2 (2 > )
hero_obj->id = id;
}
void getId() {
HeroPtr hero_obj = 0x0; //TODO how get address: hero_obj1 (1 > ) OR
// hero_obj2 (2 > )
return hero_obj->id;
}
file: main.c
#include "hero.h"
int main() {
Hero hero_obj1, hero_obj2;
//1 >
hero_obj1->setId(1);
//2 >
hero_obj2->setId(2);
return 0;
}
You could do the equivalent of what C++ does behind the scenes.
file: hero.h
typedef struct hero {
int id;
void (*setId)(struct hero*, int);
int (*getId)(struct hero*);
} Hero, *HeroPtr;
void constructHero(HeroPtr this);
file: hero.c
#include "hero.h"
static void setId(HeroPtr this, int id);
static int getId(HeroPtr this);
Hero initObj = {
.setId = &setId,
.getId = &getId,
};
void constructHero(HeroPtr this)
{
*this = initObj;
}
void setId(HeroPtr this, int id) {
HeroPtr hero_obj = this;
hero_obj->id = id;
}
int getId(HeroPtr this) {
HeroPtr hero_obj = this;
return hero_obj->id;
}
file: main.c
#include "hero.h"
#include "stdio.h"
int main() {
Hero hero1;
Hero hero2;
HeroPtr hero_obj1=&hero1;
HeroPtr hero_obj2=&hero2;
constructHero(hero_obj1);
constructHero(hero_obj2);
hero_obj1->setId(hero_obj1, 1);
hero_obj2->setId(hero_obj2, 2);
printf("hero_obj1 id = %d\n", hero_obj1->getId(hero_obj1));
printf("hero_obj2 id = %d\n", hero_obj2->getId(hero_obj2));
return 0;
}
It looks like you are trying to implement virtual functions in C, by using function pointers. In object-oriented programming languages like C++ or Java such functions or methods inside classes have an implicit this pointer as argument, which is hidden. That means that the int getId() function actually has the signature int getId(Hero* this) and the void setId(int id) function actually has the form void setId(Hero* this, int id). As I have already said, in object-oriented programming languages you don't see or add the this pointer and you also don't pass the argument when you invoke the function. The compiler does this for you. It always automatically passes the pointer to the instance as this pointer, on which the function was invoked. In C, however, these features don't exist. So you have to add the this argument and pass it when invoking the function by yourself.

How can I set a user custom pointer in libwebsockets callback?

How can I set a user custom pointer in libwebsockets callback?
I added a pointer variable into a lws_protocol.
When callback function is called, user pointer is always NULL.
I use libwebsockets v3.0.
static int interrupted, rx_seen, test;
int ws_callback(struct lws *ws, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
// user is NULL
return lws_callback_http_dummy(ws, reason, user, in, len);
}
int main() {
struct lws *ws;
struct lws_context_creation_info context_info;
struct lws_client_connect_info client_info;
struct lws_context *context;
struct lws_protocols protocols[] = {
{ "ws_callback", ws_callback, 0, 0, 0, POINTER_VARIABLE /* HERE */, 0 }
};
int n = 0;
// context creation info
memset(&context_info, 0, sizeof(context_info));
context_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
context_info.port = CONTEXT_PORT_NO_LISTEN;
context_info.protocols = protocols;
context = lws_create_context(&context_info);
if (!context) {
return;
}
memset(&client_info, 0, sizeof(client_info));
client_info.context = context;
client_info.port = 8080;
client_info.address = "192.168.1.1";
client_info.path = "/";
client_info.host = client_info.address;
client_info.origin = client_info.address;
client_info.protocol = protocols[0].name;
client_info.pwsi = &ws;
lws_client_connect_via_info(&client_info);
while (n >= 0 && ws && !interrupted) {
n = lws_service(context, 1000);
}
lws_context_destroy(context);
return 0;
}
You must specify the size of per session data structure, but not the pointer itself. Per session data will be different for each connection.
typedef struct per_session_data {
void *user_space;
} per_session_data;
int ws_callback(struct lws *ws, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
/* This will be different for every connected peer */
per_session_data *data = (per_session_data*)user;
}
struct lws_protocols protocols[] = {
{ "ws_callback", ws_callback, sizeof(per_session_data), 0 }
{ NULL, NULL, 0, 0 } /* terminator */
};
You can also set optional user pointer that will be associated with the context. lws_context_creation_info has user variable for userspace. You must set it before creating the context
typedef struct per_session_data {
void *user_space;
/*
Same other variables
*/
} per_session_data;
int ws_callback(struct lws *ws, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
/* This will be different for every connected peer */
per_session_data *data = (per_session_data*)user;
/* This will be same for every connected peer */
void *userdata = lws_context_user(lws_get_context(ws));
/* userdata is POINTER_VARIABLE specified before context creating */
switch (reason)
{
case LWS_CALLBACK_ESTABLISHED:
/* Initialize per session data here */
break;
case LWS_CALLBACK_CLOSED:
/* Destroy per session data here */;
break;
default:
break;
}
}
struct lws_protocols protocols[] = {
{ "ws_callback", ws_callback, sizeof(per_session_data), 0 }
{ NULL, NULL, 0, 0 } /* terminator */
};
int main()
{
/*
your code here
*/
// context creation info
memset(&context_info, 0, sizeof(context_info));
context_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
context_info.port = CONTEXT_PORT_NO_LISTEN;
context_info.protocols = protocols;
context_info.user = POINTER_VARIABLE; /* HERE */
context = lws_create_context(&context_info);
if (!context) {
return;
}
/*
your code here
*/
}

c programming default parameters: const struct vs init function

Suppose I have something like:
typedef struct
{
int parameter1;
int parameter2;
void (fp*)(void);
} STATE_T;
and I have various sets of default parameters to start the program(or this segment of the program) in different states.
STATE_T State;
void InitState1()
{
State.parameter1 = 123;
State.parameter2 = 321;
State.fp = Function1;
}
void InitState2()
{
State.parameter1 = 0;
State.parameter2 = 1;
State.fp = Function2;
}
or would it better to use const structs
const STATE_T STATE1 =
{
123,
321,
Function1
}
const STATE_T STATE2 =
{
0,
1,
Function2
}
I suppose in the 2nd case either a pointer can be used or a function to copy a selection of settings:
STATE_T * StatePtr;
StatePtr = &STATE1;
or
void InitState(STATE_T s)
{
State.parameter1 = s.parameter1;
State.parameter2 = s.parameter2;
State.fp = s.fp;
}
After typing out all the examples, it seems like, in the case I want to change all parameters at the same time, using a pointer to const structs would be more efficient, while an init functions would be better for only updating selected parameters that would be relevant. Are there any other advantages or differences to be aware of?
I typically declares some static versions statically ie
static State State1 = {
.paramater1 = 123,
.parameter2 = 321,
.fp = NULL,
};
static State State2 = {
.paramater1 = 999,
.parameter2 = 111,
.fp = NULL,
};
Then in an init function assign the statics to get the defaults...
static State * newState(int state) {
State *foo = calloc(1, sizeof(State));
assert(foo != NULL);
if(state == 1) {
*foo = State1;
foo->fp = function_fp1;
}
else {
*foo = State2;
foo->fp = function_fp2;
}
return foo;
}

Pebble stopwatch change in font size bug

I have been trying to get an app working on my pebble for a while now and I finally got it to work. It's a sports counter that keeps track of the score for both teams and also has a built in stop watch in the middle. Everything works as it should but if the stopwatch runs for 42 seconds then the font for the counters become very small. I have no idea whats going on.
When I was looking around someone said it may be a memory leak, but this was not similar to my problem.
#include <pebble.h>
#define COUNTER_FONT_49 RESOURCE_ID_MACHINE_GUN_49
#define STOPWATCH_FONT_24 RESOURCE_ID_SPORTS_WORLD_24
#define HOME_AWAY_FONT_18 FONT_KEY_GOTHIC_18_BOLD
//---Counter Constants---
#define COUNTER_START 0
#define COUNTER_MAX 9999
#define COUNTER_MIN -9999
#define MAX_DIGITS 4
//---Interface Variables---
Window* window;
static Layer* layer;
static GFont counterFont;
static GFont stopwatchFont;
static TextLayer* teamAScore_layer;
static TextLayer* teamBScore_layer;
static TextLayer* big_time_layer;
static TextLayer* seconds_time_layer;
static TextLayer* home_away_layer;
//---Counter Variables---
char teamA_counter_text[MAX_DIGITS + 2 /* sign & \0 */];
char teamB_counter_text[MAX_DIGITS + 2 /* sign & \0 */];
int teamACounter;
int teamBCounter;
int singleClickIncrement;
int longClickIncrement;
int doubleClickIncrement;
//The Time
static double elapsed_time = 0;
static bool started = false;
static AppTimer* update_timer = NULL;
static double start_time = 0;
static double pause_time = 0;
time_t time_seconds();
void stop_stopwatch();
void start_stopwatch();
void handle_timer(void* data);
void update_stopwatch();
double float_time_ms() {
time_t seconds;
uint16_t milliseconds;
time_ms(&seconds, &milliseconds);
return (double)seconds + ((double)milliseconds / 1000.0);
}
void stop_stopwatch() {
started = false;
pause_time = float_time_ms();
if(update_timer != NULL) {
app_timer_cancel(update_timer);
update_timer = NULL;
}
}
void start_stopwatch() {
started = true;
if(start_time == 0) {
start_time = float_time_ms();
} else if(pause_time != 0) {
double interval = float_time_ms() - pause_time;
start_time += interval;
}
update_timer = app_timer_register(100, handle_timer, NULL);
}
void update_stopwatch() {
static char big_time[] = "00:00";
static char deciseconds_time[] = ".0";
static char seconds_time[] = ":00";
// Now convert to hours/minutes/seconds.
int tenths = (int)(elapsed_time * 10) % 10;
int seconds = (int)elapsed_time % 60;
int minutes = (int)elapsed_time / 60 % 60;
int hours = (int)elapsed_time / 3600;
// We can't fit three digit hours, so stop timing here.
if(hours > 99) {
stop_stopwatch();
return;
}
if(hours < 1) {
snprintf(big_time, 6, "%02d:%02d", minutes, seconds);
snprintf(deciseconds_time, 3, ".%d", tenths);
} else {
snprintf(big_time, 6, "%02d:%02d", hours, minutes);
snprintf(seconds_time, 4, ":%02d", seconds);
}
// Now draw the strings.
text_layer_set_text(big_time_layer, big_time);
text_layer_set_text(seconds_time_layer, hours < 1 ? deciseconds_time : seconds_time);
}
void select_click_long_handler(ClickRecognizerRef recognizer, Window *window) { //pressed SELECT LONG
bool is_running = started;
stop_stopwatch();
start_time = 0;
elapsed_time = 0;
if(is_running) start_stopwatch();
update_stopwatch();
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed SELECT
if(started) {
stop_stopwatch();
} else {
start_stopwatch();
}
}
void handle_timer(void* data) {
if(started) {
double now = float_time_ms();
elapsed_time = now - start_time;
update_timer = app_timer_register(100, handle_timer, NULL);
}
update_stopwatch();
}
static int increment_value(int team_score, const int increment){
if(team_score + increment <= COUNTER_MAX && team_score + increment >= COUNTER_MIN)
team_score = team_score + increment;
return team_score;
}
static void up_multi_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP MULTI
teamACounter = increment_value(teamACounter, doubleClickIncrement);
layer_mark_dirty(layer);
}
static void up_click_long_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP LONG
teamACounter = increment_value(teamACounter, longClickIncrement);
layer_mark_dirty(layer);
}
static void up_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed UP
teamACounter = increment_value(teamACounter, singleClickIncrement);
layer_mark_dirty(layer);
}
static void down_multi_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN MULTI
teamBCounter = increment_value(teamBCounter, doubleClickIncrement);
layer_mark_dirty(layer);
}
static void down_click_long_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN LONG
teamBCounter = increment_value(teamBCounter, longClickIncrement);
layer_mark_dirty(layer);
}
static void down_click_handler(ClickRecognizerRef recognizer, void *context) { //pressed DOWN
teamBCounter = increment_value(teamBCounter, singleClickIncrement);
layer_mark_dirty(layer);
}
static void click_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_long_click_subscribe(BUTTON_ID_SELECT, 700, (ClickHandler) select_click_long_handler, NULL);
window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
window_long_click_subscribe(BUTTON_ID_UP, 700, (ClickHandler)up_click_long_handler, NULL);
window_multi_click_subscribe(BUTTON_ID_UP, 2, 10, 0, true, up_multi_click_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
window_long_click_subscribe(BUTTON_ID_DOWN, 700, (ClickHandler)down_click_long_handler, NULL);
window_multi_click_subscribe(BUTTON_ID_DOWN, 2, 10, 0, true, down_multi_click_handler);
}
static void update_layer(Layer *layer, GContext *ctx){
GRect bounds = layer_get_frame(layer);
counterFont = fonts_load_custom_font(resource_get_handle(COUNTER_FONT_49));
graphics_context_set_text_color(ctx, GColorBlack);
snprintf(teamA_counter_text, (MAX_DIGITS + 2)*sizeof(char), "%d", teamACounter);
graphics_draw_text(ctx,
teamA_counter_text,
counterFont,
GRect(0, 0, bounds.size.w - 10, 60),
GTextOverflowModeWordWrap,
GTextAlignmentCenter,
NULL);
snprintf(teamB_counter_text, (MAX_DIGITS + 2)*sizeof(char), "%d", teamBCounter);
graphics_draw_text(ctx,
teamB_counter_text,
counterFont,
GRect(0, 95, bounds.size.w - 10, 60),
GTextOverflowModeWordWrap,
GTextAlignmentCenter,
NULL);
}
static void window_load(Window *window) {
// Get the root layer
Layer *window_layer = window_get_root_layer(window);
// Get the bounds of the window for sizing the text layer
GRect bounds = layer_get_bounds(window_layer);
layer = layer_create(bounds);
layer_set_update_proc(layer, update_layer);
layer_add_child(window_layer, layer);
}
static void window_unload(Window *window) {
// Destroy TextLayer
text_layer_destroy(teamAScore_layer);
text_layer_destroy(teamBScore_layer);
text_layer_destroy(big_time_layer);
text_layer_destroy(seconds_time_layer);
}
void handle_init(void) {
window = window_create();
window_set_click_config_provider(window, click_config_provider);
window_set_window_handlers(window, (WindowHandlers) {
.load = window_load,
.unload = window_unload,
});
//Set the counters start
teamACounter = COUNTER_START;
teamBCounter = COUNTER_START;
//---------------------------------------------TEMPERARY INCREMENT
singleClickIncrement = 1;
doubleClickIncrement = 2;
longClickIncrement = 3;
Layer *root_layer = window_get_root_layer(window);
stopwatchFont = fonts_load_custom_font(resource_get_handle(STOPWATCH_FONT_24));
//-----Display Stop Watch-----
big_time_layer = text_layer_create(GRect(0, 65, 86, 35));
text_layer_set_text_alignment(big_time_layer, GTextAlignmentRight);
text_layer_set_background_color(big_time_layer, GColorClear);
text_layer_set_text(big_time_layer, "00:00");
text_layer_set_font(big_time_layer, stopwatchFont);
layer_add_child(root_layer, (Layer*)big_time_layer);
seconds_time_layer = text_layer_create(GRect(86, 65, 49, 35));
text_layer_set_text(seconds_time_layer, ".0");
text_layer_set_background_color(seconds_time_layer, GColorClear);
text_layer_set_font(seconds_time_layer, stopwatchFont);
layer_add_child(root_layer, (Layer*)seconds_time_layer);
//-----Display Home and Away-----
home_away_layer = text_layer_create(GRect(124, 0, 20, 152));
text_layer_set_text(home_away_layer, "\n H\n\n\n\n\n A");
text_layer_set_background_color(home_away_layer, GColorBlack);
text_layer_set_text_color(home_away_layer, GColorWhite);
text_layer_set_font(home_away_layer, fonts_get_system_font(HOME_AWAY_FONT_18));
layer_add_child(root_layer, (Layer*)home_away_layer);
const bool animated = true;
window_stack_push(window, animated);
}
void handle_deinit(void) {
window_destroy(window);
}
int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}
You're loading the custom font into memory each time the update_layer function is called. Eventually the app runs out of memory and can't load the font any more, which is why it uses the fallback (really small) font.
You should just load the custom font once, in your init function.

Resources