How to jumper-select configuration/boot options for the Raspberry Pi

True.

However I don’t think I will need 32 operating system versions on-hand any time soon. :wink:

Not to mention that I’d have to read ALL the switches and build a binary number from strings, then go nuts on it.

Right now they’re being used as “select bits”. The first “0” found boots that O/S, regardless of what any subsequent switch is set to.

One thing I am thinking about is using the last, unused, switch to select either a 32 bit or 64 bit kernel for those O/S’s that support it.  I’m not sure how I would do that, though I have an idea. . . .

I had assumed (I should learn not to do that) that the pins could be read as essentially T/F values (0 or 1), So it wouldn’t take much longer to read all of them than to read them sequentially and then stopping on the first one flipped. Even if you can’t read them that way, would still be straightforward. In pseudo-code something like

Flags = 0
If pin 1 = "on" then Flags += 16
If pin 2 = "on" then Flags += 8
If pin 3 = "on" then Flags += 4
If pin 4 = "on" then Flags += 2
if pin 5 = "on" then Flags += 1

This will quickly get you a value from 0 - 31 that you then compare against for your settings. Even if you only are using 4 or 5 choices initially it can help you make sure more then one switch isn’t thrown inadvertently.

/K

Damn it all!  I didn’t think of that method, I was going to build a binary number concatenating strings, but your method is much more straightforward.  Not to mention easier to read and makes more sense.

Happy to help. That’s the value of a forum community - the sharing of ideas.

And obviously you could list the switches in whichever order makes sense for the physical lay-out. Or have 3 pins define one variable and the other two pins define another; or 4 and 1. Lots of options.

Have fun.
/K

1 Like

As if my head weren’t spinning fast enough already!

Actually, one of the things I was considering, as I mentioned above, is using the last pin as a “mode” switch.

For example:
Positions 1-4 select one of four O/S’s to boot.
Position 5 selects either the standard 32 bit kernel or the optional 64 bit kernel, on every O/S except the Raspbian 64-bit image.

How I would do that?
Positions 1-4 would be selected as they are now via PINN.
Position 5 would not be read at boot-time by PINN, but would become part of the OS’s individual config.txt files after PINN has booted into them. I would place a conditional section that would select the 64 bit kernel, depending on the position of switch 5.

“Da bitch part”, (as they said in Blazing Saddles), is figuring out a way to make the display non-ambiguous.

I’d want some kind of an overlay on the screen that would tell me what kernel mode I’m in. . . Gnarly, but I’m sure it’s doable. If they can get to the moon, land, take off again, and get home safely with no more computing power than a pocket calculator, I’m sure I can figure out a way to do a screen overlay! :wink:

This is why when I do something, even if I think it’s so bizarre that people will wonder what drugs I am taking - or so outrageous that no one in their right, (or left!), mind would even consider it. . . I always want to put it here.

(P.S. I put it up on the PINN sticky, and posted it on the Raspberry Pi forums too.)

I have discovered that some of the odd-ball, one-off, “since I have nothing better to do” projects end up having serious implications. It appears that dozens of people come out of the woodwork and say, “I’ve been trying to figure that out since [choose geological epoc]!!”

Sheesh!

1 Like

Another update!

This has been accomplished.

What I did:

  1. Removed, (commented out), the configuration for the last pin in the PINN partition’s config.txt. (PINN’s partition is the first one)

  2. Removed that pin from the selection list in the pinn_init.sh script in the PINN partition so that the GPIO pin associated with switch 5 is no longer being used.

  3. Created a customized variant of the config.txt script that is put into each of the boot partitions for each operating system (except for the Raspbian 64 bit experimental O/S, which is already 64 bit.)

Here are the relevant parts:

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# Enable monitoring switch #5 on pin 36, (Broadcomm pin 16)
gpio=16=ip,pu

As mentioned before, this allows us to read the signal on physical pin 36, (Broadcomm pin 16), to see if it is is a zero or a one.
 

[all]

# If switch 5 is set, use 64 bit kernel
[gpio16=0]
arm_64bit=1

And this is what does the work.

Any statement within square brackets is a conditional statement.  For example, the part of the config.txt that says [pi4] is ONLY read if the board is a Pi-4 board.  Later on, there’s an [all] block that “ends” the [pi4] block by indicating that this applies to all versions of Raspberry Pi.

Of course, the beginning of the config.txt, since it doesn’t have a qualifier, applies to everybody.  However, once you DO qualify by type, you need to remember to “un-qualify” by adding the [all] block for anything else that follows.

This particular section, [gpio16=0], checks to see “if” pin 16 is a zero (switch selected) or not.  If the switch IS selected, (pin 16 is a logical zero), then it sets the kernel “bitness” to 64 bits by enabling the experimental 64 bit kernel.

In fact, you can do that with any available GPIO pin you want, and use it to do whatever you might need it to do.  For example you could use a dedicated/unused GPIO pin to select between a built-in display and an external HDMI display - or to enable or disable something else.

There’s a lot going on “under the hood” so to speak, and there’s a lot of fun to be had there.

Here’s the entire config.txt file I use.  It has a few other tweaks like enabling the overlay for the Adafruit real-time clock circuit I have installed.

config.txt (2.5 KB)

1 Like

Update:

It turns out that this idea has more aspects than a cat has hair!

Note:
These results are after I started a new experiment where I hand-copy, (via rsync), the data to separate root partitions and the boot partitions to subdirectories in the main boot partition.  This is an alternative to PINN, which uses a simpler organization that is easier to understand.

Viz.: Raspberry Pi 4 USB dual boot

==================

The Raspberry Pi is NOT particularly well designed for a multi-boot environment, being particularly sensitive to its boot and root partition configurations.  Anything other than a plain-vanilla two-partition SD card boot is risking “interesting” behaviors and run-time artifacts that don’t appear in the standard SD card install - even using the exact same files.

It also appears that the boot-time documentation over at Raspberry Pi is, by their own admission, woefully inadequate.  They “talk around” it, but never really say things straight out.

I posted a bug-comment over on the Pi forums, (https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=311192), and it spawned a bit of discussion.

Not the least of which is that the config.txt file is actually read and processed twice - once by the binary-blob loader, (bootloader.bin or the installed firmware on the Pi-4), and then read again when the associated start[x].elf is loaded.

What that means is that certain directives cannot be in a second-stage config file, (that is, aything referenced by an “include” directive), and one of them is start_x=1 that enables the camera.

There are certain other directives that can only be in the first-stage boot.  (i.e.  NOT in an “included” file.)

One of the Fountains of Wisdom over at Raspberry Pi had this to say:

Elsewhere in that thread:

I am still trying to determine what specific boot files, (executables, overlays, etc.), need to be included in the first-stage boot directory before passing control off to the second-stage of the boot process.  I am also trying to figure out what executables and/or overlays, (etc.), get skipped when in a second-stage boot directory.

I ended up solving the problem of having certain things in the config.txt file work on the first pass, other things work on the second pass, and nobody really knows for sure who is what or where, (who’s on first?), by combining all my config files into one unified whole.  I took all the separate config files for each O/S, diff’d them, and filtered out those things that were critically O/S specific from those things which were generic.

I put all the O/S specific stuff in the individual GPIO select statements, and left the rest as is.  I am also discovering that, (to some extent), order is important because later statements can override earlier statements, so you have to pull out defaults and set them first - so that later statements can override them as needed.

Here’s what my, (latest?  :wink:), config.txt looks like.

# Universal multi-boot config.txt
# Version 1.0 - 2021-05-05.
#  Changed:
#  *  Included all separate config files into this one and filtered out the
#     O/S specific data
#  *  Modified the system crontab on all O/S's to remove OS specific filenames.
#     The system crontab now copies the master config.txt file,
#     (in the root of the boot partition), to the individual
#     second-stage boot sub-directory for that O/S on boot-up.
#     It also copies the individual O/S config.txt back to the master
#     once a minute in case it gets changed by something/someone else.

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

#==========================================
# These lines enable GPIO Multi-Boot capability
# Setup GPIO pins, set as inputs, (ip), pulled high, (pu).
#
# Pin/dip-switch mapping for GPIO O/S selection:
# Dip-switch   Broadcomm pin   Physical pin	    Function
#    1		   21		   40		Raspbian for Robots (see switch 5)
#    2		   26		   37		GoPiGo O/S (see switch 5)
#    3		   20		   38		Raspbian 32-bit (see switch 5)
#    4		   19		   35		Raspbian 64-bit
#    5		   16		   36		"Bitness" selector. Selected = 64-bit kernel
#						"Bitness" is selected in the individual O/S
#						config.txt file. (e.g. R4R_config.txt, etc.)
#  common	 ground		   39

gpio=21=ip,pu
gpio=26=ip,pu
gpio=20=ip,pu
gpio=19=ip,pu
gpio=16=ip,pu
#=============================================

#=============================================
# These lines do O/S selection based on the dip-switch position
# as noted above
#
# Enable experimental 64-bit kernel if dip-switch 5 is flipped.
# I do this first so that it doesn't cause issues.
[gpio16=0]
arm_64bit=1
[all]

# O/S #1 is raspbian for robots
[gpio21=0]
os_prefix=R4R_boot/
[all]

# O/S #2 is GoPiGo O/S 3.0.0
[gpio26=0]
os_prefix=GPG_Boot/
[all]

# O/S #3 is Raspbian 32 bit
[gpio20=0]
os_prefix=Raspbian32/
[all]

# O/S #4 is Raspbian 64 bit
[gpio19=0]
os_prefix=Raspbian64/
arm_64bit=1
[all]
#
#==============================================

#  The rest of this is the "generic" config.txt

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
#disable_overscan=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
# This is necessary for headless VNC mode.
hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
hdmi_group=2
hdmi_mode=39

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

# Enable Adafruit RTC modules
# Uncomment the line that corresponds to
# the module, (clock chip), you're using
#dtoverlay=i2c-rtc,ds1307
#dtoverlay=i2c-rtc,pcf8523
dtoverlay=i2c-rtc,ds3231

[pi4]
# Enable DRM VC4 V3D driver on top of the dispmanx display stack
dtoverlay=vc4-fkms-v3d
max_framebuffers=2
[all]

dtoverlay=vc4-fkms-v3d
start_x=1
gpu_mem=128
dtparam=i2c1=on
enable_uart=1
gpio=8=op,dh

My ultimate goal is to not only successfully multi-boot, (which I have done), but to multi-boot accurately enough that I can confidently write bugs against it.

2 Likes

Turns out that there’s an even simpler way: Dex’s eyes.

I’m sure I posted this somewhere, but I forgot where.

What I did was modify my startup script to include:

    # check to see which kernel is being run
    os_version = os.popen('uname -m').read()
    print("os_version is", os_version)
    if os_version == "aarch64\n":
        logging.info("Operating System Kernel Version is 64 Bit\n")
        logging.info("Eyes are Blue\n")
        gopigo3_robot.left_eye_color = (0, 0, (brightness + 5))  # set eyes blue if 64 bit
        gopigo3_robot.right_eye_color = (0, 0, (brightness + 5)) # add ofset because blue is not as bright
    else:
        logging.info("Operating System Kernel Version is 32 Bit\n")
        logging.info("Eyes are Green\n")
        gopigo3_robot.left_eye_color = (0, brightness, 0)  # otherwise, set to green if 32 bit
        gopigo3_robot.right_eye_color = (0, brightness, 0)

    gopigo3_robot.open_eyes()  # enable eyes.

. . . and I get either green, (32 bit OS), or blue (64 bit OS) when I boot either GoPiGo OS or Raspbian for Robots.

“brightness” is a variable set at the beginning of the python script and is set to 10 - the same, (apparent), brightness as the other LED’s.

Of course if I, (or anything else), does something with Dex’s eyes, there goes my nice indicator! :wink:

2 Likes

Update:

I remember a time when I was taking freshman Calculus - the professor had filled the blackboard, (yes, real slate blackboards), with something that looked like it zoned in from Mars - a mangled mess of formulae and operations that would cross Einstein’s eyes!

He than said “It is intuitively obvious that. . . [something or other]”, did a few squiggles, and the entire jumbled mess collapsed in on itself like a black hole, leaving a trivial answer - something like 3 and a half.

That just happened here.

Both Procount and I have been fighting this monster since, ah, well, I don’t remember. But it was before Christmas! Maybe even since last whenever?

Yesterday, another individual, (cleverca22), while digging through the code for things like the binary blob, (the firmware files, etc.), discovered a (somewhat) hidden directive: “boot_partition=” that only works in a special kind of config.txt file called “autoboot.txt”.

“boot_partition=[x]” causes an immediate jump to that partition:

  • It must be a FAT32 partition.
  • It must contain valid boot files as if it were the first partition.
  • It must have an associated root partition.
     

Once it makes that jump, the Raspberry Pi forgets that it even booted anywhere else - it treats that partition as if it were the only boot partition in the world and has a high ole’ time.

Important notes:

  1. This assumes a PINN/NOOBS partition layout.  I suspect this was part of what was created to support NOOBS in the beginning.
  2. The PINN/NOOBS partition layout has, (among other things), pairs of partitions that represent each operating system, one FAT-32 partition representing the boot partition of the pair, and another ext4 partition that is the root partition of the pair.
     

The “boot_partition=” directive takes a partition number that represents the boot partition of the pair and executes it directly.

The result is that it absolutely trivializes the process, taking it from a mangled mess and reducing it to something simple enough for a wannabe punter like me to understand.

I detail my results on the PINN sticky here:
https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=142574&p=1863389#p1863370

Now all I have to do is clean up all the mess I made when I tried cramming all of this into something that works! :wink:

To give you an example of how stupidly simple this is - here is the autoboot.txt file that replaces many, many kb of pinn_init.conf files:

#  autoboot.txt file to automatically boot based on a GPIO pin selection

# Setup GPIO pins, set as inputs, (ip), pulled high, (pu).
#
# Pin/dip-switch mapping for GPIO O/S selection:
# Dip-switch   Broadcomm pin   Physical pin
#    1		   21		   40
#    2		   26		   37
#    3		   20		   38
#    4		   19		   35
#    5		   16		   36
#  common	 ground		   39

gpio=21=ip,pu
gpio=26=ip,pu
gpio=20=ip,pu
gpio=19=ip,pu
gpio=16=ip,pu  # reserving this pin for O/S bitness selection

# GPIO pin 16 is tested within the individual O/S config.txt files and enables the 64 bit kernel if low.

[gpio21=0]
# Raspbian for Robots
boot_partition=6
[all]

[gpio26=0]
# GoPiGo O/S
boot_partition=8
[all]

[gpio20=0]
# Raspberry Pi O/S (32 bit)
boot_partition=10
[all]

[gpio19=0]
# Raspberry Pi O/S (64 bit)
boot_partition=12
[all]

And that’s it.  100% of it.  Nothing else to do.

No boot delay while PINN boots and runs a complicated script.

It goes through the selection process like chlorine triflouride goes through cement, and it doesn’t even pause long enough to say “Hello!” before booting the selected O/S.  :wink:

1 Like

I’ll bet it felt like you were doing this:
tenor

And then someone showed you:
tenor (1)

:robot:
/K

2 Likes

Oh yea!

I love the pictures by the way

One interesting artifact of this is that something like PINN is not necessarily required - all that’s required are pairs of partitions for each operating system in the same fashion as they appear on a SD card.

In fact, the partitions don’t have to be in any particular order, or in any particular location, (except for the primary boot partition which must be first).  All that’s required is for them to be located somewhere on the same physical media.

All you need is:

  1. A “base” boot partition that is formatted FAT-32 and is the first partition on the device. It only needs to be big enough to hold the binary blob, (bootcode.bin), and the autoboot.txt.  (I haven’t tried this specific configuration, but based on what I’ve read, it should work.)
  2. A matched pair of partitions for each O/S, one Fat32 for the boot files and another ext4 for the root filesystem.
  3. You must fixup the cmdline.txt and fstab for each partition pair to use the correct PARTUUID’s for the device, otherwise it won’t boot.

I am going to try a simplified approach where I simply rsync the data to the partitions in question, using the suggestions I just made, without PINN, and see how it works.

2 Likes

Update:

I just picked up another large USB hard drive, (2T), to use as an archive drive. I need to get some of this stuff off of my “build” system as it’s CRUSHING my available space.

Once that’s done, I am going to image the two test drives I have and re-build them as pristine installs - one a “copied partition” install and the other a “PINNified” install on completely zilched drives.  (Can YOU say “Enhanced Security Erase”?  Ahh!   I KNEW you could!)

[N.B.  I can post details on how to use HDPARM in Linux to do this kind of advanced security erase if anyone is interested.]

That should clear any residual data and return the drive to being totally un-allocated with lots and lots of empty write and erase blocks.

2 Likes

Another update:

I rebuilt the “copied O/S” drive that used a different way of redirection, among other things, bind-mounting a subdirectory over /boot.

I “converted” it to a “boot_partition=” type of redirection and received some very, VERY interesting results:

First of all, I copied over pristine installs from downloaded images using rsync.

I fixed up the root and boot PARTUUID’s.

I set up an autoboot.txt file with the appropriate logic.

I tested:

  • GoPiGo O/S, Raspbian 32 and Raspbian 64 appeared to work correctly at first glance.  (I didn’t dig into them too extensively.)

  • Raspbian for Robots was nothing but trouble, particularly with WiFi - it kept reporting that it “could not find a WiFi adapter”  (the Pi-4 has it built-in)
     

I tried several things, including re-installing all to no avail.

I am now taking the file-system images from the original “copied partition” installation and creating a new installation, (using the working installations), and setting it up for “boot_partition=” switching.

We’ll see. . .

2 Likes

Hi @jimrh, first thanks for this detailed thread and your hard work ! I’ve been looking (and struggling) for a way to achieve this for a project but the world seemed to brighten up once I found this post.

I followed you process, changed slightly the pinn_init.sh script as I only need to switch between two OS. But as I power up the RPi, I get the colorful box and then a cmd line saying :
sh: /tmp/mnt/pinn_init.sh: not found

It seems the script isn’t exectued. I opened an issue on the PINN github. I was wondering if you’ve had a similar error ?

3 Likes

Actually, thanks to @procount on the Pinn github, it was resolved. I modified the pinn_init script using Notepad++ on Windows and it added DOS end of line. Just had to remove using dos2unix.

3 Likes

You can do that in Notepad++ too. It’s in the "end-of-line conversions.

You can also set the default line-endings to “whatever is already there” and it automatically matches the existing line-ending style for each document. Since I periodically edit doc’s from various sources, I “auto-match” the existing line-endings.

FYI:
There are three basic line-ending styles:

  1. Old-style printer line-endings, which were usually set to CR-LF. (0x0A, 0x0D). CPM and many other “old” operating systems used that because older mechanical printers needed explicit carriage feed commands to home the print heads, (CR) and advance the paper, (LF).
     
    MS-DOS was derived from CPM and adopted its line-endings

  2. Unix and Unix clones (like Linux), used just the carriage-return character as the internal representation of the end of a line was a null byte. (0x00). They adopted a byte-for-byte substitution replacing the 0x00 with a CR, (0x0A).  This allowed the file as edited and as stored to be the same size.

  3. Steve Jobs just HAD to be different, (:wink:), and the Mac universe adopted the line-feed character instead of the carriage return.
     
    Note that later Mac versions are based on BSD “Unix” and use the Unix convention of the LF (0x0A), character.

2 Likes