Examples
Complete examples for common embedded Linux scenarios.
Basic Linux Application
A simple update checker for any Linux system:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curl/curl.h>
#include "zelta.h"
// Current firmware version
#define CURRENT_VERSION "1.0.0"
#define FIRMWARE_PATH "/var/lib/zelta/firmware.bin"
// HTTP response buffer
struct response_buffer {
char *data;
size_t size;
};
static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
struct response_buffer *buf = userdata;
size_t total = size * nmemb;
char *new_data = realloc(buf->data, buf->size + total + 1);
if (!new_data) return 0;
buf->data = new_data;
memcpy(buf->data + buf->size, ptr, total);
buf->size += total;
buf->data[buf->size] = '\0';
return total;
}
// HTTP client implementation
static int http_request(const char *url, const char *method,
const char *body, const char *api_key,
char **response, size_t *response_len) {
CURL *curl = curl_easy_init();
if (!curl) return -1;
struct response_buffer buf = {0};
struct curl_slist *headers = NULL;
// Set headers
char auth_header[256];
snprintf(auth_header, sizeof(auth_header), "X-API-Key: %s", api_key);
headers = curl_slist_append(headers, auth_header);
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
if (strcmp(method, "POST") == 0) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
}
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
free(buf.data);
return -1;
}
*response = buf.data;
*response_len = buf.size;
return 0;
}
// File download with progress
static int download_firmware(const char *url, const char *path) {
CURL *curl = curl_easy_init();
if (!curl) return -1;
FILE *fp = fopen(path, "wb");
if (!fp) {
curl_easy_cleanup(curl);
return -1;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
CURLcode res = curl_easy_perform(curl);
fclose(fp);
curl_easy_cleanup(curl);
return (res == CURLE_OK) ? 0 : -1;
}
int main(int argc, char *argv[]) {
const char *api_key = getenv("ZELTA_API_KEY");
const char *product_id = getenv("ZELTA_PRODUCT_ID");
if (!api_key || !product_id) {
fprintf(stderr, "Set ZELTA_API_KEY and ZELTA_PRODUCT_ID\n");
return 1;
}
// Initialize Zelta
zelta_config_t config = {
.server_url = "https://api.zeltasoft.com",
.api_key = api_key,
.product_id = product_id,
.current_version = CURRENT_VERSION,
.http_request = http_request,
};
zelta_ctx_t *ctx = zelta_init(&config);
if (!ctx) {
fprintf(stderr, "Failed to initialize Zelta\n");
return 1;
}
// Check for updates
printf("Checking for updates...\n");
zelta_update_info_t update;
int result = zelta_check_update(ctx, &update);
if (result == ZELTA_UPDATE_AVAILABLE) {
printf("Update available: %s -> %s\n",
CURRENT_VERSION, update.version);
printf("Size: %zu bytes\n", update.size);
printf("Release notes: %s\n", update.release_notes);
// Download
printf("Downloading...\n");
if (download_firmware(update.download_url, FIRMWARE_PATH) == 0) {
printf("Downloaded to %s\n", FIRMWARE_PATH);
// Verify signature
if (zelta_verify_firmware(ctx, FIRMWARE_PATH,
update.signature) == ZELTA_OK) {
printf("Signature verified!\n");
// Report success
zelta_report_status(ctx, update.version,
ZELTA_STATUS_DOWNLOADED, NULL);
} else {
fprintf(stderr, "Signature verification failed!\n");
zelta_report_status(ctx, update.version,
ZELTA_STATUS_FAILED,
"Signature verification failed");
}
}
} else if (result == ZELTA_NO_UPDATE) {
printf("Already on latest version\n");
} else {
fprintf(stderr, "Update check failed\n");
}
zelta_cleanup(ctx);
return 0;
}
Raspberry Pi Example
Complete example for Raspberry Pi with GPIO status LED:
#include <stdio.h>
#include <signal.h>
#include <wiringPi.h>
#include "zelta.h"
#define LED_PIN 17
#define BUTTON_PIN 27
static volatile int running = 1;
void signal_handler(int sig) {
running = 0;
}
void blink_led(int times, int delay_ms) {
for (int i = 0; i < times; i++) {
digitalWrite(LED_PIN, HIGH);
delay(delay_ms);
digitalWrite(LED_PIN, LOW);
delay(delay_ms);
}
}
void update_progress_callback(int percent, void *userdata) {
printf("Download progress: %d%%\n", percent);
// Blink LED to show activity
if (percent % 10 == 0) {
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
}
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// Initialize GPIO
wiringPiSetupGpio();
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
pullUpDnControl(BUTTON_PIN, PUD_UP);
// Initialize Zelta
zelta_ctx_t *ctx = zelta_init_from_file("/etc/zelta/zelta.conf");
if (!ctx) {
fprintf(stderr, "Failed to initialize\n");
return 1;
}
zelta_set_progress_callback(ctx, update_progress_callback, NULL);
// Startup indication
blink_led(3, 100);
printf("Zelta OTA client running\n");
printf("Press button to check for updates\n");
time_t last_check = 0;
const int check_interval = 3600; // 1 hour
while (running) {
time_t now = time(NULL);
// Check on button press or interval
int button_pressed = (digitalRead(BUTTON_PIN) == LOW);
int interval_elapsed = (now - last_check) >= check_interval;
if (button_pressed || interval_elapsed) {
printf("Checking for updates...\n");
digitalWrite(LED_PIN, HIGH);
zelta_update_info_t update;
int result = zelta_check_update(ctx, &update);
if (result == ZELTA_UPDATE_AVAILABLE) {
printf("Update available: %s\n", update.version);
blink_led(5, 200); // Indicate update available
// Auto-download
if (zelta_download_update(ctx, &update,
"/tmp/firmware.bin") == ZELTA_OK) {
printf("Update downloaded, reboot to apply\n");
blink_led(10, 100); // Indicate ready
}
} else {
blink_led(1, 500); // Single blink = no update
}
digitalWrite(LED_PIN, LOW);
last_check = now;
// Debounce button
if (button_pressed) {
delay(500);
}
}
delay(100);
}
printf("Shutting down\n");
digitalWrite(LED_PIN, LOW);
zelta_cleanup(ctx);
return 0;
}
ESP-IDF Integration (ESP32)
For ESP32 with ESP-IDF:
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "nvs_flash.h"
#include "zelta.h"
static const char *TAG = "zelta_ota";
// HTTP client for ESP-IDF
static int esp_http_request(const char *url, const char *method,
const char *body, const char *api_key,
char **response, size_t *response_len) {
esp_http_client_config_t config = {
.url = url,
.method = (strcmp(method, "POST") == 0) ?
HTTP_METHOD_POST : HTTP_METHOD_GET,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// Set headers
esp_http_client_set_header(client, "X-API-Key", api_key);
esp_http_client_set_header(client, "Content-Type", "application/json");
if (body) {
esp_http_client_set_post_field(client, body, strlen(body));
}
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int content_length = esp_http_client_get_content_length(client);
*response = malloc(content_length + 1);
esp_http_client_read(client, *response, content_length);
(*response)[content_length] = '\0';
*response_len = content_length;
}
esp_http_client_cleanup(client);
return (err == ESP_OK) ? 0 : -1;
}
void ota_task(void *pvParameter) {
ESP_LOGI(TAG, "Starting OTA task");
// Get current version from app description
const esp_app_desc_t *app_desc = esp_app_get_description();
zelta_config_t config = {
.server_url = CONFIG_ZELTA_SERVER_URL,
.api_key = CONFIG_ZELTA_API_KEY,
.product_id = CONFIG_ZELTA_PRODUCT_ID,
.current_version = app_desc->version,
.http_request = esp_http_request,
};
zelta_ctx_t *ctx = zelta_init(&config);
while (1) {
ESP_LOGI(TAG, "Checking for updates...");
zelta_update_info_t update;
int result = zelta_check_update(ctx, &update);
if (result == ZELTA_UPDATE_AVAILABLE) {
ESP_LOGI(TAG, "Update available: %s", update.version);
// Use ESP-IDF OTA APIs
esp_ota_handle_t ota_handle;
const esp_partition_t *update_partition =
esp_ota_get_next_update_partition(NULL);
esp_err_t err = esp_ota_begin(update_partition,
OTA_SIZE_UNKNOWN, &ota_handle);
if (err == ESP_OK) {
// Download and write firmware
// ... (implement chunked download)
err = esp_ota_end(ota_handle);
if (err == ESP_OK) {
esp_ota_set_boot_partition(update_partition);
ESP_LOGI(TAG, "Update complete, rebooting...");
esp_restart();
}
}
}
// Check every hour
vTaskDelay(pdMS_TO_TICKS(3600000));
}
}
void app_main(void) {
nvs_flash_init();
esp_netif_init();
esp_event_loop_create_default();
// Connect to WiFi first...
xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL);
}
Daemon with D-Bus Interface
Full-featured daemon for desktop Linux:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "zelta.h"
static zelta_ctx_t *g_ctx = NULL;
static sd_bus *g_bus = NULL;
static sd_event *g_event = NULL;
// D-Bus method: CheckUpdate
static int method_check_update(sd_bus_message *m, void *userdata,
sd_bus_error *ret_error) {
zelta_update_info_t update;
int result = zelta_check_update(g_ctx, &update);
if (result == ZELTA_UPDATE_AVAILABLE) {
return sd_bus_reply_method_return(m, "ssb",
update.version, update.release_notes, 1);
} else {
return sd_bus_reply_method_return(m, "ssb", "", "", 0);
}
}
// D-Bus method: ApplyUpdate
static int method_apply_update(sd_bus_message *m, void *userdata,
sd_bus_error *ret_error) {
const char *version;
sd_bus_message_read(m, "s", &version);
// Trigger update application
// ...
return sd_bus_reply_method_return(m, "b", 1);
}
// D-Bus interface
static const sd_bus_vtable zelta_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("CheckUpdate", "", "ssb", method_check_update, 0),
SD_BUS_METHOD("ApplyUpdate", "s", "b", method_apply_update, 0),
SD_BUS_SIGNAL("UpdateAvailable", "ss", 0),
SD_BUS_SIGNAL("UpdateProgress", "i", 0),
SD_BUS_VTABLE_END
};
int main() {
int r;
// Initialize Zelta
g_ctx = zelta_init_from_file("/etc/zelta/zelta.conf");
// Connect to system bus
r = sd_bus_open_system(&g_bus);
if (r < 0) {
fprintf(stderr, "Failed to connect to bus: %s\n", strerror(-r));
return 1;
}
// Register D-Bus object
r = sd_bus_add_object_vtable(g_bus, NULL,
"/dev/zelta/Updater",
"dev.zelta.Updater",
zelta_vtable, NULL);
// Request service name
r = sd_bus_request_name(g_bus, "dev.zelta.Updater", 0);
// Create event loop
r = sd_event_default(&g_event);
r = sd_bus_attach_event(g_bus, g_event, 0);
printf("Zelta D-Bus service running\n");
// Run event loop
r = sd_event_loop(g_event);
sd_bus_unref(g_bus);
sd_event_unref(g_event);
zelta_cleanup(g_ctx);
return 0;
}
CMakeLists.txt
Build configuration for the examples:
cmake_minimum_required(VERSION 3.10)
project(zelta_examples C)
find_package(CURL REQUIRED)
find_package(OpenSSL REQUIRED)
# Basic example
add_executable(basic_example basic_example.c)
target_link_libraries(basic_example zelta ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES})
# Raspberry Pi example (if wiringPi available)
find_library(WIRINGPI_LIB wiringPi)
if(WIRINGPI_LIB)
add_executable(rpi_example rpi_example.c)
target_link_libraries(rpi_example zelta ${CURL_LIBRARIES} ${WIRINGPI_LIB})
endif()
# D-Bus daemon (if systemd available)
find_package(PkgConfig)
pkg_check_modules(SYSTEMD libsystemd)
if(SYSTEMD_FOUND)
add_executable(zelta_daemon daemon_example.c)
target_link_libraries(zelta_daemon zelta ${SYSTEMD_LIBRARIES})
endif()
Next Steps
- Yocto Integration - Package for Yocto builds
- Buildroot Integration - Package for Buildroot
- API Reference - Complete API documentation