Weltweiter kostenloser Versand ab 120 € Bestellwert – Zahlung mit PayPal und Stripe – Hergestellt in Finnland

Ruuvi Firmware – Teil 11: Scheduler

Ruuvi Firmware-Serie Teil 11 Einführungsbild

In diesem Teil des Tutorials fügen wir Unterstützung für die Zeitplanung von Aufgaben und die Planung zukünftig auszuführender 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ür Details zum Klonen des Repositories und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann vom Ruuvi Jenkins heruntergeladen werden.

Scheduler

Unser Programm ist um verschiedene Ereignisse herum strukturiert, wie zum Beispiel „Tue dies, wenn ein NFC-Feld erkannt wird“ oder „Tue das, wenn das BLE-Übertragungsfenster offen ist“. Einige dieser Aufgaben haben wirklich strenge Echtzeitanforderungen, wie zum Beispiel Bluetooth Low-Energy (BLE)-Übertragungen, die ihre zugewiesenen Zeitfenster genau einhalten müssen.

Es ist leicht zu erkennen, dass die Aufgaben eine Art Zusammenarbeit erfordern. Es kann nicht alles gleichzeitig ausgeführt werden, und einige Aufgaben haben höhere Prioritäten als andere. Betrachten wir die Interaktion zwischen Taste und Bluetooth.

Der Tastendruck signalisiert, dass der Benutzer etwas geschehen lassen möchte. Der Benutzer hat es jedoch wahrscheinlich nicht eilig – er kann 100 Millisekunden warten, ohne überhaupt zu bemerken, dass es eine signifikante Verzögerung gibt. Wenn unser Bluetooth-Übertragungsfenster jedoch innerhalb dieses 100-ms-Fensters liegt und die Bluetooth-Aufgabe nicht ausgeführt werden konnte, würden wir das Fenster verpassen und die Bluetooth-Funktionalität unterbrechen.

Interrupts mit Prioritäten sind ein Teil der Lösung. Unsere Bluetooth-Funktionen können alles andere unterbrechen, was im Programm läuft. Nehmen wir jedoch an, wir möchten die Sensordaten aktualisieren, nachdem die vorherigen Daten gesendet wurden: Wenn wir eine solche Aufgabe auf der höchsten Prioritätsstufe des Bluetooth-Interrupts starten, kann nichts anderes laufen, während wir die Sensoren auslesen – nicht einmal eine weitere Bluetooth-Übertragung.

Ein weiterer Teil der Lösung ist der Scheduler. Wir reservieren die Interrupts für zeitkritische Aktionen und kurze Ereignisse und planen die länger dauernden Aufgaben für einen späteren Zeitpunkt mit niedrigerer Priorität ein.

Obwohl es viele Arten von Schedulern gibt, entscheiden wir uns wieder einmal dafür, die Dinge einfach zu halten und einen queued Run-to-Completion-Scheduler zu verwenden. Aufgaben werden in der Reihenfolge ausgeführt, in der sie geplant sind, und jede Aufgabe wird bis zu ihrer Vollendung ausgeführt – möglicherweise unterbrochen durch ein anderes Ereignis.

Ein eleganterer Scheduler wäre ein kooperativer Scheduler, der andere Aufgaben laufen lassen würde, während eine Aufgabe auf etwas anderes wartet. Zum Beispiel könnte unsere Single-Shot-Sensoraufgabe die Ausführung an die nächste Aufgabe abgeben, während die Aufgabe darauf wartet, dass der Sensor die Abtastung beendet. Praktisch benötigen wir diese Art von Finesse für das alltägliche Sensor-Beaconing nicht, daher verwenden wir das einfache Modell.

Scheduler-Schnittstelle

Unsere Scheduler-Schnittstelle umfasst die Initialisierung, die Definition der auszuführenden Aufgabe, eine Funktion zur Ausführung des Zeitplans und eine Funktion zum Hinzufügen einer Aufgabe zum Zeitplan. Wir könnten einige Analysen hinzufügen, wie zum Beispiel „maximale Nutzung abrufen“, aber da dies zu diesem Zeitpunkt nicht benötigt wird, halten wir die Schnittstelle kurz.

/**
 * Initialize scheduler.
 *
 * Allocates memory for scheduler task queue.
 *
 * parameter max_event_size: maximum size for event data
 * parameter queue_size: maximum number of scheduled tasks
 *
 * Returns RUUVI_DRIVER_SUCCESS on success, error code from stack on error
 */
ruuvi_driver_status_t ruuvi_interface_scheduler_init(size_t event_size, size_t queue_size);

/**
 * Type definition for scheduler event handler.
 *
 * parameter p_event_data: Data for the event handler
 * parameter event_size: Size of the event data. Must be smaller than or equal to max_event_size
 *
 */
typedef void(*ruuvi_scheduler_event_handler_t)(void *p_event_data, uint16_t event_size);

/**
 *  Executes all scheduled tasks. If task schedules itself to be run immediately this will be run in a never-ending loop, without sleeping.
 *
 *  Returns RUUVI_DRIVER_SUCCESS if queue was executed successfully.
 *  Returns error code from the stack if error occurs.
 */
ruuvi_driver_status_t ruuvi_platform_scheduler_execute(void);

/**
 * Schedule given task to be executed on next call to ruuvi_platform_scheduler_execute
 */
ruuvi_driver_status_t ruuvi_platform_scheduler_event_put (void const *p_event_data, uint16_t event_size, ruuvi_scheduler_event_handler_t handler);

Rohdaten anzeigen

Scheduler-Implementierung

Wir werden die Nordic SDK Scheduler-Implementierung verwenden. Da die Implementierung ein Initialisierungs-Makro verwendet, das Parameter konstanter Größe erwartet, müssen wir alle dynamisch gegebenen Parameter ignorieren und feste Werte für den Scheduler #definen. Wenn die vom Benutzer angegebenen Parameter nicht mit den festen Werten übereinstimmen, geben wir dem Benutzer eine Warnung aus.

// Ignore give parameters to call the macro with #defined constants
ruuvi_driver_status_t ruuvi_interface_scheduler_init(size_t event_size, size_t queue_length)
{
  // Event size and queue length must be fixed at compile time. Warn user if other values are going to be used.
  if(event_size != NRF5_SDK15_SCHEDULER_DATA_MAX_SIZE || queue_length != NRF5_SDK15_SCHEDULER_QUEUE_MAX_LENGTH)
  { RUUVI_DRIVER_ERROR_CHECK(RUUVI_DRIVER_ERROR_INVALID_PARAM, ~~RUUVI_DRIVER_FATAL); }

  APP_SCHED_INIT(NRF5_SDK15_SCHEDULER_DATA_MAX_SIZE, NRF5_SDK15_SCHEDULER_QUEUE_MAX_LENGTH);
  return RUUVI_DRIVER_SUCCESS;
}

ruuvi_driver_status_t ruuvi_platform_scheduler_execute (void)
{
  app_sched_execute();
  return RUUVI_DRIVER_SUCCESS;
}

ruuvi_driver_status_t ruuvi_platform_scheduler_event_put (void const *p_event_data, uint16_t event_size, ruuvi_scheduler_event_handler_t handler)
{
  ret_code_t err_code = app_sched_event_put(p_event_data, event_size, (app_sched_event_handler_t) handler);
  return ruuvi_platform_to_ruuvi_error(&err_code);
}

Rohdaten anzeigen

Timer

Eng mit dem Scheduler verbunden sind ein Timer und zeitgesteuerte Aufgaben. Wir könnten die Aufgaben in Timer-Interrupts ausführen, aber da wir den Scheduler haben, nutzen wir ihn und planen die Aufgaben nur in Timer-Interrupts ein.

Timer-Schnittstelle

Eine Millisekunden-Auflösung ist für unsere Bedürfnisse ausreichend und praktischerweise dieselbe Auflösung, die unser Echtzeituhr (RTC)-Treiber hat. Wir möchten möglicherweise einige Aufgaben nur einmal ausführen, oder wir möchten die Aufgaben für immer laufen lassen. Unsere Timer-Schnittstelle verfügt über Funktionen zur Initialisierung, zum Erstellen eines Timers, zum Starten eines Timers und zum Stoppen eines Timers.

typedef enum {
  RUUVI_INTERFACE_TIMER_MODE_SINGLE_SHOT,
  RUUVI_INTERFACE_TIMER_MODE_REPEATED
}ruuvi_interface_timer_mode_t;

/**
 * Function to be called when timer event occurs.
 */
typedef void(*ruuvi_timer_timeout_handler_t)(void* p_context);

// Calls whatever initialization is required by application timers
ruuvi_driver_status_t ruuvi_platform_timers_init(void);

//return true if timers have been successfully initialized.
bool platform_timers_is_init(void);

/* Function for creating a timer instance
 *
 * @param p_timer_id pointer to timer id, outputs ID which can be used to control the timer
 * @param mode mode of the timer, single shot or repeated
 * @param timeout_handler function which gets called
 */
ruuvi_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);

/**
 * Start given timer at a mode defined in ruuvi_platform_timer_create. This operation is ignored if timer is already running.
 *
 * @param timer_id id of timer to control
 * @param timeout (or interval) of timer in milliseconds
 *
 * Return RUUVI_DRIVER_SUCCESS on success, error code on start.
 */
ruuvi_driver_status_t ruuvi_platform_timer_start (ruuvi_platform_timer_id_t timer_id, uint32_t ms);

/**
 * Stop a running timer.
 *
 * @param timer_id id of timer to stop
 * returns RUUVI_DRIVER_SUCCESS on success, error code from stack on error
 */
ruuvi_driver_status_t ruuvi_platform_timer_stop (ruuvi_platform_timer_id_t timer_id);

Rohdaten anzeigen

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üssen wir einen Header aus den Plattformdateien einbinden, um den Timer-Typ zu definieren. Dies ist eine unerwünschte Abhängigkeit zwischen Schnittstelle und Implementierung; idealerweise würde unsere Schnittstelle keine Informationen über die zugrunde liegenden Details benötigen. Ideen zur Umgehung des Problems sind willkommen.

Timer-Treiber

Unser Timer-Treiber wird die Schnittstellenaufrufe an das Nordic SDK wrappen. Wir können die bekannten Zeilen 9 und 10 in der Init-Funktion erkennen; unser Timer verwendet dieselbe Niederfrequenzuhr (LFCLK) wie die RTC und Softdevice.

static bool m_is_init = false;

ruuvi_driver_status_t ruuvi_platform_timers_init(void)
{
  if (m_is_init) { return RUUVI_DRIVER_SUCCESS; }
  ret_code_t err_code = NRF_SUCCESS;

  // Initialize clock if not already initialized
  if(false == nrf_drv_clock_init_check()){ err_code |= nrf_drv_clock_init(); }
  nrf_drv_clock_lfclk_request(NULL);

  err_code |= app_timer_init();
  if (NRF_SUCCESS == err_code) { m_is_init = true; }
  return ruuvi_platform_to_ruuvi_error(&err_code);
}

//return true if timers have been successfully initialized.
bool ruuvi_platform_timers_is_init(void)
{
  return m_is_init;
}

ruuvi_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)
{
  app_timer_mode_t nrf_mode = APP_TIMER_MODE_SINGLE_SHOT;
  if (RUUVI_INTERFACE_TIMER_MODE_REPEATED == mode) { nrf_mode = APP_TIMER_MODE_REPEATED; }

  ret_code_t err_code = app_timer_create (p_timer_id,
                        nrf_mode,
                        (app_timer_timeout_handler_t)timeout_handler);
  return ruuvi_platform_to_ruuvi_error(&err_code);
}

ruuvi_driver_status_t ruuvi_platform_timer_start (ruuvi_platform_timer_id_t timer_id, uint32_t ms)
{
  ret_code_t err_code = app_timer_start(timer_id, APP_TIMER_TICKS(ms), NULL);
  return ruuvi_platform_to_ruuvi_error(&err_code);

}

ruuvi_driver_status_t ruuvi_platform_timer_stop (ruuvi_platform_timer_id_t timer_id)
{
  ret_code_t err_code = app_timer_stop(timer_id);
  return ruuvi_platform_to_ruuvi_error(&err_code);
}

Rohdaten anzeigen

Integration in die Anwendung

Wir werden einige Änderungen an der Anwendung vornehmen. Zuerst ändern wir das BME280 Oversampling auf unendliche Impulsantwortfilterung und versetzen den Sensor in den kontinuierlichen Modus – wodurch wir effektiv eine längere Schrittantwortzeit gegen einen geringeren Stromverbrauch bei der Messung eintauschen.

Da der Beschleunigungsmesser und die Umweltsensoren nun frei laufen, können wir nur die neuesten Daten vom Sensor lesen, anstatt eine separate Abtaststartaufgabe und Datenleseaufgabe zu haben.

Unsere Batteriemessung unterstützt einen solchen kontinuierlichen Modus nicht, auch wenn unser Treiber ihn emuliert. Daher fügen wir eine Aufgabe hinzu, um die Batterie abzutasten und sie alle 30 Sekunden im wiederholten Modus auszuführen.

RUUVI_PLATFORM_TIMER_ID_DEF(adc_timer);

//handler for scheduled accelerometer event
static void task_adc_scheduler_task(void *p_event_data, uint16_t event_size)
{
  // Take new ADC sample
  task_adc_sample();
}

// Timer callback, schedule accelerometer event here.
static void task_adc_timer_cb(void* p_context)
{
  ruuvi_platform_scheduler_event_put(NULL, 0, task_adc_scheduler_task);
}

ruuvi_driver_status_t task_adc_init(void)
{
  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;
  ruuvi_driver_bus_t bus = RUUVI_DRIVER_BUS_NONE;
  uint8_t handle = RUUVI_INTERFACE_ADC_AINVDD;

  // Initialize timer for adc task. Note: the timer is not started.
  err_code |= ruuvi_platform_timer_create(&adc_timer, RUUVI_INTERFACE_TIMER_MODE_REPEATED, task_adc_timer_cb);

  err_code |= ruuvi_interface_adc_mcu_init(&adc_sensor, bus, handle);
  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);

  if(RUUVI_DRIVER_SUCCESS == err_code)
  {
    err_code |= task_adc_configure();
    // Start the ADC timer here
    err_code |= ruuvi_platform_timer_start(adc_timer, APPLICATION_ADC_SAMPLE_INTERVAL_MS);
    return err_code;
  }

  // Return error if ADC could not be configured
  return RUUVI_DRIVER_ERROR_NOT_FOUND;
}

Rohdaten anzeigen

Der obige Gist zeigt die Interaktion zwischen Aufgabe, Timer und Scheduler. Zuerst erstellen wir in Zeile 23 einen Timer 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ür den nächsten Aufruf von `ruuvi_platform_scheduler_execute()` ein.

Schließlich fügen wir eine Aufgabe hinzu, die in unserem Broadcast-Intervall ausgeführt wird, um die Bluetooth-Daten zu aktualisieren – wodurch die Daten für jede Übertragung aktualisiert werden. Ein Perfektionist könnte bemerken, dass die zufällige Verzögerung im BLE-Übertragungsintervall dazu führen wird, dass die Übertragungs- und Abtastungsereignisse auseinanderdriften, was schließlich dazu führt, dass eine einzelne Abtastung nicht übertragen wird. Dies ist keineswegs ernst, daher werden wir es noch nicht anpassen. Wir entfernen auch die Aktualisierungsfunktion für Tastendaten, da sie nicht mehr benötigt wird.

RUUVI_PLATFORM_TIMER_ID_DEF(advertisement_timer);
static ruuvi_interface_communication_t channel;

//handler for scheduled advertisement event
static void task_advertisement_scheduler_task(void *p_event_data, uint16_t event_size)
{
  // Update BLE data
  task_advertisement_send_3();
}

// Timer callback, schedule advertisement event here.
static void task_advertisement_timer_cb(void* p_context)
{
  ruuvi_platform_scheduler_event_put(NULL, 0, task_advertisement_scheduler_task);
}

ruuvi_driver_status_t task_advertisement_init(void)
{
  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;
  err_code |= ruuvi_interface_communication_ble4_advertising_init(&channel);
  err_code |= ruuvi_interface_communication_ble4_advertising_tx_interval_set(APPLICATION_ADVERTISING_INTERVAL);
  int8_t target_power = APPLICATION_ADVERTISING_POWER;
  err_code |= ruuvi_interface_communication_ble4_advertising_tx_power_set(&target_power);
  err_code |= ruuvi_interface_communication_ble4_advertising_manufacturer_id_set(RUUVI_BOARD_BLE_MANUFACTURER_ID);
  err_code |= ruuvi_platform_timer_create(&advertisement_timer, RUUVI_INTERFACE_TIMER_MODE_REPEATED, task_advertisement_timer_cb);
  err_code |= ruuvi_platform_timer_start(advertisement_timer, APPLICATION_ADVERTISING_INTERVAL);
  return err_code;
}

Rohdaten anzeigen

Unsere Initialisierung erstellt und startet einen Timer, der die Datenaktualisierung plant

Wir nutzen unsere Hauptschleife, die in den Schlafmodus geht, bis ein Ereignis eintritt. Wir können auf Ereignisse warten und den gesamten Zeitplan verarbeiten, nachdem ein Ereignis den Tag aus dem Schlafmodus geholt hat.

/**
 * Ruuvi Firmware 3.x code. Reads the sensors onboard RuuviTag and broadcasts the sensor data in a manufacturer specific format.
 *
 * License: BSD-3
 * Author: Otso Jousimaa <otso@ojousima.net>
 **/

#include "application_config.h"
#include "ruuvi_interface_log.h"
#include "ruuvi_interface_scheduler.h"
#include "ruuvi_interface_yield.h"
#include "ruuvi_boards.h"
#include "task_acceleration.h"
#include "task_adc.h"
#include "task_advertisement.h"
#include "task_button.h"
#include "task_environmental.h"
#include "task_led.h"
#include "task_nfc.h"
#include "task_power.h"
#include "task_rtc.h"
#include "task_scheduler.h"
#include "task_spi.h"
#include "task_timer.h"
#include "test_sensor.h"

#include <stdio.h>

int main(void)
{
  // Init logging
  ruuvi_driver_status_t status = RUUVI_DRIVER_SUCCESS;
  status |= ruuvi_platform_log_init(APPLICATION_LOG_LEVEL);
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Init yield
  status |= ruuvi_platform_yield_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Init GPIO
  status |= ruuvi_platform_gpio_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize LED gpio pins, turn RED led on.
  status |= task_led_init();
  status |= task_led_write(RUUVI_BOARD_LED_RED, TASK_LED_ON);
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize SPI
  status |= task_spi_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize RTC, timer and scheduler
  status |= task_rtc_init();
  status |= task_timer_init();
  status |= task_scheduler_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize power
  status |= task_power_dcdc_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  #if RUUVI_RUN_TESTS
  // Tests will initialize and uninitialize the sensors, run this before using them in application
  test_sensor_run();

  // Print unit test status, activate tests by building in DEBUG configuration under SES
  size_t tests_run, tests_passed;
  test_sensor_status(&tests_run, &tests_passed);
  char message[128] = {0};
  snprintf(message, sizeof(message), "Tests ran: %u, passed: %u\r\n", tests_run, tests_passed);
  ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, message);
  #endif

  // Initialize nfc
  status |= task_nfc_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize ADC
  status |= task_adc_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Initialize environmental- nRF52 will return ERROR NOT SUPPORTED on RuuviTag basic
  // if DSP was configured, log warning
  status |= task_environmental_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_SUPPORTED);

  // Allow NOT FOUND in case we're running on basic model
  status = task_acceleration_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_FOUND);

  // Initialize button with on_button task
  status = task_button_init(RUUVI_INTERFACE_GPIO_SLOPE_HITOLO, task_button_on_press);
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_ERROR_NOT_FOUND | RUUVI_DRIVER_ERROR_NOT_SUPPORTED);

  // Initialize BLE
  status |= task_advertisement_init();
  RUUVI_DRIVER_ERROR_CHECK(status, RUUVI_DRIVER_SUCCESS);

  // Turn RED led off. Turn GREEN LED on if no errors occured
  status |= task_led_write(RUUVI_BOARD_LED_RED, TASK_LED_OFF);
  if(RUUVI_DRIVER_SUCCESS == status)
  {
    status |= task_led_write(RUUVI_BOARD_LED_GREEN, TASK_LED_ON);
    ruuvi_platform_delay_ms(1000);
  }
  // Reset any previous errors, turn LEDs off
  status = task_led_write(RUUVI_BOARD_LED_GREEN, TASK_LED_OFF);

  while (1)
  {
    // Turn off activity led
    status = task_led_write(RUUVI_BOARD_LED_RED, !RUUVI_BOARD_LEDS_ACTIVE_STATE);
    // Sleep
    status |= ruuvi_platform_yield();
    // Turn on activity led
    status |= task_led_write(RUUVI_BOARD_LED_RED, RUUVI_BOARD_LEDS_ACTIVE_STATE);
    // Execute scheduled tasks
     status |= ruuvi_platform_scheduler_execute();
    // Reset only on fatal error
    RUUVI_DRIVER_ERROR_CHECK(status, ~RUUVI_DRIVER_ERROR_FATAL);
  }
}

Rohdaten anzeigen

Unsere `main.c` hat nur eine Funktion, die die Aufgaben startet und in den Schlafmodus geht. Wenn ein Ereignis – wie ein Timer-Ereignis – auftritt, verlässt unsere Ausführung `ruuvi_platform_yield` und tritt in `ruuvi_platform_scheduler_execute` ein. Nachdem die geplanten Aufgaben abgeschlossen sind, können wir wieder in den Schlafmodus gehen.

Stromverbrauch

Unser Projekt nähert 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. Überprüfen wir den Stromverbrauch.

Diagramme, die zeigen, dass der Stromverbrauch etwas unter 23 μA liegt
Stromverbrauch liegt etwas unter 23 μA

Unser gemessener Stromverbrauch beträgt jetzt 23 μA, während die offizielle Firmware bei gleichem Betrieb 24 μA verbraucht. Es ist nicht wirklich möglich, daraus zu schließen, dass wir etwas optimiert hätten, da die Messgenauigkeit des Power Profiler Kits ± 20 % beträgt.

Fazit

Wir haben nun eine einfache, aber leistungsstarke Low-Power-Sensor-Beacon-Firmware implementiert. Unsere nächsten Blogbeiträge werden sich mit dem Hinzufügen weiterer Funktionen befassen, wie z. B. einem Bootloader für drahtlose Software-Updates, der Verbesserung der Batteriespannungsmessungen und dem Hinzufügen von Bewegungs-Interrupts.

Bleib dran und folge @ojousima und @ruuvicom auf Twitter für #FirmwareFriday-Beiträge!