Why does my Pebble watchface not update the time every minute? - c

I'm learning watchface development. I have been following the Pebble guide closely, so 80% of my code is the same as their sample code. I'm probably missing something very small, but my face does not seem to be correctly subscribed to the time service.
What am I doing wrong?
In init(), I have:
tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);
tick_timer_service_subscribe(DAY_UNIT, tick_handler);
Here's tick_handler:
static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
update_time();
}
Here's update_time:
static void update_time() {
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
static char time_buffer[] = "00:00";
static char date_buffer[] = "00/00/00";
if (clock_is_24h_style() == true) {
strftime(time_buffer, sizeof(time_buffer), "%H:%M", tick_time);
} else {
strftime(time_buffer, sizeof(time_buffer), "%I:%M", tick_time);
}
text_layer_set_text(s_time_layer, time_buffer);
strftime(date_buffer, sizeof(date_buffer), "%D", tick_time);
text_layer_set_text(s_date_layer, date_buffer);
}
The face only updates the time when it first loads (by calling update_time).

TimeUnits is a bit mask. You set a mask and then call tick_timer_service_subscribe once. Your second call using DAY_UNITS is changing your subscription. To subscribe to both units, you bitwise-or your mask bits:
tick_timer_service_subscribe(MINUTE_UNIT | DAY_UNIT, tick_handler);
Notice how your tick handler has a TimeUnits argument. That argument tells you which unit triggered the handler. In your case, you always want to update the time and it appears DAY_UNIT is redundant. But you could do this:
static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
if( (units_changed & MINUTE_UNIT) != 0 ) {
/* Minutes changed */
}
if( (units_changed & DAY_UNIT) != 0 ) {
/* Days changed */
}
}

Related

Event-based task management using FreeRTOS

I'm trying to pick up C, using an esp32. While looking at exactly how FreeRTOS works, I found the following page regarding how to use the tasks, and best practices etc.
https://www.freertos.org/implementing-a-FreeRTOS-task.html
According to this page, to prevent starvation, tasks should be event based. Regarding what I am trying to achieve, I will try to provide a very simplified example.
Background
I have a LCD screen, which should display data from a sensor. The data shown on the LCD will be done using a task, which according to the documentation, should never exit and should be event driven to prevent starvation.
I have a way of controlling the data shown on the LCD screen, which would be a rotary encoder. This encoder can be clicked, which should refresh the sensor's data.
Question
How would I implement the event based tasks, which are described on the FreeRTOS page, in this specific context? I had a look at the documentation and the "simple" example projects on their github, but as a beginner within C and embedded, they were extremely overwhelming.
Simple demo code
void update_sensor_task(void *pvParameters)
{
// Ensure the task keeps on running
for( ; ; )
{
if(event_update_sensor) // How would I be able to notify the task that this should be run?
{
// update the data
}
}
// Tasks should not be returning, but if they happen to do so, ensure a clean exit
vTaskDelete(NULL);
}
void screen_temperature_task(void *pvParameters)
{
for(; ;)
{
if(event_sensor_updated)
{
// Update the lcd screen with the new data
}
}
vTaskDelete(NULL);
}
void on_rotary_clicked(void *pvParameters)
{
// Notify the sensor task that it should be updating?
}
EDIT:
By using what has been marked as the correct answer, I have managed to get it to work by implementing it the following way:
/* Queue used to send and receive the data */
QueueHandle_t xStructQueue = NULL;
/* Struct which shall be used to hold and pass around the data for the LCD screen*/
struct LcdData
{
int current_temp;
int current_humidity;
} xLcdData;
void initialize_queues(void)
{
xLcdData.current_humidity = 0;
xLcdData.current_temp = 0;
xStructQueue = xQueueCreate(
/* The maximum number of items the queue can hold*/
5,
/* The size of each struct, which the queue should be able to hold */
sizeof( xLcdData )
);
if(xStructQueue == NULL)
{
ESP_LOGE(TAG, "Queue has not been initialized successfully");
}
}
void screen_temperature_task_simplified(void *pvParameters)
{
int counter = 0;
for(; ;)
{
struct LcdData xReceivedStructure;
BaseType_t result;
result = xQueueReceive(xStructQueue, &xReceivedStructure, ( TickType_t ) 10);
if(result == pdPASS)
{
counter = counter + 1;
char snum_current_counter[12];
sprintf(snum_current_counter, "%d", counter);
i2c_lcd1602_clear (lcd_info);
i2c_lcd1602_write_string (lcd_info, snum_current_counter);
}
}
vTaskDelete(NULL);
}
void update_sensor_struct(void)
{
xLcdData.current_temp = DHT11_read().temperature;
xLcdData.current_humidity = DHT11_read().humidity;
// Log the results in the console
printf("Temperature is %d \n", xLcdData.current_temp);
printf("Humidity is %d\n", xLcdData.current_humidity);
ESP_LOGI(TAG, "Data has been updated");
}
void on_rotary_clicked_simplified()
{
ESP_LOGI(TAG, "Rotary encoder has been clicked!");
// Update the struct which holds the data
update_sensor_struct();
/* Send the entire struct to the queue */
xQueueSend(
/* The handle of the queue */
xStructQueue,
/* The adress of the struct which should be sent */
(void *) &xLcdData,
/* Block time of 0 says don't block if the queue is already full.
Check the value returned by xQueueSend() to know if the message
was sent to the queue successfully. */
( TickType_t ) 0
);
}
I use FRTOS and event driven development.
The typical flow here would be:
for(;;)
{
BaseType_t result;
result = xQueueReceive(LCD_Event_Queue, &someLCDEvent, QUEUE_TIMEOUT);
if (result == pdPASS)
{
/* We have new event data in someLCDEvent; Use that data to update the LCD */
}
else
{
/* No new event, do some brief idle-time processing if necessary */
}
}
In brief, wait up to QUEUE_TIMEOUT time for a new event to arrive.
If a new event arrives within that timeframe successfully, then process the data in that event and update your screen.
If a new event does not arrive, you have an opportunity to do some other maintenance work.
Designing and defining the structure-type of someLCDEvent, and putting data into the queue is a big topic, and will depend a lot on your specific project.

Not getting proper time duration in C

Let's consider a multi-sports race event like a triathlon. In this event when a person completes one activity (running, swimming, cycling etc) they immediately start a new activity after it. Suppose I have a device that continuously monitors the activity of the person. If you see the code, in "main()" function the volatile variable "activity" gets input from the device.
The race was started 2 Hr before.
My aim is to find out from how long till the current moment the person is in that particular activity. Say from past 1000 seconds the person is in running activity and earlier he/she was doing "cycling" (this could be anything in the general case). Also, the point is that the end time of previous activity is the start time current activity.
The "triathlonTim()" function is called after every 1 sec continuously. When I calculate time as per my code, it's coming out to be 1 sec but actually it should be 1000 seconds. Here, time(NULL) and "stateTim" are updating continuously. "StateTim" variable must only be updated at the point when the person stops one activity and start other activity. So how do I fix it out? Any other idea or hint can also be useful to me.
#include<stdio.h>
#include<time.h>
#define CYCLING 1
#define RUNNING 2
#define SWIMMING 3
static int state ;
static int prevState ;
int stateTim;
void triathlonTim(int activity)
{
int activtyTimDur ;
if(activity == 10)
{
printf("doing Cycling\n\r");
state = CYCLING;
}
else if(activity == 20)
{
printf("doing Running\n\r");
state = RUNNING;
}
else if(activity == 30)
{
printf("doing Swimming\n\r");
state = SWIMMING;
}
if(prevState != state)
{
activtyTimDur = time(NULL) - stateTim;
stateTim =time(NULL);
printf("Activity Time Duration = %d\n\r", activtyTimDur);
}
}
int main(void)
{
volatile int activity;
while(1)
{
triathlonTim(activity);
sleep(1);
}
return 0;
}
You need to set prevState whenever the state changes. Otherwise, every call will be treated as a state change.
if(prevState != state)
{
activtyTimDur = time(NULL) - stateTim;
stateTim =time(NULL);
prevState = state;
printf("Activity Time Duration = %d\n\r", activtyTimDur);
}

How to resync time from NTP server in esp-idf?

I use an ESP32 with esp-idf. I need correct time, therefore i'm trying resync time with NTP server.
I use this example.[1]: https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp
When i call again the obtain_time() method the device is rebooting.
What do i wrong? I didn't find anything which help.
I (2259) initialise_wifi: Setting WiFi configuration SSID OpenWrt
I (2359) syncTime: I'm runing :)
I (2369) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.
I (2389) initialize_sntp: Initializing SNTP
I (2389) obtain_time: Waiting for system time to be set... (1/10)
...
I (18389) obtain_time: Waiting for system time to be set... (9/10)
-----The time is correct, but when i'm trying resync with NTP
I (20639) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.
I (20639) initialize_sntp: Initializing SNTP
assertion "Operating mode must not be set while SNTP client is running" failed: file "/home/lenovov510/esp/esp-idf/components/lwip/lwip/src/apps/sntp/sntp.c", line 600, function: sntp_s
etoperatingmode
abort() was called at PC 0x400d2c6b on core 1
ELF file SHA256: 145d1f5e047670ed10c462ae090b3e64db1c5aa158a9988417a513b2ee801051
Backtrace: 0x4008623c:0x3ffc7e00 0x40086489:0x3ffc7e20 0x400d2c6b:0x3ffc7e40 0x4011e251:0x3ffc7e70 0x400d28b4:0x3ffc7e90 0x400d28c7:0x3ffc7eb0 0x400d2aff:0x3ffc7f10 0x400d2bcd:0x3ffc7fa0
0x4008b569:0x3ffc7fc0
Rebooting...
There is my methods:
This give back the timestamp.
void getDateTime(char *dateTime)
{
char *TAG = "getDateTime";
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
char strftime_buf[64];
setenv("TZ", "GTM-2", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
sprintf(dateTime, "20%d-%d-%d+%d:%d:%d", timeinfo.tm_year - 100, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
This method trying to update time.
void syncTime()
{
char *TAG = "syncTime";
obtain_time();
}
static void obtain_time(void)
{
static const char *TAG = "obtain_time";
initialize_sntp();
time_t now = 0;
struct tm timeinfo = {0};
int retry = 0;
const int retry_count = 10;
while (retry!=retry_count)// timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count )
{
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
vTaskDelay(2000 / portTICK_PERIOD_MS);
time(&now);
localtime_r(&now, &timeinfo);
}
}
//----
static void initialize_sntp(void)
{
static const char *TAG = "initialize_sntp";
ESP_LOGI(TAG, "Initializing SNTP");
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();
...
//Update the timeInSec and Datettime variable
void updateTimeVariables(void *pvParameter)
{
char *TAG = "updateTimeVariables";
while (1 == 1)
{
getDateTime(dateTime);
timeInSec = getTimeNow();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
//Sync NTP server.
void updateTime(void *pvParameter)
{
char *TAG = "updateTime";
while (1 == 1)
{
syncTime();
vTaskDelay(10000 / portTICK_PERIOD_MS);//1800000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
...
xTaskCreate(&updateTime, "updateTime", 4000, NULL, 6, NULL);
xTaskCreate(&updateTimeVariables, "updateTimeVariables", 4000, NULL, 0, NULL);
Looks like you are trying to initialize sntp every time you update time.
Watch for second line of obtain_time function:
static const char *TAG = "obtain_time";
initialize_sntp(); // <<<< THIS ONE.
time_t now = 0;
struct tm timeinfo = {0};
//.....
You have to change you code in a way initialize_sntp would be called only once.
To solve this problem, you need to do a couple of things.
1.) Modify the sntp.c file in users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/src/apps/sntp/sntp.c and the users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/include/lwip/apps/sntp.h files and change the following:
a.) in the sntp.c file - change the static void sntp_request(void *arg) to "
void sntp_request(void *arg) to make the function available to other modules. It's somewhere around line 490 in the source file. Also, at line number 160, remove the word "static" to prevent compiler errors.
b.) In the sntp.h header file, add the statement void sntp_request(void *) to make the function prototype available to your code.
Below is my code with modifications to allow calling sntp_request() as needed. I call mine every 30 minutes or so, but you could wait a longer time, maybe one a day, would be sufficient to keep the clock reasonably stable.
bool sntp_1st_init = true; // 1st init call allowed
static void obtain_time(void)
{
if(sntp_1st_init) // doing this again?
{
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "north-america.pool.ntp.org");
ESP_LOGI(TAG, "Initializing SNTP");
sntp_1st_init = false; // don't call again
sntp_init(); // init and set time
}
else
{
ESP_LOGI(TAG, "Syncing System Time");
sntp_request(NULL); // sync time again
}
// wait for System time to be set by monitoring Date changes
int retry = 0;
const int retry_count = 15;
while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count)
{
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)",
retry, retry_count);
vTaskDelay((1 * ONEsec) / portTICK_PERIOD_MS);
time(&now);
localtime_r(&now, &timeinfo);
}
}
The bool "sntp_1st_init" is set true on program startup and set false after 1st sntp init takes place. The call to "sntp_setoperatingmode(SNTP_OPMODE_POLL)" can only be performed one time, so must be placed in the sntp_1st_init code section.
I have confirmed that these changes work by altering the system time to something other than the correct time and observing that the time gets corrected as expected.
The original authors limited the functionality of the sntp code by making the sntp_request() function static, preventing the user from making additional sntp corrections to the computer clock time. Even the best oscillator drifts over time, and if your going to bother to use sntp, you might as well allow for clock drift corrections as well.
Hope this helps.
Jerry
JWM Engineering Group
Luckily the sntp_stop() function doesn't remove previous settings (including servers) so you can use this:
sntp_stop();
sntp_init();
On the first run the sync takes ~30s and subsequent runs ~500ms.
I place this in a FreeRTOS task:
#include "esp_sntp.h"
#include "freertos/task.h"
void update(void* pvParameters) {
while (true) {
sntp_stop();
sntp_init();
vTaskDelay(pdMS_TO_TICKS(60 * 60 * 1000));
}
}
void setup(void) {
// Add your SNTP setup code here
xTaskCreate(update, "NtpUpdate", 2048, NULL, tskIDLE_PRIORITY,
&updateHandle);
}
Through issue 4386 the SNTP documentation has been updated with the following:
An application with this initialization code will periodically
synchronize the time. The time synchronization period is determined by
CONFIG_LWIP_SNTP_UPDATE_DELAY (default value is one hour). To modify
the variable, set CONFIG_LWIP_SNTP_UPDATE_DELAY in project
configuration.
All you need is to use the below code in your application:
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();

How to send messages in PM server Minix

So I'm trying to create a new system call on PM server. My question is, how can I send some kind of message to function.
in IPC server all I had to do is add my system call to the list, because all functions there were defined as (*func)(message *)
(...)/servers/ipc/main.c
static struct {
int type;
int (*func)(message *);
int reply; /* whether the reply action is passed through */
} ipc_calls[] = {
(...)
{ IPC_MYNEWSIGNAL, do_something, 1 },
};
but in PM in table.c functions are defined as
(...)/servers/pm/table.c
int (* const call_vec[NR_PM_CALLS])(void) = {
(...)
CALL(PM_GETSYSINFO) = do_getsysinfo
}
and if I try to pass function with signature
int do_something(message *m)
I will get error:
Incompatible pointer types: initializing int (*const)(void) with int (message *)
What is the correct way to create signal on PM server if I need to receive some kind of information?
As far as I understood from the question, you want to receive arguments inside the syscall handler. Let's take as an example the library function clock_settime from libc.
int clock_settime(clockid_t clock_id, const struct timespec *ts)
{
message m;
memset(&m, 0, sizeof(m));
m.m_lc_pm_time.clk_id = clock_id;
m.m_lc_pm_time.now = 1; /* set time immediately. don't use adjtime() method. */
m.m_lc_pm_time.sec = ts->tv_sec;
m.m_lc_pm_time.nsec = ts->tv_nsec;
if (_syscall(PM_PROC_NR, PM_CLOCK_SETTIME, &m) < 0)
return -1;
return 0;
}
As you can see it writes the args inside message struct and passes to _syscall. OK, now have a look at syscall handler for PM_CLOCK_SETTIME which is mounted in table.c.
int do_gettime()
{
clock_t ticks, realtime, clock;
time_t boottime;
int s;
if ( (s=getuptime(&ticks, &realtime, &boottime)) != OK)
panic("do_time couldn't get uptime: %d", s);
switch (m_in.m_lc_pm_time.clk_id) {
case CLOCK_REALTIME:
clock = realtime;
break;
case CLOCK_MONOTONIC:
clock = ticks;
break;
default:
return EINVAL; /* invalid/unsupported clock_id */
}
mp->mp_reply.m_pm_lc_time.sec = boottime + (clock / system_hz);
mp->mp_reply.m_pm_lc_time.nsec =
(uint32_t) ((clock % system_hz) * 1000000000ULL / system_hz);
return(OK);
}
It becomes clear that the argument is a global variable named m_in. A little bit more search shows that it comes from glo.h
/* The parameters of the call are kept here. */
EXTERN message m_in; /* the incoming message itself is kept here. */
I suppose that MINIX will handle setting and accessing the global variable, so you don't need to explicitly write to it.
Have a look at point 7 Passing a parameter to a system call here. To understand how to compile the kernel correctly refer to this post.

Print OSTime in UC/OS-II

Device : F28335 contorolCRAD and Experimenter's Kit - Delfino F28335.
Ported ucos-II.
I use OSTimeGet() function to get OSTime.
But task1 returns '0' everytime and task2 doesn't work.
What is the problem?
How to fix this?
App_Task1's priority = 6u
App_Task2's priority = 7u
static void App_Task1 (void *p_arg)
{
(void)&p_arg;
INT32U t;
while (DEF_TRUE) {
t = OSTimeGet();
printf("Task1 \n");
printf("OSTime=%lu\n",t);
OSTimeDly(5);
}
}
static void App_Task2 (void *p_arg)
{
(void)&p_arg;
INT32U t;
while (DEF_TRUE) {
t = OSTimeGet();
printf("Task2 \n");
printf("OSTime=%lu\n",t);
OSTimeDly(10);
}
}
output
Task1 OSTime=0
It seems that your Systick function isn't running correctly. As I have no experience in the Chip you are using I cannot give you the full answer.
But your systick function should contain something like this.
This is code from a LPC17xx but something similliar should happen for you
void OS_CPU_SysTickHandler (void)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
OSIntNestingCtr++; /* Tell uC/OS-II that we are starting an ISR */
CPU_CRITICAL_EXIT();
OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */
OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
}
The OSTimeTick() is used for your OSTimeDly(), OSTimeGet() and the task switching

Resources