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).
- Installare RIOT 2023.04 nella macchina virtuale e compilare l'esempio
hello-worldper la scheda Arduino Mega 2560; provarlo sul simulatore Wokwi. - Estendere un'applicazione RIOT con una shell e due comandi:
boardstampa il nome della scheda ecpuquello del processore. - Compito. Aggiungere un comando
echoche mostra le parole passate come argomenti (comeechoin Linux):echo Hello world!stampaHello world!. Compilare, provare e consegnare.
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)
- Spazio libero: almeno 500 MB (
df -m /); si libera consudo apt autoremove,sudo apt autoclean,sudo apt clean. - Compilatore e librerie C per il microcontrollore:
sudo apt install gcc-avr avr-libc. - 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 cartellaRIOT-2023.04. - Compilazione:
cd RIOT-2023.04/examples/hello-world, poimake BOARD=arduino-mega2560; se va a buon fine c'èbin/arduino-mega2560/hello-world.elf. - Simulazione: aprire
https://wokwi.com/projects/new/arduino-mega, premereF1e scegliere "Upload Firmware and Start Simulation…", poi scegliere il file.elf: il simulatore stampa "Hello World" e altre informazioni su RIOT e sulla scheda. - Per un'applicazione propria si compila dalla sua cartella con
make BOARD=arduino-mega2560 RIOTBASE=percorso-di-RIOT(per esempioRIOTBASE=/home/debianos/RIOT-2023.04). Il percorso non deve contenere spazi, altrimentimakefallisce.
La shell (Makefile e main.c)
Makefile: si aggiunge il modulo della shell:
USEMODULE += shellmain.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:
- una funzione per ogni comando, con la firma
int nome(int argc, char **argv):argcè il numero di parole della riga (compreso il comando) eargvi puntatori alle parole (come nelmaindi un programma C); - un vettore di
shell_command_t: nome, descrizione e funzione, terminato da{ NULL, NULL, NULL }; - la chiamata a
shell_runcon quel vettore.
#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_BOARDeRIOT_CPUsono 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 conputs.- 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
argceargv. Perecho Hello world!si haargc = 3,argv[0] = "echo",argv[1] = "Hello",argv[2] = "world!"; il ciclo parte dai = 1e stampaHello world!. shell_runnon ritorna mai: ilreturn 0dopo 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:
helpmostra i comandi (quelli di base e i tre aggiunti);board,cpueecho Hello world!mostrano i risultati.
Errori tipici
- Percorsi con spazi:
makesi interrompe. - Dimenticare
USEMODULE += shellnelMakefile:shell.hnon si trova. - Non terminare il vettore dei comandi con
{ NULL, NULL, NULL }: la shell scorre oltre la fine. - Partire da
i = 0nell'echo: si stamperebbe anche il nome del comando. - Mettere codice dopo
shell_runaspettandosi 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.