
RTOS ultra-ligero para IoT con planificación preventiva, TLS/DTLS, MQTT, CoAP, compatibilidad con POSIX y protección de memoria basada en MPU. Kernel de menos de 10 KB.
Un sistema operativo en tiempo real ultraligero para dispositivos IoT y empotrados con recursos limitados.
Huella del kernel inferior a 10 KB, 2 KB de RAM mínima, planificación basada en prioridades con desalojo.
| Categoría | Detalles |
|---|---|
| Núcleo | Planificación basada en prioridades con desalojo (256 niveles), round-robin dentro de la misma prioridad, búsqueda O(1) de prioridad mediante mapa de bits, herencia de prioridad |
| Sincronización | Mutex (con herencia de prioridad), semáforo, variable de condición, grupos de eventos, colas de mensajes |
| Temporizadores de software | Disparo único y recarga automática, precisión de milisegundos, cambio de período en tiempo de ejecución |
| Memoria | Asignador de primer ajuste con coalescencia inmediata (montón de 8 KB, alineado a 8 bytes), detección de desbordamiento de pila, marca de agua alta por tarea |
| Shell | Shell interactivo VT100: 23 comandos integrados, historial de comandos (↑↓), finalización con tabulador, editor de línea completo |
| Compatibilidad POSIX | pthreads (create/join/detach/exit, mutex, cond var) · API de sockets BSD (socket/bind/listen/accept/connect/send/recv, inet_pton/ntop, htons/htonl) |
| Sistema de archivos | FS de dispositivo de bloques con journaling (WAL, recuperación ante fallos), compartición de bloques COW, instantáneas atómicas, API similar a POSIX |
| Red | Ethernet, IPv4, ICMP, UDP, TCP, cliente/servidor HTTP, DNS |
| TLS / DTLS | TLS 1.2/1.3 sobre TCP, DTLS 1.2 sobre UDP (backend mbedTLS) |
| MQTT | MQTT 3.1.1 completo: QoS 0/1/2 con tabla de reintentos en vuelo, cola fuera de línea, reconexión automática con retroceso exponencial |
| CoAP | Cliente/servidor compatible con RFC 7252, patrón observe |
| OTA | Actualizaciones de firmware por partición A/B, verificación CRC32, reversión |
| Perro guardián | Perro guardián de hardware y software, monitoreo de tiempo de espera por tarea |
| Energía | Modos inactivo, suspensión y suspensión profunda, inactividad sin ticks, escalado de frecuencia de CPU |
| Seguridad | Protección de memoria basada en MPU, soporte de arranque seguro |
| HAL | Capa genérica de abstracción de hardware: ARM Cortex-M / RISC-V / AVR; selección de arquitectura en tiempo de compilación, tablas de operaciones de periféricos |
| Arquitectura | Ejemplos |
|---|---|
| ARM Cortex-M (M0/M0+/M3/M4/M7) | STM32, nRF52, Raspberry Pi Pico |
| RISC-V (RV32I) | ESP32-C3 |
| AVR (experimental) | ATmega |
sudo apt-get install -y gcc-arm-none-eabi binutils-arm-none-eabi
sudo apt-get install -y qemu-system
Verificar instalación:```bash
arm-none-eabi-gcc --version # 10.x or later
qemu-system-arm --version # 6.x or later
make
make ARCH=cortex-m0 # Cortex-M0/M0+ make ARCH=cortex-m7 # Cortex-M7 make ARCH=riscv32 # RISC-V RV32I (uses riscv32-unknown-elf-gcc) make ARCH=avr5 # AVR ATmega (uses avr-gcc)
make EXAMPLE=blink_led # LED blink + task scheduler demo make EXAMPLE=event_groups # Event group AND/OR/NOT/SYNC demo make EXAMPLE=iot_sensor # Multi-sensor IoT node make EXAMPLE=shell_demo # Interactive UART shell make EXAMPLE=mqtt_demo # MQTT publish/subscribe make EXAMPLE=condition_variable # Producer/consumer
make example-blink make example-events make example-shell make example-mqtt make example-iot
make size # Print ROM/RAM usage
Los artefactos de compilación se colocan en `build/`:
| Archivo | Descripción |
|---|---|
| `build/tinyos.elf` | Imagen ELF con símbolos de depuración |
| `build/tinyos.bin` | Binario sin formato para flasheo |
| `build/tinyos.map` | Mapa del enlazador (tamaños de símbolos) |
### Ejecutar en QEMU
TinyOS se ejecuta en el objetivo `mps2-an385` de QEMU (ARM Cortex-M3, 4 MB flash, 4 MB RAM):```bash
# Run indefinitely (Ctrl-A X to quit)
qemu-system-arm \
-machine mps2-an385 \
-cpu cortex-m3 \
-nographic \
-kernel build/tinyos.elf
# Run for a fixed duration (e.g. 10 seconds)
timeout 10 qemu-system-arm \
-machine mps2-an385 \
-cpu cortex-m3 \
-nographic \
-kernel build/tinyos.elf
# Debug: trace interrupts
qemu-system-arm \
-machine mps2-an385 \
-cpu cortex-m3 \
-nographic \
-d int \
-kernel build/tinyos.elf
Salida esperada del seguimiento de interrupciones: las líneas repetidas successful exception return confirman que el planificador está ejecutándose, SysTick está funcionando y los cambios de contexto de PendSV se completan correctamente.
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
-c "program build/tinyos.bin verify reset exit 0x08000000"
pyocd flash --target cortex_m build/tinyos.bin
### Compilar con TLS (mbedTLS)
El soporte TLS se habilita automáticamente cuando mbedTLS está presente en `~/mbedtls`.
Para usar una ruta diferente:```bash
# Clone and build mbedTLS
git clone https://github.com/Mbed-TLS/mbedtls ~/mbedtls
make -C ~/mbedtls
# Build TinyOS with TLS
make MBEDTLS_DIR=~/mbedtls
Ejemplo de tarea mínima:```c #include "tinyos.h"
void my_task(void param) { while (1) { / work */ os_task_delay_ms(100); } }
int main(void) { tcb_t task; os_init(); os_task_create(&task, "my_task", my_task, NULL, PRIORITY_NORMAL); os_start(); }
---
## Resumen de la API
### Gestión de Tareas```c
os_task_create(tcb, name, entry, param, priority)
os_task_delete(task)
os_task_suspend(task) / os_task_resume(task)
os_task_delay(ticks) / os_task_delay_ms(ms)
os_task_set_priority(task, priority)
os_task_get_stats(task, stats)
os_task_get_stats_by_index(index, stats) /* iterate all tasks by index */
os_task_find_by_name(name) /* returns tcb_t*, NULL if not found */
os_get_system_stats(stats)
os_get_memory_stats(&free, &used, &allocs, &frees)
os_mutex_init(mutex) / os_mutex_lock(mutex, timeout) / os_mutex_unlock(mutex) os_semaphore_init(sem, count) / os_semaphore_wait(sem, timeout) / os_semaphore_post(sem) os_cond_init(cond) / os_cond_wait(cond, mutex, timeout) os_cond_signal(cond) / os_cond_broadcast(cond) os_event_group_set_bits(eg, bits) / os_event_group_wait_bits(eg, bits, opts, out, timeout) os_queue_init(q, buf, item_size, max) / os_queue_send(q, item, timeout) os_queue_receive(q, item, timeout) / os_queue_peek(q, item, timeout)
### Temporizadores```c
os_timer_create(timer, name, type, period_ms, callback, param)
os_timer_start(timer) / os_timer_stop(timer) / os_timer_reset(timer)
os_timer_change_period(timer, ms) / os_timer_get_remaining_ms(timer)
/* Register custom commands before calling shell_start() */ shell_register_cmd(name, handler_fn, help_text)
/* Provide UART I/O callbacks and start the shell task */ shell_io_t io = { .getc = uart_getc, .puts = uart_puts }; shell_start(&io)
/* Change the prompt at any time */ shell_set_prompt("mydevice> ")
/* Execute a single line programmatically */ shell_exec(line)
**Ejemplo de comando personalizado:**```c
static int cmd_led(int argc, char *argv[]) {
if (argc < 2) return 1; /* non-zero → prints usage */
bool on = (strcmp(argv[1], "on") == 0);
gpio_write(LED_PIN, on);
return 0;
}
/* In main(), before shell_start(): */
shell_register_cmd("led", cmd_led, "led <on|off> Toggle LED");
Métodos abreviados del editor de líneas:
Configuración del shell (include/tinyos/shell.h):```c
#define SHELL_MAX_COMMANDS 32 /* max registered commands /
#define SHELL_LINE_MAX 128 / max input line length (bytes) /
#define SHELL_ARGV_MAX 16 / max arguments per command /
#define SHELL_HISTORY_DEPTH 8 / command history entries */
### Red```c
net_init(driver, config) / net_start()
net_socket(type) / net_bind(sock, addr) / net_connect(sock, addr, timeout_ms)
net_send(sock, data, len, timeout_ms) / net_recv(sock, buf, len, timeout_ms)
net_sendto(sock, data, len, addr) / net_recvfrom(sock, buf, len, addr)
net_close(sock)
net_ping(dest_ip, timeout_ms, rtt)
net_dns_resolve(hostname, ip, timeout_ms)
net_http_get(url, response, timeout_ms)
net_http_post(url, content_type, body, len, response, timeout_ms)
TinyOS proporciona dos capas finas de compatibilidad que permiten que el código portátil estándar se compile y ejecute en TinyOS con cambios mínimos.
include/tinyos/posix_threads.h)Agrega src/posix/posix_threads.c a tu compilación.```c
#include "tinyos/posix_threads.h"
/* ── Thread ── / pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); tinyos_pthread_attr_setpriority(&attr, PRIORITY_NORMAL); / TinyOS extension / pthread_create(&tid, &attr, my_fn, arg); pthread_join(tid, &retval); pthread_detach(tid); / free resources automatically on exit / pthread_exit(retval); / terminate calling thread / pthread_self(); / handle of calling thread */
/* ── Mutex ── / pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mtx); pthread_mutex_trylock(&mtx); / returns EBUSY if already locked */ pthread_mutex_unlock(&mtx);
/* ── Condition variable ── / pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_wait(&cond, &mtx); pthread_cond_timedwait(&cond, &mtx, &abstime); / abstime relative to boot */ pthread_cond_signal(&cond); pthread_cond_broadcast(&cond);
| Concept | Mapped to |
|---|---|
| `pthread_t` | Índice en un grupo interno de ranuras `tcb_t` de TinyOS |
| `pthread_mutex_t` | Incorpora `mutex_t` directamente (inicialización a cero / `PTHREAD_MUTEX_INITIALIZER` válido) |
| `pthread_cond_t` | Incorpora `cond_var_t` directamente (inicialización a cero / `PTHREAD_COND_INITIALIZER` válido) |
| `pthread_join` | Espera en un `semaphore_t` por hilo publicado por `pthread_exit` |
**No soportado:** `pthread_cancel`, almacenamiento local de hilos (`pthread_key_*`),
mutex recursivos (devuelve `ENOTSUP`).
**Configuración** (`include/tinyos/posix_threads.h`):```c
#define PTHREAD_MAX_THREADS MAX_TASKS /* max concurrent pthreads */
include/tinyos/posix_socket.h)Agrega src/posix/posix_socket.c a tu compilación.```c
#include "tinyos/posix_socket.h"
/* ── TCP server ── */ int srv = socket(AF_INET, SOCK_STREAM, 0);
int reuse = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr = { htonl(INADDR_ANY) }, }; bind(srv, (struct sockaddr *)&addr, sizeof(addr)); listen(srv, 4);
struct sockaddr_in peer; socklen_t plen = sizeof(peer); int client = accept(srv, (struct sockaddr )&peer, &plen); recv(client, buf, sizeof(buf), 0); send(client, response, response_len, 0); posix_sock_close(client); / or define TINYOS_POSIX_WRAP_CLOSE to use close() */ posix_sock_close(srv);
/* ── TCP client ── */ int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in dest = { .sin_family = AF_INET, .sin_port = htons(80), .sin_addr = { inet_addr("192.168.1.1") }, }; connect(fd, (struct sockaddr *)&dest, sizeof(dest)); send(fd, request, request_len, 0); recv(fd, buf, sizeof(buf), 0); posix_sock_close(fd);
/* ── UDP ── */ int udp = socket(AF_INET, SOCK_DGRAM, 0); sendto(udp, data, len, 0, (struct sockaddr *)&dest, sizeof(dest)); recvfrom(udp, buf, sizeof(buf), 0, (struct sockaddr *)&src, &srclen); posix_sock_close(udp);
/* ── Address utilities ── / htons(port) / htonl(addr) / ntohs(n) / ntohl(n) inet_addr("192.168.1.1") / → in_addr_t, network byte order / inet_ntoa(in) / → "192.168.1.1" (static buffer) */ inet_pton(AF_INET, "192.168.1.1", &in_addr) inet_ntop(AF_INET, &in_addr, buf, sizeof(buf))
| Llamada BSD | Asignada a |
|---|---|
| `socket()` | `net_socket()` |
| `bind()` | `net_bind()` |
| `listen()` | `net_listen()` |
| `accept()` | `net_accept()` |
| `connect()` | `net_connect()` |
| `send()` / `recv()` | `net_send()` / `net_recv()` |
| `sendto()` / `recvfrom()` | `net_sendto()` / `net_recvfrom()` |
| `posix_sock_close()` | `net_close()` |
**Opciones `setsockopt` compatibles:**
| Opción | Efecto |
|---|---|
| `SO_REUSEADDR` | Aceptada; no-op (siempre reutilizable en TinyOS) |
| `SO_RCVTIMEO` | Establece el tiempo de espera de recepción por socket (struct timeval → ms) |
| `SO_SNDTIMEO` | Establece el tiempo de espera de envío/conexión por socket |
**Redirección de `close()`:** defina `TINYOS_POSIX_WRAP_CLOSE` antes de incluir el
encabezado para mapear `close(fd)` → `posix_sock_close(fd)`.
**No compatibles:** `select` / `poll` / `epoll`, modo no bloqueante (`O_NONBLOCK`),
IPv6 (`AF_INET6` devuelve `EAFNOSUPPORT`).
---
### TLS / DTLS
TLS 1.2/1.3 sobre TCP y DTLS 1.2 sobre UDP respaldados por **mbedTLS**.
Habilítelo en tiempo de compilación con `-DTINYOS_TLS_ENABLE` y enlace contra mbedTLS.```c
/* Client (TLS over TCP) */
tls_context_t tls;
tls_config_t cfg = TLS_CONFIG_DEFAULT_CLIENT;
cfg.ca_cert = ca_cert_pem;
cfg.ca_cert_len = sizeof(ca_cert_pem);
tls_init(&tls, &cfg);
net_socket_t sock = net_socket(SOCK_STREAM);
net_connect(sock, &broker_addr, 5000);
tls_connect(&tls, sock, "example.com", 5000);
tls_send(&tls, data, len);
tls_recv(&tls, buf, sizeof(buf), 5000);
tls_close(&tls);
/* Server (TLS accept) */
tls_config_t srv_cfg = TLS_CONFIG_DEFAULT_SERVER;
srv_cfg.cert = server_cert_pem;
srv_cfg.cert_len = sizeof(server_cert_pem);
srv_cfg.key = server_key_pem;
srv_cfg.key_len = sizeof(server_key_pem);
tls_init(&tls, &srv_cfg);
tls_accept(&tls, client_sock, 5000);
/* DTLS over UDP */
tls_config_t dtls_cfg = TLS_CONFIG_DEFAULT_DTLS_CLIENT;
net_socket_t usock = net_socket(SOCK_DGRAM);
tls_connect_dtls(&tls, usock, "example.com", 5000);
Full MQTT 3.1.1 con garantías de entrega QoS por mensaje.```c mqtt_config_t cfg = { .broker_host = "mqtt.example.com", .client_id = "tinyos-01", .keepalive_sec = 60, .clean_session = true, .auto_reconnect = true, .reconnect_interval_ms = 3000, /* base; doubles each attempt (max 60 s) */ }; mqtt_client_t client; mqtt_client_init(&client, &cfg); mqtt_set_connection_callback(&client, on_connect, NULL); mqtt_set_message_callback(&client, on_message, NULL); mqtt_connect(&client);
/* Publish — QoS1/2 are buffered offline if disconnected */ mqtt_publish(&client, "sensors/temp", "23.5", 4, MQTT_QOS_1, false);
/* Inspect reliability queues / uint8_t in_flight = mqtt_get_inflight_count(&client); / sent, awaiting ACK / uint8_t pending = mqtt_get_pending_count(&client); / queued while offline */
mqtt_subscribe(&client, "cmd/#", MQTT_QOS_1); mqtt_flush_pending(&client); /* discard offline queue */ mqtt_disconnect(&client);
#### Modelo de fiabilidad MQTT```
QoS 0 ─── fire-and-forget; dropped if disconnected
QoS 1 ─── in-flight table tracks each PUBLISH until PUBACK
↳ retransmits with DUP=1 every 5 s, up to 5 times
↳ if offline → offline queue (up to 8 messages)
QoS 2 ─── full PUBLISH → PUBREC → PUBREL → PUBCOMP handshake
↳ each step is retried independently on timeout
Auto-reconnect back-off: 3 s → 6 s → 12 s → … → 60 s (cap)
On reconnect: re-subscribes all topics, flushes offline queue
Configuración de fiabilidad MQTT (include/tinyos/mqtt.h):```c
#define MQTT_MAX_INFLIGHT 8 /* in-flight slots /
#define MQTT_MAX_PENDING 8 / offline queue slots /
#define MQTT_MAX_PAYLOAD_SIZE 512 / bytes per queued msg /
#define MQTT_RETRY_INTERVAL_MS 5000 / retry after (ms) /
#define MQTT_MAX_RETRY_COUNT 5 / retries before drop /
#define MQTT_RECONNECT_BASE_MS 3000 / first reconnect delay /
#define MQTT_RECONNECT_MAX_MS 60000 / backoff ceiling */
### CoAP```c
coap_init(ctx, config, is_server) / coap_start(ctx) / coap_stop(ctx)
coap_get(ctx, ip, port, path, response, timeout_ms)
coap_post(ctx, ip, port, path, format, payload, len, response, timeout_ms)
coap_resource_create(ctx, path, handler, user_data)
coap_process(ctx, timeout_ms)
ota_init(config) ota_start_update(url, callback, user_data) ota_write_chunk(data, size, offset) / ota_finalize_update() ota_confirm_boot() / ota_rollback() ota_verify_partition(type)
### Sistema de archivos```c
fs_format(device) / fs_mount(device) / fs_unmount()
fs_open(path, flags) / fs_close(fd)
fs_read(fd, buf, size) / fs_write(fd, buf, size)
fs_seek(fd, offset, whence) / fs_tell(fd)
fs_mkdir(path) / fs_remove(path) / fs_rmdir(path)
fs_stat(path, stat)
fs_opendir(path) / fs_readdir(dir, entry) / fs_closedir(dir)
fs_get_stats(stats) / fs_get_free_space() / fs_is_mounted()
/* Copy-on-Write snapshots */
fs_snapshot(source_path, snapshot_name) /* atomic COW snapshot of a file */
fs_get_block_refcount(block_nr) /* reference count of a data block */
El sistema de archivos utiliza journaling solo de metadatos (modo ordenado). Antes de que se modifique cualquier inodo, mapa de bits o bloque de directorio, se escribe un registro del diario en un área WAL dedicada de 32 bloques al inicio de la partición. En el siguiente montaje después de un fallo, fs_mount reproduce el diario y restaura un estado consistente.```
Disk layout (FS_BLOCK_SIZE = 512 bytes)
Block 0 Superblock (version 0x00020000 — v2 with journaling)
Block 1 Block bitmap
Block 2 Journal header
Blocks 3-33 Journal data (31 slots)
Blocks 34-41 Inode table (8 blocks, 128 inodes)
Block 42+ Data blocks
#### Copia en escritura (COW)
Cada bloque de datos lleva un contador de referencias en memoria reconstruido a partir de la tabla de inodos
al momento del montaje. Escribir en un bloque que está compartido (`refcount > 1`)
asigna primero una copia privada — el contador del bloque original se decrementa y
todos los cambios ocurren en el nuevo bloque.
`fs_snapshot()` crea una instantánea atómica en un punto en el tiempo duplicando el
inodo fuente (mismos punteros a bloque, contadores de referencia incrementados) dentro de una única transacción
del diario. O la instantánea completa se confirma o no cambia nada.```c
/* Create a snapshot of /data/config → /snapshots/config-20260101 */
fs_snapshot("/data/config", "/snapshots/config-20260101");
/* Inspect sharing */
uint8_t rc = fs_get_block_refcount(42); /* 1 = private, >1 = shared */
os_power_init() os_power_set_mode(mode) /* ACTIVE / IDLE / SLEEP / DEEP_SLEEP */ os_power_get_mode() os_power_enter_sleep(duration_ms) os_power_enter_deep_sleep(duration_ms) os_power_enable_tickless_idle(enable) os_power_set_cpu_frequency(freq_hz) os_power_configure_wakeup(source, enable) os_power_get_stats(stats) os_power_get_consumption_mw() os_power_estimate_battery_life_hours()
### Watchdog```c
wdt_init(config) / wdt_start() / wdt_stop()
wdt_feed() / wdt_set_timeout(ms)
wdt_register_task(task, timeout_ms) / wdt_feed_task(task)
TinyOS proporciona una HAL genérica que oculta todo el acceso a registros
específicos de la arquitectura detrás de una interfaz C estable. La arquitectura se selecciona en tiempo
de compilación mediante la variable ARCH del Makefile; no aparecen protecciones #ifdef en el código del núcleo.
static inline en cada cabecera de arquitectura)```cuint32_t hal_irq_save(void) /* disable IRQs, return saved state / void hal_irq_restore(uint32_t s) / restore IRQ state / void hal_context_switch_trigger() / pend PendSV / raise MSIP / Timer0 / void hal_cpu_wait_for_interrupt() / WFI / wfi / sleep instruction / void hal_cpu_dsb(void) / data synchronization barrier / void hal_cpu_isb(void) / instruction synchronization barrier */
#### Funciones no inline (implementadas por arquitectura en `hal/<arch>/hal_<arch>.c`)```c
void hal_init(void)
void hal_tick_init(uint32_t core_clock_hz, uint32_t tick_rate_hz)
void hal_tick_suppress(uint32_t max_ticks)
uint32_t hal_tick_unsuppress(void)
uint32_t hal_core_clock_hz(void)
bool hal_cycle_counter_init(void)
void hal_cycle_counter_reset(void)
uint32_t hal_cycle_counter_read(void)
bool hal_mpu_init(uint8_t *region_count)
int hal_mpu_configure_region(uint8_t region, uint32_t base,
uint32_t size, uint32_t attrs)
void hal_mpu_enable(bool allow_privileged_default)
void hal_mpu_disable(void)
void hal_irq_set_priority(int irq_num, uint8_t priority)
void hal_system_reset(void) /* does not return */
void hal_fault_capture(const uint32_t *frame, hal_fault_info_t *info)
Registre los periféricos de la placa una vez al inicio; el kernel y el administrador de energía consultan la tabla a través de hal_platform_get() en lugar de usar símbolos débiles.```c
static const hal_uart_ops_t my_uart = { .init = ..., .putc = ... };
static const hal_power_ops_t my_power = { .enter_sleep = ...,
.set_clock_hz = ... };
static const hal_platform_t board = { .uart[0] = &my_uart, .power = &my_power, };
hal_platform_register(&board); /* call before os_start() */
`hal_platform_t` proporciona espacios para `uart[4]`, `flash`, `gpio`, `spi[4]`, `i2c[4]` y `power`. Cualquier puntero `NULL` significa "no presente en esta placa".
#### Matriz de soporte de arquitectura
| Arquitectura | Fuente de tick | Contador de ciclos | MPU / PMP | Disparador de cambio de contexto |
|---|---|---|---|---|
| Cortex-M0/M0+/M3/M4/M7 | SysTick | DWT CYCCNT (M3+) | MPU | PendSV via ICSR |
| RISC-V RV32I/IM | CLINT MTIMECMP | `rdcycle` CSR | PMP (4 regions) | MSIP software interrupt |
| AVR ATmega/ATtiny | Timer0 CTC | — (devuelve 0) | — | Timer0 overflow |
---
## Configuración
**`include/tinyos.h`** — kernel y SO:```c
#define MAX_TASKS 16 /* max concurrent tasks */
#define STACK_SIZE 256 /* stack size per task (words) */
#define TICK_RATE_HZ 1000 /* scheduler tick frequency (Hz) */
#define TIME_SLICE_MS 10 /* round-robin time slice (ms) */
#define TICKLESS_MAX_SLEEP_TICKS 100U /* tickless idle: max ticks per WFI sleep */
include/tinyos/shell.h — shell interactiva:```c
#define SHELL_MAX_COMMANDS 32 /* max registered commands /
#define SHELL_LINE_MAX 128 / max input line length (bytes) /
#define SHELL_ARGV_MAX 16 / max arguments per command /
#define SHELL_HISTORY_DEPTH 8 / command history ring buffer */
**`include/tinyos/mqtt.h`** — fiabilidad de MQTT:```c
#define MQTT_MAX_INFLIGHT 8 /* in-flight QoS1/2 slots */
#define MQTT_MAX_PENDING 8 /* offline queue slots */
#define MQTT_MAX_PAYLOAD_SIZE 512 /* max queued payload bytes */
#define MQTT_RETRY_INTERVAL_MS 5000 /* unACKed retry interval */
#define MQTT_MAX_RETRY_COUNT 5 /* retries before discard */
#define MQTT_RECONNECT_BASE_MS 3000 /* initial reconnect delay */
#define MQTT_RECONNECT_MAX_MS 60000 /* back-off ceiling */
TLS — requiere mbedTLS; habilite con:```makefile CFLAGS += -DTINYOS_TLS_ENABLE LDFLAGS += -lmbedtls -lmbedcrypto -lmbedx509
---
## Estructura del proyecto```
tinyos-rtos/
├── include/
│ ├── tinyos.h # Core API (tasks, sync, timers, memory, FS, power)
│ └── tinyos/
│ ├── shell.h # Interactive shell API & configuration
│ ├── net.h # Network stack
│ ├── tls.h # TLS 1.2/1.3 + DTLS 1.2 (mbedTLS)
│ ├── mqtt.h # MQTT 3.1.1 client
│ ├── coap.h # CoAP RFC 7252
│ ├── ota.h # OTA firmware updates
│ ├── watchdog.h # Watchdog timer
│ ├── posix_threads.h # POSIX pthreads compatibility layer
│ └── posix_socket.h # BSD socket compatibility layer
├── src/
│ ├── startup.s # Vector table, Reset_Handler, SysTick/SVC/PendSV stubs
│ ├── context_switch.s # Thumb-2: PendSV_Handler, SVC_Handler, os_pend_sv
│ ├── kernel.c # Preemptive scheduler & task management
│ ├── sync.c # Mutex, semaphore, queue, condition var, event groups
│ ├── timer.c # Software timers
│ ├── memory.c # Heap allocator
│ ├── shell.c # Interactive shell (VT100, history, tab completion)
│ ├── filesystem.c # Block-device file system
│ ├── security.c # MPU memory protection
│ ├── power.c # Power management & CPU frequency scaling
│ ├── watchdog.c # Watchdog (HW + SW, per-task monitoring)
│ ├── bootloader.c # Secure bootloader
│ ├── ota.c # OTA A/B partition updates
│ ├── mqtt.c # MQTT client (in-flight table, offline queue)
│ ├── coap.c # CoAP client/server
│ ├── net/
│ │ ├── network.c # Core & buffer management
│ │ ├── ethernet.c # Ethernet / ARP
│ │ ├── ip.c # IPv4 / ICMP
│ │ ├── socket.c # UDP / TCP socket API
│ │ ├── http_dns.c # HTTP client & DNS resolver
│ │ └── tls.c # TLS/DTLS (mbedTLS wrapper, excluded when mbedTLS absent)
│ └── posix/
│ ├── posix_threads.c # pthreads → TinyOS task/sync wrapper
│ └── posix_socket.c # BSD socket → net_* wrapper
├── hal/
│ ├── hal.h # Portable HAL interface (arch-agnostic API + peripheral op-tables)
│ ├── cortex_m/
│ │ ├── hal_cortex_m.h # Register defines + static inline primitives (irq_save, WFI, DSB …)
│ │ └── hal_cortex_m.c # SysTick, DWT, MPU, AIRCR reset, fault capture
│ ├── riscv/
│ │ ├── hal_riscv.h # csrrci/csrw inline primitives, CLINT defines
│ │ └── hal_riscv.c # CLINT tick, rdcycle counter, PMP, PLIC priority, CSR fault capture
│ └── avr/
│ ├── hal_avr.h # SREG-based irq_save, Timer0 context-switch trigger
│ └── hal_avr.c # Timer0 CTC tick, watchdog reset, stub MPU/cycle-counter
├── drivers/
│ ├── flash.c / flash.h # Flash memory driver
│ ├── ramdisk.c / ramdisk.h # RAM disk (testing)
│ └── loopback_net.c # Loopback network driver (testing)
├── linker.ld # Linker script (mps2-an385: Flash 0x0/4MB, RAM 0x20000000/4MB)
└── examples/
├── blink_led.c # GPIO blink
├── iot_sensor.c # Multi-task sensor node
├── shell_demo.c # Custom shell commands over UART
├── network_demo.c # TCP/UDP/HTTP/ping
├── tls_demo.c # TLS client/server
├── mqtt_demo.c # MQTT publish/subscribe (QoS 1/2)
├── coap_demo.c # CoAP client/server
├── ota_demo.c # Firmware update flow
├── filesystem_demo.c # File I/O
├── watchdog_demo.c # Watchdog configuration
├── low_power.c # Power mode transitions
├── software_timers.c # Timer creation and callbacks
├── event_groups.c # Event synchronisation
├── event_flags_logic.c # AND / OR / NOT(CLEAR) / SYNC(barrier) patterns
├── condition_variable.c # Producer/consumer
├── priority_adjustment.c # Dynamic priority
├── task_statistics.c # CPU and stack monitoring
└── posix_compat_demo.c # POSIX pthreads + socket usage examples
| Arquitectura | Cambio de contexto |
|---|---|
| Cortex-M0 | ~2 μs |
| Cortex-M4 | ~1 μs |
| RISC-V | ~1.5 μs |
Requisitos del sistema: 2 KB RAM mínimo · < 10 KB ROM solo para el kernel
Nuevas características
Sistema de archivos con diario (src/filesystem.c) — El registro de escritura anticipada (WAL) protege
todas las escrituras de metadatos (inodos, mapa de bits, superbloque, directorios).
El formato en disco se actualizó a FS_VERSION 0x00020000. El diario ocupa 32 bloques
comenzando en el bloque 2; fs_mount reproduce cualquier transacción comprometida pero no aplicada
antes de pasar el control a la aplicación. El diario solo de metadatos (ordenado)
mantiene baja la amplificación de escritura mientras garantiza un sistema de archivos consistente después de
un fallo de energía o reinicio en cualquier punto durante una escritura.
Compartición de bloques con Copia en Escritura (src/filesystem.c) — Cada bloque de datos lleva
un contador de referencias en memoria (reconstruido desde la tabla de inodos al montar).
Escribir en un bloque compartido asigna silenciosamente una copia privada; el contador del bloque compartido
se decrementa. fs_snapshot(origen, nombre) crea una instantánea atómica
puntual dentro de una única transacción de diario — todo o nada se
compromete. Nueva API pública: fs_snapshot() y
fs_get_block_refcount() (consulte include/tinyos.h).
HAL genérica (hal/) — Una Capa de Abstracción de Hardware de dos niveles desacopla el
kernel del ensamblador específico de ARM Cortex-M.
Cambios importantes
src/kernel.c ya no contiene definiciones de registros SYST_*, SCB_SHPR3, SCB_AIRCR o
DWT_* — estas ahora son proporcionadas por la HAL. El código que
referenciaba directamente estas macros debe actualizarse para usar la API de la HAL.src/power.c (platform_enter_sleep_mode, etc.) ahora delegan en
hal_platform_t->power cuando se registra una plataforma; las placas que dependían
del __asm__ volatile("wfi") por defecto antiguo verán un comportamiento idéntico a menos
que se registre hal_power_ops_t.0x00020000. Los volúmenes formateados con v1.x deben
reformatearse (fs_format).Corrección de errores
startup.s — MemManage_Handler, BusFault_Handler y UsageFault_Handler ahora apuntan a los manejadores correctos en fault.c.Default_Handler, por lo que los fallos de MPU, bus y uso se ignoraban silenciosamente en lugar de activar el volcado de diagnóstico.os_cond_wait — cond_remove_task() ya decrementa waiting_count; se han corregido los tres sitios de llamada que lo decrementaban nuevamente después.os_mutex_lock ahora llama a mutex_pip_recalculate() antes de devolver OS_ERROR_TIMEOUT.
Anteriormente, el impulso de prioridad aplicado al propietario nunca se deshacía cuando la tarea en espera se rendía, causando inflación permanente de prioridad.Mejoras
src/memory.c) — se reemplazó el grupo de bloques fijos de 32 bytes con un asignador de primer ajuste que cuenta con:
os_free() (hacia adelante y hacia atrás)≥ BLK_HDR + ALIGN)os_init() — os_mem_init() y os_power_init() ahora se llaman antes que os_timer_init(), de modo que el montón y el subsistema de energía están listos antes de que se ejecuten las devoluciones de llamada de temporizador o el código de tareas.src/kernel.c: os_kernel_tickless_sleep) — cuando se habilita mediante os_power_enable_tickless_idle(true), la tarea inactiva suprime SysTick antes de WFI y usa el contador de ciclos DWT para medir el tiempo real transcurrido.
Tras el despertar, SysTick se reinicia, se avanza por los ticks medidos (limitado a ), y se llama a para desbloquear inmediatamente cualquier tarea vencida.
Anteriormente, la bandera existía pero la ruta inactiva siempre caía en un simple con SysTick en ejecución.Nuevas características
EVENT_WAIT_CLEAR para grupos de eventos — espera hasta que los bits se borren (condición NOT).EVENT_WAIT_ALL | EVENT_WAIT_CLEAR se activa cuando todos los bits enmascarados son 0; EVENT_WAIT_ANY | EVENT_WAIT_CLEAR se activa cuando alguno es 0.os_event_group_sync() — primitiva de rendezvous / barrera.examples/event_flags_logic.c que demuestra los cuatro modos (AND, OR, NOT, SYNC).Correcciones de compilación y tiempo de ejecución
src/startup.s: tabla de vectores, Reset_Handler (copia de .data, cero de .bss), stub SysTick_Handler, HardFault_Handler.linker.ld: diseño de memoria para mps2-an385 (Flash 0x00000000 / 4 MB, RAM 0x20000000 / 4 MB).LDMIA {R4-R11} de PendSV funcione correctamente en el primer cambio de contexto hacia cualquier tarea recién creada.SVC_Handler actualizado a LDMIA {R4-R11} antes de establecer PSP, manteniendo ambas rutas SVC y PendSV simétricas.MPU_TYPE en os_mpu_configure_default() — la configuración de MPU se omite silenciosamente cuando no hay MPU presente (QEMU ).Licencia MIT — consulte LICENSE para obtener detalles.
| Comando | Descripción |
|---|
help [cmd] | Lista todos los comandos, o muestra ayuda detallada para cmd |
clear | Limpia la pantalla del terminal (VT100) |
echo <text> | Imprime texto en el terminal |
history | Muestra el historial de comandos |
ps | Lista todas las tareas (estado, prioridad, %CPU, uso de pila) |
top | Lista de tareas ordenadas por uso de CPU descendente |
kill <name> [suspend|resume|delete] | Controla una tarea por nombre |
mem | Estadísticas del montón (total / usado / libre, conteos de alloc/free) |
ver | Versión de TinyOS y tiempo activo formateado |
net | Estadísticas de red (contadores Ethernet, IP, UDP, TCP) |
ping <ip> [count] | Envía solicitudes de eco ICMP |
ifconfig [ip|netmask|gw|dns <addr>] | Muestra o cambia la configuración de red |
power [active|idle|sleep|deepsleep] | Estadísticas de energía o cambio de modo |
ls [path] | Lista el directorio (predeterminado: /) |
cat <file> | Muestra el contenido del archivo |
mkdir <path> | Crea un directorio |
rm <path> | Elimina un archivo o directorio vacío |
df | Estadísticas de uso del sistema de archivos |
touch <file> | Crea un archivo vacío |
cp <src> <dst> | Copia un archivo |
uptime | Muestra el tiempo de actividad del sistema (HH:MM:SS o N día(s), HH:MM:SS) |
sleep <ms> | Retrasa la tarea del shell durante N milisegundos |
reboot | Reinicia el sistema |
| Tecla | Acción |
|---|
← / Ctrl-B | Mover el cursor a la izquierda |
→ / Ctrl-F | Mover el cursor a la derecha |
Inicio / Ctrl-A | Saltar al inicio de la línea |
Fin / Ctrl-E | Saltar al final de la línea |
↑ / ↓ | Navegar por el historial de comandos |
Tabulador | Completar el nombre del comando |
Ctrl-K | Borrar hasta el final de la línea |
Ctrl-U | Borrar hasta el inicio de la línea |
Ctrl-W | Borrar la palabra anterior |
Ctrl-L | Limpiar pantalla y redibujar |
Ctrl-C | Cancelar la línea actual |
| Componente | ROM | RAM |
|---|
| Kernel | 6 KB | 512 B |
| Por tarea | — | ~1 KB |
| Mutex | — | 12 B |
| Semáforo | — | 8 B |
| Cola de mensajes (10 elementos) | — | 40 B + datos |
| Shell (con 23 comandos integrados) | ~4 KB | ~2.5 KB |
| Cliente MQTT (con colas) | ~8 KB | ~10 KB |
| Capa de hilos POSIX | ~2 KB | ~PTHREAD_MAX_THREADS × (tcb_t + 32 B) |
| Capa de sockets POSIX | ~1 KB | ~NET_MAX_SOCKETS × 12 B |
hal/hal.h — interfaz independiente de la arquitectura: tick, contador de ciclos, MPU,
guardar/restaurar IRQ, disparador de cambio de contexto, reinicio del sistema, captura de fallos y
tablas de operaciones de periféricos (hal_platform_t con ranuras para UART / flash / GPIO / SPI / I²C /
potencia).hal/cortex_m/ — implementación completa para Cortex-M0–M7 (SysTick, DWT, AIRCR,
PRIMASK, disparador PendSV).hal/riscv/ — stub para RISC-V RV32 (tick CLINT MTIMECMP, contador rdcycle,
PMP, prioridad PLIC, captura de fallos CSR).hal/avr/ — stub para AVR ATmega (tick Timer0 CTC, secciones críticas SREG,
reinicio por watchdog).ARCH= (cortex-m* / riscv* / avr*)
y pasa -DHAL_ARCH_* al compilador.CYCCNTkernel.tick_countTICKLESS_MAX_SLEEP_TICKSdelay_queue_tick()WFIMAX_TASKS incrementado — aumentado de 8 a 16 para soportar cargas de trabajo IoT más realistas sin cambios de configuración por parte del usuario.mps2-an385timer_t → os_timer_t para evitar conflicto con <sys/types.h> de POSIX.-I. para includes de drivers/; las fuentes de TLS se excluyen automáticamente cuando mbedTLS está ausente; -Wno-stringop-truncation para falsos positivos de advertencias strncpy.coap.c, filesystem.c, mqtt.c, ota.c, security.c, net/ip.c, net/http_dns.c.