Commutateur de Démarrage Alimenté par Pico : Choisissez Votre OS Avant la Mise Sous Tension
Pré-choisissez l'OS à démarrer, même avant d'allumer l'ordinateur, en basculant un interrupteur. Vous n'avez plus à attendre pour sélectionner l'OS.
Histoire
En parcourant Hackaday.io pour des projets, je suis tombé sur ce projet (clic) de Stephen Holdaway. Dans ce projet, il a résolu une tâche frustrante rencontrée par chaque utilisateur de dual-boot, qui est de s'asseoir et attendre pour choisir l'OS (Windows) depuis le menu GRUB chaque fois qu'on veut basculer vers Windows. Il a réussi à ajouter un interrupteur matériel pour déterminer l'OS à démarrer à chaque fois que l'ordinateur est allumé.
Il a réalisé cela en configurant un microcontrôleur STM32 comme périphérique de stockage de masse USB. Il a documenté tout son parcours à travers la recherche et l'implémentation du projet dans son post Hackaday (clic). Veuillez consulter son post pour une meilleure compréhension des implémentations.
Dans ce projet, je vais montrer comment j'ai réussi à porter les changements sur Raspberry Pi Pico. Vous pouvez trouver ma version dans ce dépôt GitHub (Clic).
Concept
GNU GRUB est un programme qui s'exécute avant le chargement de tout système d'exploitation. À travers ce menu, nous pouvons sélectionner quel OS charger. GRUB offre des modules très limités pour travailler. Cela signifie qu'il ne peut pas lire de données depuis un microcontrôleur connecté via USB. Mais il peut lire des données depuis des disques de stockage.
Nous pouvons donc tromper GRUB pour qu'il lise des données depuis le microcontrôleur, en énumérant notre micro en tant que périphérique de stockage de masse.
Ainsi, nous énumérons notre Raspberry Pi Pico en tant que périphérique de stockage de masse, via la bibliothèque tinyUSB, qui aura un fichier switch.cfg, dans lequel le Pico écrira la position de l'interrupteur, c.-à-d. 1 pour ON, 0 pour OFF.
Nous devons ajouter un script dans GRUB, dont la fonction est de lire le fichier switch.cfg et de définir le default sur 0 (Ubuntu) / 2 (Windows).
Quand GRUB charge, il exécute nos scripts personnalisés, qui à leur tour recherchent notre périphérique par ses identifiants UUID, et s'il existe, lisent le fichier switch.cfg. Après avoir obtenu la position de l'interrupteur, il définit la sélection d'OS par défaut respectivement.
En résumé :
- Le Pico se configure en tant que périphérique de stockage de masse.
- Le menu GRUB appelle notre script et demande le fichier particulier.
- Le Pico répond à la demande de lecture en ajoutant la position de l'interrupteur dans le fichier switch.cfg.
- Le script dans GRUB extrait l'info du fichier et définit l'option par défaut à partir des données extraites.
Configurer le Pico en Périphérique de Stockage de Masse
J'ai utilisé l'exemple cdc_msc de tinyUSB pour y parvenir. L'exemple configure le Pico en tant que périphérique de stockage de masse et crée un système de fichiers FAT12 et énumère un fichier README.txt.
J'ai changé README.txt en switch.cfg et ajouté la ligne « set os_hw_switch=0\n » au fichier.
#define SWITCH_CFG_CONTENTS \
"set os_hw_switch=0\n"
...
//------------- Bloc 3 : Contenu Readme -------------//
SWITCH_CFG_CONTENTS
Maintenant, nous avons configuré le Pico en tant que périphérique de stockage de masse. Après avoir copié le fichier uf2 sur le Pico, il s'énumère comme un périphérique de stockage. Nous aurons besoin de l'UUID du périphérique pour le script GRUB, qui est UUID="0000-1234".
$ sudo blkid
...
/dev/sda: SEC_TYPE="msdos" LABEL_FATBOOT="TinyUSB MSC" LABEL="TinyUSB MSC" UUID="0000-1234" BLOCK_SIZE="512" TYPE="vfat"
Circuit
Lire la Position de l'Interrupteur et Écrire dans le Fichier
Maintenant, nous devons lire la position de l'interrupteur et changer le contenu du fichier switch.cfg en conséquence, c.-à-d. :
- si l'interrupteur est ON : set os_hw_switch=1\n
- si l'interrupteur est OFF : set os_hw_switch=0\n
J'ai utilisé GPIO_PIN 28 comme broche d'interrupteur, qui est configurée en pull-down.
read_switch_value retourne la position de l'interrupteur, c.-à-d. '1' est on (tiré haut) et '0' est off (tiré bas).
//-------------------------main.c---------------------
#define SWITCH_PIN 28
// lire la valeur de l'interrupteur
uint8_t read_switch_value()
{
return gpio_get(SWITCH_PIN) ? '1' : '0';
}
int main(void)
{
gpio_init(SWITCH_PIN);
// configurer la broche en ENTRÉE
gpio_set_dir(SWITCH_PIN, false);
// configurer la broche en PULL_DOWN
gpio_set_pulls(SWITCH_PIN, false, true);
Pour écrire la position de l'interrupteur dans switch.cfg, j'ai utilisé readGRUBConfig() qui appelle la fonction read_switch_value, et définit le tampon de sortie avec la position de l'interrupteur.
J'ai découvert que lors de la lecture du troisième bloc3, lba est défini sur 3, donc j'intercepte l'appel et j'appelle readGrubConfig en passant le tampon où le contenu du fichier sera copié.
//-------------------------msc_disk.c---------------------
static char grubConfigStr[] = "set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
// Modifier la chaîne de config avec la valeur actuelle de l'interrupteur
grubConfigStr[sizeof(grubConfigStr)-3] = read_switch_value();
memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}
// Callback invoqué quand une commande READ10 est reçue.
// Copie les données du disque vers le tampon (jusqu'à bufsize) et retourne le nombre d'octets copiés.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
// lors de la lecture du fichier
if(lba == 3){
readGrubConfig(buffer);
return bufsize;
}
...
...
}
Compiler le Code du Pico
Nous devons ajouter pico stdlib à notre code pour obtenir l'accès aux broches gpio.
//-------------------------main.c-----------------------------------
#include
#include
#include
#include "bsp/board.h"
#include "tusb.h"
...
#include "pico/stdlib.h"
Pour construire le projet :
$ mkdir build
$ cd build
$ cmake ..
$ make
Configurer GRUB pour Lire le Contenu du Fichier
J'ai ajouté ces changements dans mon Ubuntu 20.10.
$ sudo vim /etc/grub.d/40_custom
....
# Rechercher le périphérique commutateur matériel par son ID de système de fichiers codé en dur
search --no-floppy --fs-uuid --set hdswitch 0000-1234
# Si trouvé, lire le fichier de config dynamique et sélectionner l'entrée appropriée pour chaque position
if [ "${hdswitch}" ] ; then
source ($hdswitch)/switch.cfg
if [ "${os_hw_switch}" == 0 ] ; then
# Démarrer Linux
set default="0"
elif [ "${os_hw_switch}" == 1 ] ; then
# Démarrer Windows
set default="2"
else
# Repli par défaut
set default="${GRUB_DEFAULT}"
fi
else
set default="${GRUB_DEFAULT}"
fi
D'abord, nous recherchons notre système de fichiers. GRUB a une sous-commande search juste pour cela.
- -no-floppy option empêche la recherche sur les lecteurs de disquettes
- -fs–uuid 0000-1234 recherche un système de fichiers avec UUID 0000-1234.
Si un périphérique est trouvé, le premier périphérique trouvé est défini comme valeur de la variable d'environnement.
–set hdswitchhdswitch est notre variable d'environnement et est définie avec le nom du disque s'il est trouvé.
Ensuite, nous chargeons le fichier switch.cfg si la variable hdswitch est définie, ce qui crée une autre variable d'environnement os_hw_switch avec la position de l'interrupteur, c.-à-d. soit 0/1.
Nous lisons la valeur de os_hw_switch et définissons le default sur 0 ou 2 respectivement. 0 car Ubuntu est à la position 0 et Windows à la position 2 dans le menu GRUB.
Enfin, si hdswitch n'était pas défini, nous définissons le default sur GRUB_DEFAULT.
Maintenant, nous devons mettre à jour notre grub :
$ sudo update-grub
Source : Hardware Boot Select Switch Using Pico
Processus de fabrication
- Maîtriser l'instruction Switch C# : Syntaxe, exemples et bonnes pratiques
- Construction d'une station météo extérieure avec Raspberry Pi 2 et ADS‑WS1
- Système de contrôle de température basé LM35 avec microcontrôleur AT89S52
- Surveillance météo à distance avec Raspberry Pi 3 et PubNub
- Intégration du TI SensorTag à Blynk via Node‑RED sur Raspberry Pi Zero
- Contrôler une LED avec un capteur de mouvement PIR sur Raspberry Pi
- Construire un robot intelligent avec le Bridge Shield – Intégration Raspberry Pi & Arduino
- Construire un robot Raspberry Pi piloté Wi-Fi avec Python – Guide pas à pas
- Hacking hardware embarqué pour appareils IoT : outils, techniques et implications business