Salta al contenuto
Note per Studenti Esercizio - Shell di RIOT con i comandi board, cpu ed echo (laboratorio 11)

Esercizio - Shell di RIOT con i comandi board, cpu ed echo (laboratorio 11)

In questa pagina 4

Testo (laboratorio 11 del corso di Sistemi Operativi, Ing. Informatica UniPD).

  1. Installare RIOT 2023.04 nella macchina virtuale e compilare l'esempio hello-world per la scheda Arduino Mega 2560; provarlo sul simulatore Wokwi.
  2. Estendere un'applicazione RIOT con una shell e due comandi: board stampa il nome della scheda e cpu quello del processore.
  3. Compito. Aggiungere un comando echo che mostra le parole passate come argomenti (come echo in Linux): echo Hello world! stampa Hello world!. Compilare, provare e consegnare.

Teoria: Sistemi operativi embedded e real-time per l'IoTUn sistema embedded è un dispositivo con elettronica e software per una funzione specifica, spesso con vincoli di tempo reale; i microcontrollori (8, 16 o 32 bit, KB di RAM, senza MMU, mW) formano i sistemi profondamente embedded dell'IoT, divisi dall'IETF in classi 0, 1 e 2; i sistemi embedded hanno kernel configurabile, boot loader e root file system costruiti su un host con cross-compilazione; si può adattare un OS esistente (embedded Linux, uClinux senza MMU, Buildroot, Yocto) o usare un OS dedicato (TinyOS a componenti ed eventi, RIOT a microkernel con scheduler a priorità fissa preemptive senza tick, FreeRTOS, Zephyr, Contiki-NG con protothread); l'IoT ha requisiti di memoria minima, hardware eterogeneo, rete a basso consumo, efficienza energetica, tempo reale e sicurezza; nel laboratorio con RIOT si crea una shell, si leggono i thread con ps, si creano thread con priorità e si sincronizzano con un mutex.Sistemi operativi embedded e real-time per l'IoT →.


Nota. I programmi di questa pagina non sono stati compilati in questo lavoro: servono il sorgente di RIOT 2023.04 e la toolchain AVR (gcc-avr, avr-libc), che qui non ci sono. Il codice segue le istruzioni del laboratorio e le API documentate di RIOT; va provato nella macchina virtuale del corso.

Preparazione (passaggi del laboratorio)

  1. Spazio libero: almeno 500 MB (df -m /); si libera con sudo apt autoremove, sudo apt autoclean, sudo apt clean.
  2. Compilatore e librerie C per il microcontrollore: sudo apt install gcc-avr avr-libc.
  3. Sorgenti: scaricare RIOT 2023.04 (wget https://github.com/RIOT-OS/RIOT/archive/refs/tags/2023.04.zip) ed estrarlo (unzip 2023.04.zip): compare la cartella RIOT-2023.04.
  4. Compilazione: cd RIOT-2023.04/examples/hello-world, poi make BOARD=arduino-mega2560; se va a buon fine c'è bin/arduino-mega2560/hello-world.elf.
  5. Simulazione: aprire https://wokwi.com/projects/new/arduino-mega, premere F1 e scegliere "Upload Firmware and Start Simulation…", poi scegliere il file .elf: il simulatore stampa "Hello World" e altre informazioni su RIOT e sulla scheda.
  6. Per un'applicazione propria si compila dalla sua cartella con make BOARD=arduino-mega2560 RIOTBASE=percorso-di-RIOT (per esempio RIOTBASE=/home/debianos/RIOT-2023.04). Il percorso non deve contenere spazi, altrimenti make fallisce.

La shell (Makefile e main.c)

Makefile: si aggiunge il modulo della shell:

make
USEMODULE += shell

main.c: la shell si avvia con shell_run(comandi, buffer, dimensione). Si passa NULL al posto dei comandi per avere solo quelli di base (help elenca i comandi disponibili). Per aggiungere i propri comandi:

  1. una funzione per ogni comando, con la firma int nome(int argc, char **argv): argc è il numero di parole della riga (compreso il comando) e argv i puntatori alle parole (come nel main di un programma C);
  2. un vettore di shell_command_t: nome, descrizione e funzione, terminato da { NULL, NULL, NULL };
  3. la chiamata a shell_run con quel vettore.
c
#include <stdio.h>

#include "shell.h"

static int _board_handler(int argc, char **argv)
{
    (void)argc; /* non usati: evitano un avviso in compilazione */
    (void)argv;
    puts(RIOT_BOARD); /* macro predefinita: nome della scheda */
    return 0;
}

static int _cpu_handler(int argc, char **argv)
{
    (void)argc;
    (void)argv;
    puts(RIOT_CPU); /* macro predefinita: nome del processore */
    return 0;
}

/* echo: stampa gli argomenti separati da uno spazio */
static int _echo_handler(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) { /* argv[0] e' "echo" stesso */
        printf("%s", argv[i]);
        if (i + 1 < argc) {
            printf(" ");
        }
    }
    puts("");
    return 0;
}

static const shell_command_t shell_commands[] = {
    { "board", "Print the board name", _board_handler },
    { "cpu", "Print the cpu name", _cpu_handler },
    { "echo", "Print the arguments", _echo_handler },
    { NULL, NULL, NULL }
};

int main(void)
{
    puts("RIOT shell");

    char line_buf[SHELL_DEFAULT_BUFSIZE];
    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);

    return 0;
}

Come funziona

  • RIOT_BOARD e RIOT_CPU sono due macro definite dal sistema di compilazione di RIOT con il nome della scheda e della CPU per cui si compila (per la scheda, arduino-mega2560): sono stringhe e si stampano con puts.
  • La shell legge una riga da tastiera (nel simulatore, dal terminale seriale), la divide in parole e cerca il comando nel vettore: se lo trova chiama la funzione con argc e argv. Per echo Hello world! si ha argc = 3, argv[0] = "echo", argv[1] = "Hello", argv[2] = "world!"; il ciclo parte da i = 1 e stampa Hello world!.
  • shell_run non ritorna mai: il return 0 dopo la chiamata non viene eseguito, e tutto ciò che deve partire prima della shell (per esempio altri thread) va messo prima della chiamata (vedi Esercizio - Thread e mutex in RIOT con un buffer condiviso (laboratorio 12)).
  • Provare in Wokwi: help mostra i comandi (quelli di base e i tre aggiunti); board, cpu e echo Hello world! mostrano i risultati.

Errori tipici

  • Percorsi con spazi: make si interrompe.
  • Dimenticare USEMODULE += shell nel Makefile: shell.h non si trova.
  • Non terminare il vettore dei comandi con { NULL, NULL, NULL }: la shell scorre oltre la fine.
  • Partire da i = 0 nell'echo: si stamperebbe anche il nome del comando.
  • Mettere codice dopo shell_run aspettandosi che venga eseguito.

Versione ripasso

Procedura. sudo apt install gcc-avr avr-libc; scaricare e decomprimere RIOT 2023.04; make BOARD=arduino-mega2560 (con RIOTBASE=... per applicazioni proprie, percorsi senza spazi); caricare l'.elf su Wokwi (F1, "Upload Firmware and Start Simulation").

Shell. USEMODULE += shell; #include "shell.h"; comandi int f(int argc, char **argv); vettore shell_command_t {nome, descrizione, funzione} terminato da {NULL, NULL, NULL}; shell_run(comandi, line_buf, SHELL_DEFAULT_BUFSIZE); NULL == solo i comandi di base (help). RIOT_BOARD, RIOT_CPU.

echo. Ciclo da i = 1 fino a argc - 1, spazio fra le parole, a-capo finale. shell_run non ritorna: tutto il resto prima.

Non compilato qui (servono RIOT e la toolchain AVR).

Passi. (1) installare gcc-avr, avr-libc e RIOT 2023.04; (2) USEMODULE += shell nel Makefile; (3) #include "shell.h" e tre funzioni int f(int argc, char **argv) (board: puts(RIOT_BOARD), cpu: puts(RIOT_CPU), echo: ciclo da i = 1 con printf("%s", argv[i]) e spazi); (4) vettore shell_commands[] terminato da {NULL, NULL, NULL}; (5) shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); (6) make BOARD=arduino-mega2560 RIOTBASE=... e Wokwi: help, board, cpu, echo Hello world!.

Errori tipici: spazi nel percorso; modulo shell mancante; vettore non terminato; ciclo da 0; codice dopo shell_run.

Teoria: Sistemi operativi embedded e real-time per l'IoTUn sistema embedded è un dispositivo con elettronica e software per una funzione specifica, spesso con vincoli di tempo reale; i microcontrollori (8, 16 o 32 bit, KB di RAM, senza MMU, mW) formano i sistemi profondamente embedded dell'IoT, divisi dall'IETF in classi 0, 1 e 2; i sistemi embedded hanno kernel configurabile, boot loader e root file system costruiti su un host con cross-compilazione; si può adattare un OS esistente (embedded Linux, uClinux senza MMU, Buildroot, Yocto) o usare un OS dedicato (TinyOS a componenti ed eventi, RIOT a microkernel con scheduler a priorità fissa preemptive senza tick, FreeRTOS, Zephyr, Contiki-NG con protothread); l'IoT ha requisiti di memoria minima, hardware eterogeneo, rete a basso consumo, efficienza energetica, tempo reale e sicurezza; nel laboratorio con RIOT si crea una shell, si leggono i thread con ps, si creano thread con priorità e si sincronizzano con un mutex.Sistemi operativi embedded e real-time per l'IoT →.

Esercizi su questo argomento

Teoria collegata