Pico-aangedreven Boot-switch: Kies je OS voordat je aanzet
Pre-kies het OS om te booten, nog voordat de computer aanstaat, door een schakelaar te togglen. Nu hoef je niet meer te wachten om de OS te selecteren.
Verhaal
Rondwanderend op Hackaday.io voor projecten, stuitte ik op dit project(klik) door Stephen Holdaway. In dit project loste hij een frustrerende taak op die elke dual-boot-gebruiker kent: zitten en wachten om de OS (Windows) te kiezen uit het GRUB-menu elke keer dat we willen switchen naar Windows. Hij kon een hardware-schakelaar toevoegen om het te booten OS te bepalen elke keer dat de computer aangezet wordt.
Hij bereikte dit door een STM32-microcontroller te configureren als een USB-massastorage-apparaat. Hij heeft zijn hele reis gedocumenteerd door het onderzoek en de implementatie van het project in een Hackaday-post(klik). Ga door zijn post voor een beter begrip van de implementaties.
In dit project laat ik zien hoe ik de wijzigingen naar de Raspberry Pi Pico heb geport. Je kunt mijn versie in deze GitHub Repo (Klik) vinden.
Concept
GNU GRUB is een programma dat draait voordat enig Operating System geladen wordt. Via dit menu kunnen we selecteren welk OS te laden. GRUB biedt zeer beperkte modules om mee te werken. Dit betekent dat het geen data kan lezen van een microcontroller verbonden via USB. Maar het kan data lezen van opslagschijven.
Dus we kunnen GRUB bedriegen om data van de microcontroller te lezen, door onze micro te enumereren als een massastorage-apparaat.
Daarom enumereren we onze Raspberry Pi Pico als een massastorage-apparaat, via de tinyUSB-bibliotheek, die een bestand switch.cfg zal hebben, waarnaar de Pico de schakelaarpositie schrijft, namelijk 1 voor AAN 0 voor UIT.
We moeten een script in GRUB toevoegen, dat functies heeft om het switch.cfg-bestand te lezen en de default in te stellen op 0(Ubuntu )/2(Windows).
Wanneer GRUB laadt, draait het onze custom scripts, die op hun beurt zoeken naar ons apparaat aan de hand van UUID-identificaties, en als het bestaat, het switch.cfg-bestand lezen. Na het verkrijgen van de schakelaarpositie, stelt het de default OS-selectie respectievelijk in.
Samenvattend,
- Pico zal zichzelf configureren als een massastorage-apparaat.
- GRUB-menu roept ons script aan en vraagt om het specifieke bestand.
- Pico reageert op de leesverzoek door de schakelaarpositie in het switch.cfg-bestand toe te voegen.
- Het script in GRUB extraheert de info uit het bestand en stelt de default-optie in vanuit de geëxtraheerde data.
Pico Configureren als Massastorage-apparaat
Ik heb het cdc_mscvoorbeeld van tinyUSB gebruikt om dit te bereiken. Het voorbeeld configureert de Pico als een massastorage-apparaat en creëert een FAT12-bestandssysteem en enumereert een README.txt-bestand.
Ik veranderde README.txt naar switch.cfg en voegde de regel "set os_hw_switch=0\n" toe aan het bestand.
#define SWITCH_CFG_CONTENTS \
"set os_hw_switch=0\n"
...
//------------- Block3: Readme Content -------------//
SWITCH_CFG_CONTENTS
Nu hebben we de Pico geconfigureerd als een massastorage-apparaat. Na het kopiëren van het uf2-bestand naar de Pico, enumereert het als een opslagapparaat. We zullen de UUID-id van het apparaat nodig hebben voor het GRUB-script, wat 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"
Schakeling
Schakelaarpositie Uitlezen en naar Bestand Schrijven
Nu moeten we de schakelaarpositie uitlezen en de inhoud van het switch.cfg-bestand dienovereenkomstig wijzigen, namelijk
- als de schakelaar AAN staat: set os_hw_switch=1\n
- als de schakelaar UIT staat: set os_hw_switch=0\n
Ik heb GPIO_PIN 28 gebruikt als de schakelaarpin, ingesteld op pull-down.
read_switch_value retourneert de schakelaarpositie, namelijk '1' is aan (pulled high) en '0' is uit (pulled low).
//-------------------------main.c---------------------
#define SWITCH_PIN 28
// read switch value
uint8_t read_switch_value()
{
return gpio_get(SWITCH_PIN) ? '1' : '0';
}
int main(void)
{
gpio_init(SWITCH_PIN);
//configure pin as INPUT
gpio_set_dir(SWITCH_PIN, false);
//configure pin as PULL_DOWN
gpio_set_pulls (SWITCH_PIN,false,true);
Om de schakelaarpositie naar switch.cfg te schrijven, heb ik readGRUBConfig() gebruikt wat de read_switch_value-functie aanroept, en de output-buffer instelt met de schakelaarpositie.
Ik ontdekte dat bij het lezen van het derde blok3 lba ingesteld is op 3, dus de call afvangen en readGrubConfig aanroepen en de buffer doorgeven waar de inhoud van het bestand naar gekopieerd wordt.
//-------------------------msc_disk.c---------------------
static char grubConfigStr[] = "set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
// Config-string wijzigen met huidige schakelaarwaarde
grubConfigStr[sizeof(grubConfigStr)-3] = read_switch_value();
memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}
// Callback aangeroepen wanneer READ10-commando ontvangen.
// Kopieer schijfdata naar buffer (tot bufsize) en retourneer aantal gekopieerde bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
// wanneer het bestand gelezen wordt
if(lba == 3){
readGrubConfig(buffer);
return bufsize;
}
...
...
}
De Pico-code Compileren
We moeten pico stdlib aan onze code toevoegen om gpio-pintoegang te krijgen.
//-------------------------main.c-----------------------------------
#include
#include
#include
#include "bsp/board.h"
#include "tusb.h"
...
#include "pico/stdlib.h"
Om het project te maken:
$ mkdir build
$ cd build
$ cmake ..
$ make
GRUB Configureren om de Bestandsinhoud te Lezen
Ik heb deze wijzigingen in mijn Ubuntu 20.10 toegepast.
$ sudo vim /etc/grub.d/40_custom
....
# Zoek naar hardware-schakelaarapparaat via zijn hardcoded filesystem-ID
search --no-floppy --fs-uuid --set hdswitch 0000-1234
# Als gevonden, lees dynamisch config-bestand en selecteer passende entry voor elke positie
if [ "${hdswitch}" ] ; then
source ($hdswitch)/switch.cfg
if [ "${os_hw_switch}" == 0 ] ; then
# Boot Linux
set default="0"
elif [ "${os_hw_switch}" == 1 ] ; then
# Boot Windows
set default="2"
else
# Fallback naar default
set default="${GRUB_DEFAULT}"
fi
else
set default="${GRUB_DEFAULT}"
fi
Eerst zoeken we naar ons bestandssysteem. GRUB heeft een subcommando search speciaal hiervoor.
- -no-floppy optie voorkomt zoeken naar floppy-apparaten
- -fs–uuid 0000-1234 zoekt een bestandssysteem met UUID van 0000-1234.
Als enig apparaat gevonden wordt, wordt het eerste gevonden apparaat ingesteld als waarde van de omgevingsvariabele.
–set hdswitchhdswitch is onze omgevingsvariabele en wordt ingesteld met schijfnaam als gevonden.
Vervolgens sourcen we het switch.cfg-bestand als de hdswitch-variabele ingesteld is, wat een andere omgevingsvariabele os_hw_switch creëert met de schakelaarpositie, namelijk 0/1.
We lezen de waarde van os_hw_switch en stellen de default in op 0 of 2 respectievelijk. 0 omdat Ubuntu op 0de positie staat en Windows op 2de positie in het GRUB-menu.
Tot slot, als hdswitch niet ingesteld was, stellen we de default in op GRUB_DEFAULT.
Nu moeten we onze GRUB updaten:
$ sudo update-grub
Bron: Hardware Boot Select Switch Using Pico
Productieproces
- Uitgebreide Gids tot Moderne Epilatieapparaten: Ontwerp, Geschiedenis en Toekomst
- Een buitenweerstation bouwen met Raspberry Pi 2 en ADS-WS1
- LM35-gebaseerd temperatuurregelsysteem met AT89S52 microcontroller
- Externe weerbewaking met Raspberry Pi 3 en PubNub
- TI SensorTag integreren met Blynk via Node-RED op Raspberry Pi Zero
- Een LED Besturen met een PIR-bewegingssensor op Raspberry Pi
- Bouw een Slimme Robot met de Bridge Shield – Raspberry Pi & Arduino Integratie
- Bouw een Wi-Fi-gecontroleerde Raspberry Pi Robot met Python – Stap-voor-stap Gids
- Embedded Hardware Hacken voor IoT-apparaten: Tools, Technieken en Zakelijke Implicaties
- De C# Switch Statement: Syntaxis, Voorbeelden & Best Practices
- C# using-statements: imports, aliasen en static directives