Write better, more efficient, and more maintainable code with these proven patterns and guidelines.
📑 Table of Contents
- Event Loop Best Practices
- Memory Management
- Display Optimization
- Callback Design
- Error Handling
- Power Efficiency
- Code Organization
- Security Considerations
- Testing & Debugging
🔄 Event Loop Best Practices
✅ DO: Keep Your Main Loop Simple
int main(void) {
// Initialization
setup_display();
setup_callbacks();
// Simple, clean event loop
while(1) {
akira_process_events();
}
return 0;
}
❌ DON’T: Put Business Logic in Main Loop
// BAD - cluttered main loop
int main(void) {
while(1) {
read_sensors();
update_display();
check_buttons();
process_network();
akira_process_events();
}
}
Why? akira_process_events() already dispatches to your callbacks. Keep the main loop focused on event processing.
✅ DO: Use Callbacks for Asynchronous Events
void on_timer() {
update_sensor_display();
}
void on_button(uint32_t buttons) {
handle_user_input(buttons);
}
int main(void) {
akira_register_timer_callback(0, on_timer);
akira_input_set_callback(on_button);
while(1) {
akira_process_events();
}
}
❌ DON’T: Block in Callbacks
// BAD - blocks event processing
void on_button(uint32_t buttons) {
for(int i = 0; i < 1000; i++) {
akira_system_sleep(10); // Blocks for 10 seconds!
update_animation();
}
}
Why? Callbacks must return quickly. Use timers or state machines for long operations.
✅ DO: Use State Machines for Complex Logic
typedef enum {
STATE_IDLE,
STATE_READING,
STATE_PROCESSING,
STATE_COMPLETE
} AppState;
static AppState state = STATE_IDLE;
void on_timer() {
switch(state) {
case STATE_IDLE:
start_sensor_read();
state = STATE_READING;
break;
case STATE_READING:
if (sensor_ready()) {
state = STATE_PROCESSING;
}
break;
case STATE_PROCESSING:
process_data();
state = STATE_COMPLETE;
break;
case STATE_COMPLETE:
display_results();
state = STATE_IDLE;
break;
}
}
💾 Memory Management
✅ DO: Use Static Buffers for Fixed-Size Data
static char display_buffer[256];
static uint8_t sensor_readings[MAX_SAMPLES];
Benefits:
- No heap fragmentation
- Predictable memory usage
- No allocation failures
❌ DON’T: Use Large Stack Buffers
void process_data() {
char huge_buffer[4096]; // BAD - may overflow stack
// ...
}
Why? WASM has limited stack space. Use static allocation for large buffers.
✅ DO: Check Array Bounds
static int values[MAX_VALUES];
static int count = 0;
void add_value(int val) {
if (count < MAX_VALUES) {
values[count++] = val;
} else {
akira_log(1, "Buffer full!");
}
}
✅ DO: Free Resources When Done
void cleanup() {
akira_rf_deinit();
akira_unregister_timer_callback(0);
akira_unregister_gpio_callback(0, 5);
}
🎨 Display Optimization
✅ DO: Batch Display Updates
// GOOD - single flush
void update_screen() {
akira_display_clear(0x0000);
akira_display_text(10, 10, "Line 1", 0xFFFF);
akira_display_text(10, 30, "Line 2", 0xFFFF);
akira_display_text(10, 50, "Line 3", 0xFFFF);
akira_display_flush(); // Flush once
}
❌ DON’T: Flush After Every Draw
// BAD - too many flushes
akira_display_text(10, 10, "Line 1", 0xFFFF);
akira_display_flush();
akira_display_text(10, 30, "Line 2", 0xFFFF);
akira_display_flush();
akira_display_text(10, 50, "Line 3", 0xFFFF);
akira_display_flush();
Why? Each flush is expensive. Batch all updates, then flush once.
✅ DO: Only Update Changed Regions
static int last_value = -1;
void update_value(int new_value) {
if (new_value != last_value) {
akira_display_rect(100, 50, 50, 20, 0x0000); // Clear old
char text[16];
snprintf(text, sizeof(text), "%d", new_value);
akira_display_text(100, 50, text, 0xFFFF);
akira_display_flush();
last_value = new_value;
}
}
✅ DO: Limit Frame Rate
void animation_loop() {
while(1) {
update_animation();
akira_system_sleep(16); // ~60 FPS
akira_process_events();
}
}
Why? Higher frame rates waste power and CPU.
💡 Color Palette Tip
#define COLOR_BG 0x0000
#define COLOR_TEXT 0xFFFF
#define COLOR_SUCCESS 0x07E0
#define COLOR_ERROR 0xF800
#define COLOR_WARNING 0xFFE0
void show_status(bool success) {
uint16_t color = success ? COLOR_SUCCESS : COLOR_ERROR;
akira_display_text(10, 10, "Status", color);
}
🎯 Callback Design
✅ DO: Keep Callbacks Short and Fast
void on_button(uint32_t buttons) {
if (buttons & AKIRA_BTN_A) {
flag_action_needed = true; // Set flag, process later
}
}
void on_timer() {
if (flag_action_needed) {
perform_long_operation();
flag_action_needed = false;
}
}
✅ DO: Check for NULL Callbacks
int my_register_callback(callback_func_t cb) {
if (!cb) {
akira_log(0, "NULL callback!");
return -1;
}
// Register...
}
❌ DON’T: Call Callbacks Recursively
// BAD - can cause stack overflow
void on_timer() {
process_data();
on_timer(); // Don't do this!
}
✅ DO: Handle All Event Cases
void on_button(uint32_t buttons) {
if (buttons & AKIRA_BTN_A) {
handle_a();
} else if (buttons & AKIRA_BTN_B) {
handle_b();
} else if (buttons == 0) {
handle_release();
} else {
akira_log(3, "Unknown button combo");
}
}
⚠️ Error Handling
✅ DO: Check Return Values
if (akira_sensor_read(SENSOR_TYPE_TEMP, &temp) != 0) {
akira_log(0, "Sensor read failed!");
return;
}
process_temperature(temp);
❌ DON’T: Ignore Errors
// BAD - ignores potential failure
akira_sensor_read(SENSOR_TYPE_TEMP, &temp);
process_temperature(temp); // temp might be uninitialized!
✅ DO: Provide User Feedback
void show_error(const char *message) {
akira_display_clear(0x0000);
akira_display_text(10, 10, "Error!", 0xF800);
akira_display_text(10, 30, message, 0xFFFF);
akira_display_flush();
akira_log(0, message);
}
✅ DO: Implement Retry Logic
int retry_sensor_read(float *value, int max_retries) {
for (int i = 0; i < max_retries; i++) {
if (akira_sensor_read(SENSOR_TYPE_TEMP, value) == 0) {
return 0;
}
akira_system_sleep(100);
}
return -1;
}
✅ DO: Use Defensive Programming
void process_message(const char *topic, const void *payload, uint32_t len) {
if (!topic || !payload || len == 0) {
akira_log(1, "Invalid message parameters");
return;
}
if (len > MAX_PAYLOAD_SIZE) {
akira_log(1, "Payload too large");
return;
}
handle_message(topic, payload, len);
}
🔋 Power Efficiency
✅ DO: Sleep When Idle
void main_loop() {
while(1) {
if (has_work()) {
do_work();
} else {
akira_system_sleep(100); // Sleep when idle
}
akira_process_events();
}
}
✅ DO: Reduce Sensor Polling
// GOOD - read every 5 seconds
#define SENSOR_INTERVAL_MS 5000
void on_timer() {
read_sensor();
}
// BAD - constant polling
void main_loop() {
while(1) {
read_sensor(); // Too frequent!
akira_system_sleep(10);
}
}
✅ DO: Disable Unused Features
void cleanup_unused_features() {
akira_rf_deinit(); // Power down RF if not needed
akira_unregister_timer_callback(unused_timer_id);
}
✅ DO: Optimize Display Updates
static float last_temp = -999.0;
void update_display(float temp) {
if (fabs(temp - last_temp) > 0.1) { // Threshold
// Display update logic
last_temp = temp;
}
}
📁 Code Organization
✅ DO: Use Meaningful Names
// GOOD
void update_temperature_display(float celsius);
void handle_button_press(uint32_t button_mask);
// BAD
void upd(float t);
void hndl(uint32_t b);
✅ DO: Group Related Functions
// sensor.c
void sensor_init(void);
float sensor_read_temp(void);
float sensor_read_humidity(void);
// display.c
void display_init(void);
void display_update(void);
void display_clear(void);
// main.c
int main(void) {
sensor_init();
display_init();
// ...
}
✅ DO: Use Constants Instead of Magic Numbers
// GOOD
#define TEMP_THRESHOLD_LOW 18.0
#define TEMP_THRESHOLD_HIGH 25.0
#define UPDATE_INTERVAL_MS 1000
if (temp < TEMP_THRESHOLD_LOW) {
set_color(COLOR_BLUE);
}
// BAD
if (temp < 18.0) {
set_color(0x001F);
}
✅ DO: Comment Complex Logic
void calculate_moving_average() {
// Use circular buffer to maintain last N samples
// When buffer is full, overwrite oldest sample
int index = sample_count % BUFFER_SIZE;
buffer[index] = new_sample;
sample_count++;
// Calculate average of all valid samples
float sum = 0;
int count = (sample_count < BUFFER_SIZE) ? sample_count : BUFFER_SIZE;
for (int i = 0; i < count; i++) {
sum += buffer[i];
}
return sum / count;
}
🔒 Security Considerations
✅ DO: Validate Input Sizes
void handle_message(const char *topic, const void *payload, uint32_t len) {
if (len > MAX_SAFE_SIZE) {
akira_log(0, "Payload too large - rejected");
return;
}
process_payload(payload, len);
}
✅ DO: Use Bounds-Checked Functions
// GOOD
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// BAD
strcpy(dest, src); // No bounds checking!
✅ DO: Validate Pointers
void process_data(const char *data) {
if (!data) {
akira_log(0, "NULL pointer!");
return;
}
do_something(data);
}
✅ DO: Sanitize User Input
void handle_rf_packet(uint8_t *data, size_t len) {
if (len < sizeof(PacketHeader)) {
akira_log(1, "Invalid packet size");
return;
}
PacketHeader *hdr = (PacketHeader*)data;
if (!verify_checksum(hdr)) {
akira_log(1, "Checksum failed");
return;
}
process_valid_packet(hdr);
}
🧪 Testing & Debugging
✅ DO: Use Log Levels Appropriately
akira_log(0, "Critical error occurred!"); // Error
akira_log(1, "Warning: retrying operation"); // Warning
akira_log(2, "Sensor read successful"); // Info
akira_log(3, "Debug: value = 42"); // Debug
✅ DO: Add Debug Visualizations
#ifdef DEBUG
void show_debug_info() {
akira_display_text(200, 5, "DBG", 0xF800);
char dbg[32];
snprintf(dbg, sizeof(dbg), "M:%zu", akira_system_free_memory());
akira_display_text(180, 110, dbg, 0x7BEF);
}
#endif
✅ DO: Test Error Paths
void test_sensor_failure() {
float dummy;
int result = akira_sensor_read(SENSOR_TYPE_INVALID, &dummy);
if (result != 0) {
akira_log(2, "Error handling works!");
}
}
✅ DO: Monitor Resource Usage
void log_resources() {
size_t free_mem = akira_system_free_memory();
uint64_t uptime = akira_system_uptime_ms();
char msg[128];
snprintf(msg, sizeof(msg),
"Mem: %zu bytes | Uptime: %llu ms",
free_mem, uptime);
akira_log(3, msg);
}
🎯 Quick Reference Checklist
Before deploying your app, verify:
- Main loop calls
akira_process_events() - Callbacks return quickly
- All return values checked
- No large stack allocations
- Display updates batched
- Errors logged and handled
- Resource cleanup on exit
- Input validation present
- Power-saving measures used
- Code is commented
- Constants used instead of magic numbers
- Memory leaks checked
Related Documentation
- SDK API Reference - Complete API documentation
- SDK Troubleshooting - Common issues
- Building Apps - WASM compilation workflow
- First App Tutorial - Hello World