{"id":135843,"date":"2018-10-05T11:12:00","date_gmt":"2018-10-05T08:12:00","guid":{"rendered":"https:\/\/ruuvi.com\/ruuvi-firmware-part-11-scheduler\/"},"modified":"2026-06-11T08:48:56","modified_gmt":"2026-06-11T05:48:56","slug":"ruuvi-firmware-part-11-scheduler","status":"publish","type":"post","link":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/","title":{"rendered":"Ruuvi Firmware \u2013 Teil 11: Scheduler"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"536\" src=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11-1024x536.jpeg\" alt=\"Ruuvi Firmware-Serie Teil 11 Einf&#xFC;hrungsbild\" class=\"wp-image-3725\" srcset=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11-1024x536.jpeg 1024w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11-450x236.jpeg 450w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11-768x402.jpeg 768w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11-600x314.jpeg 600w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">In diesem Teil des Tutorials f\u00fcgen wir Unterst\u00fctzung f\u00fcr die Zeitplanung von Aufgaben und die Planung zuk\u00fcnftig auszuf\u00fchrender Aufgaben hinzu. Der finale Code dieses Blogbeitrags kann auf <a href=\"https:\/\/github.com\/ruuvi\/ruuvi.firmware.c\">Ruuvi GitHub<\/a> im <em>ruuviblog<\/em>-Branch, Tag <em>3.11.0-alpha<\/em>, heruntergeladen werden. <\/p>\n\n<p class=\"wp-block-paragraph\">Bitte folge <a href=\"https:\/\/ruuvi.com\/ruuvi-firmware-part-1-sleep\/\">Teil 1 der Serie<\/a> f\u00fcr Details zum Klonen des Repositories und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann vom <a href=\"http:\/\/jenkins.ruuvi.com\/job\/ruuvi.firmware.c\/32\/\">Ruuvi Jenkins<\/a> heruntergeladen werden. <\/p>\n\n<h2 class=\"wp-block-heading\">Scheduler<\/h2>\n\n<p class=\"wp-block-paragraph\">Unser Programm ist um verschiedene Ereignisse herum strukturiert, wie zum Beispiel \u201eTue dies, wenn ein NFC-Feld erkannt wird\u201c oder \u201eTue das, wenn das BLE-\u00dcbertragungsfenster offen ist\u201c. Einige dieser Aufgaben haben wirklich strenge Echtzeitanforderungen, wie zum Beispiel <em>Bluetooth Low-Energy <\/em>(<strong>BLE<\/strong>)-\u00dcbertragungen, die ihre zugewiesenen Zeitfenster genau einhalten m\u00fcssen. <\/p>\n\n<p class=\"wp-block-paragraph\">Es ist leicht zu erkennen, dass die Aufgaben eine Art Zusammenarbeit erfordern. Es kann nicht alles gleichzeitig ausgef\u00fchrt werden, und einige Aufgaben haben h\u00f6here Priorit\u00e4ten als andere. Betrachten wir die Interaktion zwischen Taste und Bluetooth.  <\/p>\n\n<p class=\"wp-block-paragraph\">Der Tastendruck signalisiert, dass der Benutzer etwas geschehen lassen m\u00f6chte. Der Benutzer hat es jedoch wahrscheinlich nicht eilig \u2013 er kann 100 Millisekunden warten, ohne \u00fcberhaupt zu bemerken, dass es eine signifikante Verz\u00f6gerung gibt. Wenn unser Bluetooth-\u00dcbertragungsfenster jedoch innerhalb dieses 100-ms-Fensters liegt und die Bluetooth-Aufgabe nicht ausgef\u00fchrt werden konnte, w\u00fcrden wir das Fenster verpassen und die Bluetooth-Funktionalit\u00e4t unterbrechen.  <\/p>\n\n<p class=\"wp-block-paragraph\">Interrupts mit Priorit\u00e4ten sind ein Teil der L\u00f6sung. Unsere Bluetooth-Funktionen k\u00f6nnen alles andere unterbrechen, was im Programm l\u00e4uft. Nehmen wir jedoch an, wir m\u00f6chten die Sensordaten aktualisieren, nachdem die vorherigen Daten gesendet wurden: Wenn wir eine solche Aufgabe auf der h\u00f6chsten Priorit\u00e4tsstufe des Bluetooth-Interrupts starten, kann nichts anderes laufen, w\u00e4hrend wir die Sensoren auslesen \u2013 nicht einmal eine weitere Bluetooth-\u00dcbertragung.  <\/p>\n\n<p class=\"wp-block-paragraph\">Ein weiterer Teil der L\u00f6sung ist der Scheduler. Wir reservieren die Interrupts f\u00fcr zeitkritische Aktionen und kurze Ereignisse und planen die l\u00e4nger dauernden Aufgaben f\u00fcr einen sp\u00e4teren Zeitpunkt mit niedrigerer Priorit\u00e4t ein. <\/p>\n\n<p class=\"wp-block-paragraph\">Obwohl es viele Arten von Schedulern gibt, entscheiden wir uns wieder einmal daf\u00fcr, die Dinge einfach zu halten und einen queued Run-to-Completion-Scheduler zu verwenden. Aufgaben werden in der Reihenfolge ausgef\u00fchrt, in der sie geplant sind, und jede Aufgabe wird bis zu ihrer Vollendung ausgef\u00fchrt \u2013 m\u00f6glicherweise unterbrochen durch ein anderes Ereignis. <\/p>\n\n<p class=\"wp-block-paragraph\">Ein eleganterer Scheduler w\u00e4re ein kooperativer Scheduler, der andere Aufgaben laufen lassen w\u00fcrde, w\u00e4hrend eine Aufgabe auf etwas anderes wartet. Zum Beispiel k\u00f6nnte unsere Single-Shot-Sensoraufgabe die Ausf\u00fchrung an die n\u00e4chste Aufgabe abgeben, w\u00e4hrend die Aufgabe darauf wartet, dass der Sensor die Abtastung beendet. Praktisch ben\u00f6tigen wir diese Art von Finesse f\u00fcr das allt\u00e4gliche Sensor-Beaconing nicht, daher verwenden wir das einfache Modell.  <\/p>\n\n<h3 class=\"wp-block-heading\">Scheduler-Schnittstelle<\/h3>\n\n<p class=\"wp-block-paragraph\">Unsere Scheduler-Schnittstelle umfasst die Initialisierung, die Definition der auszuf\u00fchrenden Aufgabe, eine Funktion zur Ausf\u00fchrung des Zeitplans und eine Funktion zum Hinzuf\u00fcgen einer Aufgabe zum Zeitplan. Wir k\u00f6nnten einige Analysen hinzuf\u00fcgen, wie zum Beispiel \u201emaximale Nutzung abrufen\u201c, aber da dies zu diesem Zeitpunkt nicht ben\u00f6tigt wird, halten wir die Schnittstelle kurz. <\/p>\n\n<pre class=\"wp-block-code\"><code>\/**\n * Initialize scheduler.\n *\n * Allocates memory for scheduler task queue.\n *\n * parameter max_event_size: maximum size for event data\n * parameter queue_size: maximum number of scheduled tasks\n *\n * Returns RUUVI_DRIVER_SUCCESS on success, error code from stack on error\n *\/\nruuvi_driver_status_t ruuvi_interface_scheduler_init(size_t event_size, size_t queue_size);\n\n\/**\n * Type definition for scheduler event handler.\n *\n * parameter p_event_data: Data for the event handler\n * parameter event_size: Size of the event data. Must be smaller than or equal to max_event_size\n *\n *\/\ntypedef void(*ruuvi_scheduler_event_handler_t)(void *p_event_data, uint16_t event_size);\n\n\/**\n *  Executes all scheduled tasks. If task schedules itself to be run immediately this will be run in a never-ending loop, without sleeping.\n *\n *  Returns RUUVI_DRIVER_SUCCESS if queue was executed successfully.\n *  Returns error code from the stack if error occurs.\n *\/\nruuvi_driver_status_t ruuvi_platform_scheduler_execute(void);\n\n\/**\n * Schedule given task to be executed on next call to ruuvi_platform_scheduler_execute\n *\/\nruuvi_driver_status_t ruuvi_platform_scheduler_event_put (void const *p_event_data, uint16_t event_size, ruuvi_scheduler_event_handler_t handler);<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/6535bf7ccc4badfe1a341d05a1c671d8\/raw\/c246e9490e3b1beac4bfd9722658c62d9d9afa76\/ruuvi_interface_scheduler.h\">Rohdaten anzeigen<\/a><\/p>\n\n<h3 class=\"wp-block-heading\">Scheduler-Implementierung<\/h3>\n\n<p class=\"wp-block-paragraph\">Wir werden die Nordic SDK Scheduler-Implementierung verwenden. Da die Implementierung ein Initialisierungs-Makro verwendet, das Parameter konstanter Gr\u00f6\u00dfe erwartet, m\u00fcssen wir alle dynamisch gegebenen Parameter ignorieren und feste Werte f\u00fcr den Scheduler <em>#define<\/em>n. Wenn die vom Benutzer angegebenen Parameter nicht mit den festen Werten \u00fcbereinstimmen, geben wir dem Benutzer eine Warnung aus.  <\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ Ignore give parameters to call the macro with #defined constants\nruuvi_driver_status_t ruuvi_interface_scheduler_init(size_t event_size, size_t queue_length)\n{\n  \/\/ Event size and queue length must be fixed at compile time. Warn user if other values are going to be used.\n  if(event_size != NRF5_SDK15_SCHEDULER_DATA_MAX_SIZE || queue_length != NRF5_SDK15_SCHEDULER_QUEUE_MAX_LENGTH)\n  { RUUVI_DRIVER_ERROR_CHECK(RUUVI_DRIVER_ERROR_INVALID_PARAM, ~~RUUVI_DRIVER_FATAL); }\n\n  APP_SCHED_INIT(NRF5_SDK15_SCHEDULER_DATA_MAX_SIZE, NRF5_SDK15_SCHEDULER_QUEUE_MAX_LENGTH);\n  return RUUVI_DRIVER_SUCCESS;\n}\n\nruuvi_driver_status_t ruuvi_platform_scheduler_execute (void)\n{\n  app_sched_execute();\n  return RUUVI_DRIVER_SUCCESS;\n}\n\nruuvi_driver_status_t ruuvi_platform_scheduler_event_put (void const *p_event_data, uint16_t event_size, ruuvi_scheduler_event_handler_t handler)\n{\n  ret_code_t err_code = app_sched_event_put(p_event_data, event_size, (app_sched_event_handler_t) handler);\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/cfde722e4a6a96c9e22e921c0494fb07\/raw\/c87c3b9f048220e1b34dc21467416509f657ae49\/ruuvi_platform_scheduler.h\">Rohdaten anzeigen<\/a><\/p>\n\n<h2 class=\"wp-block-heading\">Timer<\/h2>\n\n<p class=\"wp-block-paragraph\">Eng mit dem Scheduler verbunden sind ein Timer und zeitgesteuerte Aufgaben. Wir k\u00f6nnten die Aufgaben in Timer-Interrupts ausf\u00fchren, aber da wir den Scheduler haben, nutzen wir ihn und planen die Aufgaben nur in Timer-Interrupts ein. <\/p>\n\n<h3 class=\"wp-block-heading\">Timer-Schnittstelle<\/h3>\n\n<p class=\"wp-block-paragraph\">Eine Millisekunden-Aufl\u00f6sung ist f\u00fcr unsere Bed\u00fcrfnisse ausreichend und praktischerweise dieselbe Aufl\u00f6sung, die unser <a href=\"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-8-echtzeituhr\/\">Echtzeituhr<\/a> (<strong>RTC<\/strong>)-Treiber hat. Wir m\u00f6chten m\u00f6glicherweise einige Aufgaben nur einmal ausf\u00fchren, oder wir m\u00f6chten die Aufgaben f\u00fcr immer laufen lassen. Unsere Timer-Schnittstelle verf\u00fcgt \u00fcber Funktionen zur Initialisierung, zum Erstellen eines Timers, zum Starten eines Timers und zum Stoppen eines Timers.  <\/p>\n\n<pre class=\"wp-block-code\"><code>typedef enum {\n  RUUVI_INTERFACE_TIMER_MODE_SINGLE_SHOT,\n  RUUVI_INTERFACE_TIMER_MODE_REPEATED\n}ruuvi_interface_timer_mode_t;\n\n\/**\n * Function to be called when timer event occurs.\n *\/\ntypedef void(*ruuvi_timer_timeout_handler_t)(void* p_context);\n\n\/\/ Calls whatever initialization is required by application timers\nruuvi_driver_status_t ruuvi_platform_timers_init(void);\n\n\/\/return true if timers have been successfully initialized.\nbool platform_timers_is_init(void);\n\n\/* Function for creating a timer instance\n *\n * @param p_timer_id pointer to timer id, outputs ID which can be used to control the timer\n * @param mode mode of the timer, single shot or repeated\n * @param timeout_handler function which gets called\n *\/\nruuvi_driver_status_t ruuvi_platform_timer_create(ruuvi_platform_timer_id_t const *p_timer_id, ruuvi_interface_timer_mode_t mode, ruuvi_timer_timeout_handler_t timeout_handler);\n\n\/**\n * Start given timer at a mode defined in ruuvi_platform_timer_create. This operation is ignored if timer is already running.\n *\n * @param timer_id id of timer to control\n * @param timeout (or interval) of timer in milliseconds\n *\n * Return RUUVI_DRIVER_SUCCESS on success, error code on start.\n *\/\nruuvi_driver_status_t ruuvi_platform_timer_start (ruuvi_platform_timer_id_t timer_id, uint32_t ms);\n\n\/**\n * Stop a running timer.\n *\n * @param timer_id id of timer to stop\n * returns RUUVI_DRIVER_SUCCESS on success, error code from stack on error\n *\/\nruuvi_driver_status_t ruuvi_platform_timer_stop (ruuvi_platform_timer_id_t timer_id);<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/12a4a9ffebc4733a771ac717a12af152\/raw\/38e4973827d9dfa406a54c0e5e6d762431d86a7c\/ruuvi_interface_timer.h\">Rohdaten anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Eine Besonderheit ergibt sich aus der Verwendung des Nordic SDK Timer-Treibers. Die Timer werden zur Kompilierzeit zugewiesen, was die Definition des Timers mittels eines Makros erfordert. Da das Makro einen Timer des korrekten Typs definieren muss, m\u00fcssen wir einen Header aus den Plattformdateien einbinden, um den Timer-Typ zu definieren. Dies ist eine unerw\u00fcnschte Abh\u00e4ngigkeit zwischen Schnittstelle und Implementierung; idealerweise w\u00fcrde unsere Schnittstelle keine Informationen \u00fcber die zugrunde liegenden Details ben\u00f6tigen. Ideen zur Umgehung des Problems sind willkommen.    <\/p>\n\n<h3 class=\"wp-block-heading\">Timer-Treiber<\/h3>\n\n<p class=\"wp-block-paragraph\">Unser Timer-Treiber wird die Schnittstellenaufrufe an das Nordic SDK wrappen. Wir k\u00f6nnen die bekannten Zeilen 9 und 10 in der Init-Funktion erkennen; unser Timer verwendet dieselbe <em>Niederfrequenzuhr<\/em> (<strong>LFCLK<\/strong>) wie die <strong>RTC<\/strong> und Softdevice. <\/p>\n\n<pre class=\"wp-block-code\"><code>static bool m_is_init = false;\n\nruuvi_driver_status_t ruuvi_platform_timers_init(void)\n{\n  if (m_is_init) { return RUUVI_DRIVER_SUCCESS; }\n  ret_code_t err_code = NRF_SUCCESS;\n\n  \/\/ Initialize clock if not already initialized\n  if(false == nrf_drv_clock_init_check()){ err_code |= nrf_drv_clock_init(); }\n  nrf_drv_clock_lfclk_request(NULL);\n\n  err_code |= app_timer_init();\n  if (NRF_SUCCESS == err_code) { m_is_init = true; }\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}\n\n\/\/return true if timers have been successfully initialized.\nbool ruuvi_platform_timers_is_init(void)\n{\n  return m_is_init;\n}\n\nruuvi_driver_status_t ruuvi_platform_timer_create (ruuvi_platform_timer_id_t const *p_timer_id, ruuvi_interface_timer_mode_t mode, ruuvi_timer_timeout_handler_t timeout_handler)\n{\n  app_timer_mode_t nrf_mode = APP_TIMER_MODE_SINGLE_SHOT;\n  if (RUUVI_INTERFACE_TIMER_MODE_REPEATED == mode) { nrf_mode = APP_TIMER_MODE_REPEATED; }\n\n  ret_code_t err_code = app_timer_create (p_timer_id,\n                        nrf_mode,\n                        (app_timer_timeout_handler_t)timeout_handler);\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}\n\nruuvi_driver_status_t ruuvi_platform_timer_start (ruuvi_platform_timer_id_t timer_id, uint32_t ms)\n{\n  ret_code_t err_code = app_timer_start(timer_id, APP_TIMER_TICKS(ms), NULL);\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n\n}\n\nruuvi_driver_status_t ruuvi_platform_timer_stop (ruuvi_platform_timer_id_t timer_id)\n{\n  ret_code_t err_code = app_timer_stop(timer_id);\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/5d4089f9da44f1faed95aff5ff0d0bfc\/raw\/52ac584dd68a96e5c72bec23a8efce799cab5b0a\/ruuvi_platform_timer.c\">Rohdaten anzeigen<\/a><\/p>\n\n<h2 class=\"wp-block-heading\">Integration in die Anwendung<\/h2>\n\n<p class=\"wp-block-paragraph\">Wir werden einige \u00c4nderungen an der Anwendung vornehmen. Zuerst \u00e4ndern wir das <strong>BME280<\/strong> <em>Oversampling<\/em> auf <em>unendliche Impulsantwortfilterung<\/em> und versetzen den Sensor in den kontinuierlichen Modus \u2013 wodurch wir effektiv eine l\u00e4ngere Schrittantwortzeit gegen einen geringeren Stromverbrauch bei der Messung eintauschen. <\/p>\n\n<p class=\"wp-block-paragraph\">Da der Beschleunigungsmesser und die Umweltsensoren nun frei laufen, k\u00f6nnen wir nur die neuesten Daten vom Sensor lesen, anstatt eine separate Abtaststartaufgabe und Datenleseaufgabe zu haben.<\/p>\n\n<p class=\"wp-block-paragraph\">Unsere Batteriemessung unterst\u00fctzt einen solchen kontinuierlichen Modus nicht, auch wenn unser Treiber ihn emuliert. Daher f\u00fcgen wir eine Aufgabe hinzu, um die Batterie abzutasten und sie alle 30 Sekunden im wiederholten Modus auszuf\u00fchren. <\/p>\n\n<pre class=\"wp-block-code\"><code>RUUVI_PLATFORM_TIMER_ID_DEF(adc_timer);\n\n\/\/handler for scheduled accelerometer event\nstatic void task_adc_scheduler_task(void *p_event_data, uint16_t event_size)\n{\n  \/\/ Take new ADC sample\n  task_adc_sample();\n}\n\n\/\/ Timer callback, schedule accelerometer event here.\nstatic void task_adc_timer_cb(void* p_context)\n{\n  ruuvi_platform_scheduler_event_put(NULL, 0, task_adc_scheduler_task);\n}\n\nruuvi_driver_status_t task_adc_init(void)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  ruuvi_driver_bus_t bus = RUUVI_DRIVER_BUS_NONE;\n  uint8_t handle = RUUVI_INTERFACE_ADC_AINVDD;\n\n  \/\/ Initialize timer for adc task. Note: the timer is not started.\n  err_code |= ruuvi_platform_timer_create(&amp;adc_timer, RUUVI_INTERFACE_TIMER_MODE_REPEATED, task_adc_timer_cb);\n\n  err_code |= ruuvi_interface_adc_mcu_init(&amp;adc_sensor, bus, handle);\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  if(RUUVI_DRIVER_SUCCESS == err_code)\n  {\n    err_code |= task_adc_configure();\n    \/\/ Start the ADC timer here\n    err_code |= ruuvi_platform_timer_start(adc_timer, APPLICATION_ADC_SAMPLE_INTERVAL_MS);\n    return err_code;\n  }\n\n  \/\/ Return error if ADC could not be configured\n  return RUUVI_DRIVER_ERROR_NOT_FOUND;\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/03b174370767822a1df98dc58f8d1c75\/raw\/c51edd9d57190c6e1469babc3331bae6b3786441\/fixed_interval_task_adc.c\">Rohdaten anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Der obige Gist zeigt die Interaktion zwischen Aufgabe, Timer und Scheduler. Zuerst erstellen wir in Zeile 23 einen <em>Timer<\/em> im wiederholten Modus. In Zeile 32 starten wir den Timer in einem vordefinierten Intervall. Im Timer-Callback `task_adc_timer_adc` planen wir die Abtastung f\u00fcr den n\u00e4chsten Aufruf von `ruuvi_platform_scheduler_execute()` ein.   <\/p>\n\n<p class=\"wp-block-paragraph\">Schlie\u00dflich f\u00fcgen wir eine Aufgabe hinzu, die in unserem Broadcast-Intervall ausgef\u00fchrt wird, um die Bluetooth-Daten zu aktualisieren \u2013 wodurch die Daten f\u00fcr jede \u00dcbertragung aktualisiert werden. Ein Perfektionist k\u00f6nnte bemerken, dass die zuf\u00e4llige Verz\u00f6gerung im <strong>BLE<\/strong>-\u00dcbertragungsintervall dazu f\u00fchren wird, dass die \u00dcbertragungs- und Abtastungsereignisse auseinanderdriften, was schlie\u00dflich dazu f\u00fchrt, dass eine einzelne Abtastung nicht \u00fcbertragen wird. Dies ist keineswegs ernst, daher werden wir es noch nicht anpassen. Wir entfernen auch die Aktualisierungsfunktion f\u00fcr Tastendaten, da sie nicht mehr ben\u00f6tigt wird.   <\/p>\n\n<pre class=\"wp-block-code\"><code>RUUVI_PLATFORM_TIMER_ID_DEF(advertisement_timer);\nstatic ruuvi_interface_communication_t channel;\n\n\/\/handler for scheduled advertisement event\nstatic void task_advertisement_scheduler_task(void *p_event_data, uint16_t event_size)\n{\n  \/\/ Update BLE data\n  task_advertisement_send_3();\n}\n\n\/\/ Timer callback, schedule advertisement event here.\nstatic void task_advertisement_timer_cb(void* p_context)\n{\n  ruuvi_platform_scheduler_event_put(NULL, 0, task_advertisement_scheduler_task);\n}\n\nruuvi_driver_status_t task_advertisement_init(void)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  err_code |= ruuvi_interface_communication_ble4_advertising_init(&amp;channel);\n  err_code |= ruuvi_interface_communication_ble4_advertising_tx_interval_set(APPLICATION_ADVERTISING_INTERVAL);\n  int8_t target_power = APPLICATION_ADVERTISING_POWER;\n  err_code |= ruuvi_interface_communication_ble4_advertising_tx_power_set(&amp;target_power);\n  err_code |= ruuvi_interface_communication_ble4_advertising_manufacturer_id_set(RUUVI_BOARD_BLE_MANUFACTURER_ID);\n  err_code |= ruuvi_platform_timer_create(&amp;advertisement_timer, RUUVI_INTERFACE_TIMER_MODE_REPEATED, task_advertisement_timer_cb);\n  err_code |= ruuvi_platform_timer_start(advertisement_timer, APPLICATION_ADVERTISING_INTERVAL);\n  return err_code;\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/cdb224b04c447de7d3c41db0041f7a44\/raw\/af9e4e88aa0b13cf42622e31c2e1e6f877b57f27\/ruuviblog_11_task_advertisement.c\">Rohdaten anzeigen<\/a><\/p>\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Unsere Initialisierung erstellt und startet einen Timer, der die Datenaktualisierung plant<\/em><\/p>\n\n<p class=\"wp-block-paragraph\">Wir nutzen unsere Hauptschleife, die in den Schlafmodus geht, bis ein Ereignis eintritt. Wir k\u00f6nnen auf Ereignisse warten und den gesamten Zeitplan verarbeiten, nachdem ein Ereignis den Tag aus dem Schlafmodus geholt hat. <\/p>\n\n<pre class=\"wp-block-code\"><code>\/**\n * Ruuvi Firmware 3.x code. Reads the sensors onboard RuuviTag and broadcasts the sensor data in a manufacturer specific format.\n *\n * License: BSD-3\n * Author: Otso Jousimaa &lt;otso@ojousima.net&gt;\n **\/\n\n#include \"application_config.h\"\n#include \"ruuvi_interface_log.h\"\n#include \"ruuvi_interface_scheduler.h\"\n#include \"ruuvi_interface_yield.h\"\n#include \"ruuvi_boards.h\"\n#include \"task_acceleration.h\"\n#include \"task_adc.h\"\n#include \"task_advertisement.h\"\n#include \"task_button.h\"\n#include \"task_environmental.h\"\n#include \"task_led.h\"\n#include \"task_nfc.h\"\n#include \"task_power.h\"\n#include \"task_rtc.h\"\n#include \"task_scheduler.h\"\n#include \"task_spi.h\"\n#include \"task_timer.h\"\n#include \"test_sensor.h\"\n\n#include &lt;stdio.h&gt;\n\nint main(void)\n{\n  \/\/ Init logging\n  ruuvi_driver_status_t status = RUUVI_DRIVER_SUCCESS;\n  status |= ruuvi_platform_log_init(APPLICATION_LOG_LEVEL);\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Init yield\n  status |= ruuvi_platform_yield_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Init GPIO\n  status |= ruuvi_platform_gpio_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize LED gpio pins, turn RED led on.\n  status |= task_led_init();\n  status |= task_led_write(RUUVI_BOARD_LED_RED, TASK_LED_ON);\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize SPI\n  status |= task_spi_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize RTC, timer and scheduler\n  status |= task_rtc_init();\n  status |= task_timer_init();\n  status |= task_scheduler_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize power\n  status |= task_power_dcdc_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  #if RUUVI_RUN_TESTS\n  \/\/ Tests will initialize and uninitialize the sensors, run this before using them in application\n  test_sensor_run();\n\n  \/\/ Print unit test status, activate tests by building in DEBUG configuration under SES\n  size_t tests_run, tests_passed;\n  test_sensor_status(&amp;tests_run, &amp;tests_passed);\n  char message&#091;128] = {0};\n  snprintf(message, sizeof(message), \"Tests ran: %u, passed: %u\\r\\n\", tests_run, tests_passed);\n  ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, message);\n  #endif\n\n  \/\/ Initialize nfc\n  status |= task_nfc_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize ADC\n  status |= task_adc_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Initialize environmental- nRF52 will return ERROR NOT SUPPORTED on RuuviTag basic\n  \/\/ if DSP was configured, log warning\n  status |= task_environmental_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_SUPPORTED);\n\n  \/\/ Allow NOT FOUND in case we're running on basic model\n  status = task_acceleration_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_FOUND);\n\n  \/\/ Initialize button with on_button task\n  status = task_button_init(RUUVI_INTERFACE_GPIO_SLOPE_HITOLO, task_button_on_press);\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_FOUND | RUUVI_DRIVER_ERROR_NOT_SUPPORTED);\n\n  \/\/ Initialize BLE\n  status |= task_advertisement_init();\n  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);\n\n  \/\/ Turn RED led off. Turn GREEN LED on if no errors occured\n  status |= task_led_write(RUUVI_BOARD_LED_RED, TASK_LED_OFF);\n  if(RUUVI_DRIVER_SUCCESS == status)\n  {\n    status |= task_led_write(RUUVI_BOARD_LED_GREEN, TASK_LED_ON);\n    ruuvi_platform_delay_ms(1000);\n  }\n  \/\/ Reset any previous errors, turn LEDs off\n  status = task_led_write(RUUVI_BOARD_LED_GREEN, TASK_LED_OFF);\n\n  while (1)\n  {\n    \/\/ Turn off activity led\n    status = task_led_write(RUUVI_BOARD_LED_RED, !RUUVI_BOARD_LEDS_ACTIVE_STATE);\n    \/\/ Sleep\n    status |= ruuvi_platform_yield();\n    \/\/ Turn on activity led\n    status |= task_led_write(RUUVI_BOARD_LED_RED, RUUVI_BOARD_LEDS_ACTIVE_STATE);\n    \/\/ Execute scheduled tasks\n     status |= ruuvi_platform_scheduler_execute();\n    \/\/ Reset only on fatal error\n    RUUVI_DRIVER_ERROR_CHECK(status, ~RUUVI_DRIVER_ERROR_FATAL);\n  }\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/7da8afe1e9e7b192ff3c0335aa21143b\/raw\/238d65a63a176a74338bb047bdc54aa5ab38cba2\/ruufifw_3_11_main.c\">Rohdaten anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Unsere `main.c` hat nur eine Funktion, die die Aufgaben startet und in den Schlafmodus geht. Wenn ein Ereignis \u2013 wie ein Timer-Ereignis \u2013 auftritt, verl\u00e4sst unsere Ausf\u00fchrung `ruuvi_platform_yield` und tritt in `ruuvi_platform_scheduler_execute` ein. Nachdem die geplanten Aufgaben abgeschlossen sind, k\u00f6nnen wir wieder in den Schlafmodus gehen.  <\/p>\n\n<h2 class=\"wp-block-heading\">Stromverbrauch<\/h2>\n\n<p class=\"wp-block-paragraph\">Unser Projekt n\u00e4hert sich der Funktionsgleichheit mit der offiziellen Ruuvi Firmware. Uns fehlen Watchdog, Bootloader und URL-Modus, aber ansonsten hat der Tag jetzt die gleichen Funktionen wie die offizielle Firmware. \u00dcberpr\u00fcfen wir den Stromverbrauch.  <\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1021\" height=\"769\" src=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-power-consumtion-below-23.png\" alt=\"Diagramme, die zeigen, dass der Stromverbrauch etwas unter 23 &#x3BC;A liegt\" class=\"wp-image-3726\" srcset=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-power-consumtion-below-23.png 1021w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-power-consumtion-below-23-450x339.png 450w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-power-consumtion-below-23-768x578.png 768w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-power-consumtion-below-23-600x452.png 600w\" sizes=\"auto, (max-width: 1021px) 100vw, 1021px\" \/><figcaption>Stromverbrauch liegt etwas unter 23 \u03bcA<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">Unser gemessener Stromverbrauch betr\u00e4gt jetzt 23 <em>\u03bcA<\/em>, w\u00e4hrend die offizielle Firmware bei gleichem Betrieb 24 <em>\u03bcA<\/em> verbraucht. Es ist nicht wirklich m\u00f6glich, daraus zu schlie\u00dfen, dass wir etwas optimiert h\u00e4tten, da die Messgenauigkeit des Power Profiler Kits \u00b1 20 % betr\u00e4gt. <\/p>\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n<p class=\"wp-block-paragraph\">Wir haben nun eine einfache, aber leistungsstarke Low-Power-Sensor-Beacon-Firmware implementiert. Unsere n\u00e4chsten Blogbeitr\u00e4ge werden sich mit dem Hinzuf\u00fcgen weiterer Funktionen befassen, wie z. B. einem Bootloader f\u00fcr drahtlose Software-Updates, der Verbesserung der Batteriespannungsmessungen und dem Hinzuf\u00fcgen von Bewegungs-Interrupts. <\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Bleib dran und folge <\/strong><a href=\"https:\/\/twitter.com\/ojousima\"><strong>@ojousima<\/strong><\/a><strong> und <\/strong><a href=\"https:\/\/twitter.com\/ruuvicom\"><strong>@ruuvicom<\/strong><\/a><strong> auf Twitter f\u00fcr #FirmwareFriday-Beitr\u00e4ge!<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Teil des Tutorials f\u00fcgen wir Unterst\u00fctzung f\u00fcr die Zeitplanung von Aufgaben und die Planung zuk\u00fcnftig auszuf\u00fchrender Aufgaben hinzu. Der finale Code dieses Blogbeitrags kann auf Ruuvi GitHub im ruuviblog-Branch, Tag 3.11.0-alpha, heruntergeladen werden. Bitte folge Teil 1 der Serie f\u00fcr Details zum Klonen des Repositories und Kompilieren des Codes. Die finale Hex-Datei dieses [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":135844,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[324],"tags":[],"class_list":["post-135843","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruuvi-software-artikel"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi<\/title>\n<meta name=\"description\" content=\"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi\" \/>\n<meta property=\"og:description\" content=\"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/\" \/>\n<meta property=\"og:site_name\" content=\"Ruuvi\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/ruuvi.cc\" \/>\n<meta property=\"article:published_time\" content=\"2018-10-05T08:12:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-11T05:48:56+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"1400\" \/>\n\t<meta property=\"og:image:height\" content=\"733\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Otso Jousimaa\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ruuvicom\" \/>\n<meta name=\"twitter:site\" content=\"@ruuvicom\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Otso Jousimaa\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"15\u00a0Minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/\"},\"author\":{\"name\":\"Otso Jousimaa\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\"},\"headline\":\"Ruuvi Firmware \u2013 Teil 11: Scheduler\",\"datePublished\":\"2018-10-05T08:12:00+00:00\",\"dateModified\":\"2026-06-11T05:48:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/\"},\"wordCount\":1286,\"image\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-11.jpeg\",\"articleSection\":[\"Ruuvi-Software-Artikel\"],\"inLanguage\":\"de\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/\",\"name\":\"Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-11.jpeg\",\"datePublished\":\"2018-10-05T08:12:00+00:00\",\"dateModified\":\"2026-06-11T05:48:56+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\"},\"description\":\"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#primaryimage\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-11.jpeg\",\"contentUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-11.jpeg\",\"width\":1400,\"height\":733,\"caption\":\"Ruuvi Firmware-Serie Teil 11 Einf\u00fchrungsbild\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-part-11-scheduler\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/front\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ruuvi Firmware \u2013 Teil 11: Scheduler\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/\",\"name\":\"Ruuvi\",\"description\":\"Measure Your World\",\"potentialAction\":[],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\",\"name\":\"Otso Jousimaa\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"caption\":\"Otso Jousimaa\"},\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/author\\\/ojousima\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi","description":"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/","og_locale":"de_DE","og_type":"article","og_title":"Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi","og_description":"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.","og_url":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/","og_site_name":"Ruuvi","article_publisher":"https:\/\/www.facebook.com\/ruuvi.cc","article_published_time":"2018-10-05T08:12:00+00:00","article_modified_time":"2026-06-11T05:48:56+00:00","og_image":[{"width":1400,"height":733,"url":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg","type":"image\/jpeg"}],"author":"Otso Jousimaa","twitter_card":"summary_large_image","twitter_creator":"@ruuvicom","twitter_site":"@ruuvicom","twitter_misc":{"Verfasst von":"Otso Jousimaa","Gesch\u00e4tzte Lesezeit":"15\u00a0Minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#article","isPartOf":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/"},"author":{"name":"Otso Jousimaa","@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00"},"headline":"Ruuvi Firmware \u2013 Teil 11: Scheduler","datePublished":"2018-10-05T08:12:00+00:00","dateModified":"2026-06-11T05:48:56+00:00","mainEntityOfPage":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/"},"wordCount":1286,"image":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#primaryimage"},"thumbnailUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg","articleSection":["Ruuvi-Software-Artikel"],"inLanguage":"de"},{"@type":"WebPage","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/","url":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/","name":"Ruuvi Firmware \u2013 Teil 11: Scheduler \u2013 Ruuvi","isPartOf":{"@id":"https:\/\/ruuvi.com\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#primaryimage"},"image":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#primaryimage"},"thumbnailUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg","datePublished":"2018-10-05T08:12:00+00:00","dateModified":"2026-06-11T05:48:56+00:00","author":{"@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00"},"description":"Wie man einen Scheduler zum Firmware-Code des RuuviTags hinzuf\u00fcgt, um die Initialisierung und Definition f\u00fcr die auszuf\u00fchrende Aufgabe zu erm\u00f6glichen.","breadcrumb":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#primaryimage","url":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg","contentUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-11.jpeg","width":1400,"height":733,"caption":"Ruuvi Firmware-Serie Teil 11 Einf\u00fchrungsbild"},{"@type":"BreadcrumbList","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-part-11-scheduler\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ruuvi.com\/de\/front\/"},{"@type":"ListItem","position":2,"name":"Ruuvi Firmware \u2013 Teil 11: Scheduler"}]},{"@type":"WebSite","@id":"https:\/\/ruuvi.com\/de\/#website","url":"https:\/\/ruuvi.com\/de\/","name":"Ruuvi","description":"Measure Your World","potentialAction":[],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00","name":"Otso Jousimaa","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","caption":"Otso Jousimaa"},"url":"https:\/\/ruuvi.com\/de\/author\/ojousima\/"}]}},"_links":{"self":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/comments?post=135843"}],"version-history":[{"count":1,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135843\/revisions"}],"predecessor-version":[{"id":135846,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135843\/revisions\/135846"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/media\/135844"}],"wp:attachment":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/media?parent=135843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/categories?post=135843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/tags?post=135843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}