Adding files for RPi-imager
commit
e33fb470df
@ -0,0 +1,68 @@
|
||||
# Raspberry Pi Initial Setup
|
||||
|
||||
This repository is about the latest procedure for configuring Raspberry Pi boards in 2026. The reported procedure can be used on any Raspberry Pi board.
|
||||
|
||||
The procedure is about flashing the OS image, make ssh connection, both Wi-Fi and USB (Gadget-Mode), and finally upgrading the OS packages.
|
||||
|
||||
# Flashing the OS image
|
||||
|
||||
**Note: This guide is recommended only for the latest OS image or newer; the Trixi image is used in this guide.**
|
||||
|
||||
First you need to download the file `create_local_json.py`; it is recommended to download the file as a repository to organize your data. Then, open a terminal and change the path to the repository or file location:
|
||||
|
||||
```sh
|
||||
cd /path/to/the/file
|
||||
```
|
||||
|
||||
now, the file should be visible using listing commands -like `ls` command-.
|
||||
|
||||
The file must be have executable permission, therefore run:
|
||||
|
||||
```sh
|
||||
chmod +x ./create_local_json.py
|
||||
```
|
||||
|
||||
Now, run the command with the following flags to search online OS images and enable the Gadget mode -USB OTG-:
|
||||
|
||||
```sh
|
||||
./create_local_json.py --online --capabilities usb_otg --device-capabilities usb_otg
|
||||
```
|
||||
|
||||
The shell will give an output similar to:
|
||||
|
||||
```sh
|
||||
Wrote 184 images to os_list_local.rpi-imager-manifest
|
||||
```
|
||||
|
||||
Now, you have a new RPi-imager file that will allow us to set up the gadget mode.
|
||||
|
||||
**Note: you must double-click to open `Raspberry Pi Imager` App or right-click open with `Raspberry Pi Imager`; the recommended version is `v2.0.7` or newer.**
|
||||
|
||||
|
||||

|
||||
|
||||
Now, you can select your device, the OS type -desktop or lite-, and make the image customization —user, Wi-Fi, remote access (ssh), Pi Connect, and gadget mode—; **particularly, if you want to use Gadget mode, you must turn on remote access and gadget mode.**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Finally, you must write the image on the micro-SD and get a customization like this:
|
||||
|
||||

|
||||
|
||||
After RPi-Imager has finished, insert the micro-SD card into your device and connect it with a USB to micro-USB cable using the USB port; the other port must be used for only supply mode:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os.path
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
import urllib.request
|
||||
from collections import OrderedDict
|
||||
|
||||
DEFAULT_REPO_URL = "https://downloads.raspberrypi.com/os_list_imagingutility_v4.json"
|
||||
DEFAULT_OUTPUT_JSON_FILE = "os_list_local.rpi-imager-manifest"
|
||||
CACHE_DIR = "cache"
|
||||
ICONS_DIR = os.path.join(CACHE_DIR, "icons")
|
||||
CHUNK_SIZE = 1024 * 1024 # read files in 1MB chunks
|
||||
|
||||
# Valid OS capabilities (see doc/json-schema/os-list-schema.json)
|
||||
VALID_OS_CAPABILITIES = {
|
||||
"i2c": "Enable I2C interface option",
|
||||
"onewire": "Enable 1-Wire interface option",
|
||||
"passwordless_sudo": "Enable passwordless sudo option in user setup",
|
||||
"rpi_connect": "Enable Raspberry Pi Connect setup",
|
||||
"secure_boot": "Enable secure boot signing",
|
||||
"serial": "Enable serial interface option",
|
||||
"spi": "Enable SPI interface option",
|
||||
"usb_otg": "Enable USB Gadget mode option",
|
||||
}
|
||||
|
||||
# Valid device (hardware) capabilities
|
||||
VALID_DEVICE_CAPABILITIES = {
|
||||
"i2c": "Device supports I2C interface",
|
||||
"onewire": "Device supports 1-Wire interface",
|
||||
"serial": "Device supports serial interface",
|
||||
"serial_on_console_only": "Serial is console-only mode",
|
||||
"spi": "Device supports SPI interface",
|
||||
"usb_otg": "Device supports USB Gadget mode",
|
||||
}
|
||||
|
||||
|
||||
def fatal_error(reason):
|
||||
print(f"Error: {reason}")
|
||||
sys.exit(1)
|
||||
|
||||
def display_warning(reason):
|
||||
print(f"Warning: {reason}")
|
||||
|
||||
def scan_os_list(os_list, os_entries=OrderedDict(), duplicates=set()):
|
||||
for os_entry in os_list:
|
||||
if "subitems" in os_entry:
|
||||
scan_os_list(os_entry["subitems"], os_entries, duplicates)
|
||||
elif "url" in os_entry:
|
||||
image_filename = os.path.basename(os_entry["url"])
|
||||
if image_filename not in duplicates:
|
||||
if image_filename in os_entries:
|
||||
duplicates.add(image_filename)
|
||||
del os_entries[image_filename]
|
||||
os_entries[image_filename] = os_entry
|
||||
return os_entries, duplicates
|
||||
|
||||
def download_file(url, filename):
|
||||
try:
|
||||
#print(f"Downloading {url} to {filename}")
|
||||
with urllib.request.urlopen(url) as response:
|
||||
with open(filename, "wb") as fh:
|
||||
shutil.copyfileobj(response, fh, CHUNK_SIZE)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def download_icon(icon_url):
|
||||
icon_filename = os.path.join(ICONS_DIR, os.path.basename(icon_url))
|
||||
if os.path.exists(icon_filename) or download_file(icon_url, icon_filename):
|
||||
return icon_filename
|
||||
else:
|
||||
display_warning(f"Couldn't download {icon_url}")
|
||||
|
||||
def calculate_checksum(filename):
|
||||
with open(filename, "rb") as f:
|
||||
if hasattr(hashlib, "file_digest"):
|
||||
# only available in python 3.11 or newer
|
||||
hasher = hashlib.file_digest(f, "sha256")
|
||||
else:
|
||||
hasher = hashlib.new("sha256")
|
||||
while True:
|
||||
data = f.read(CHUNK_SIZE)
|
||||
if not data:
|
||||
break
|
||||
hasher.update(data)
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
epilog = "available OS capabilities for --capabilities:\n"
|
||||
epilog += "\n".join(f" {cap:12} {desc}" for cap, desc in VALID_OS_CAPABILITIES.items())
|
||||
epilog += "\n\navailable device capabilities for --device-capabilities:\n"
|
||||
epilog += "\n".join(f" {cap:20} {desc}" for cap, desc in VALID_DEVICE_CAPABILITIES.items())
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=epilog)
|
||||
parser.add_argument("--repo", default=DEFAULT_REPO_URL, help="custom repository URL")
|
||||
parser.add_argument("--search-dir", default=".", help="directory to search for downloaded images")
|
||||
parser.add_argument("--output-json", default=DEFAULT_OUTPUT_JSON_FILE, help=f"manifest file to create (defaults to {DEFAULT_OUTPUT_JSON_FILE})")
|
||||
parser.add_argument("--online", action="store_true", help="use online URLs from the repository instead of searching for local files")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("--dry-run", action="store_true", help="dry run only, don't create manifest file")
|
||||
group.add_argument("--download-icons", action="store_true", help="make a local copy of the device and OS icons")
|
||||
parser.add_argument("--verify-checksums", action="store_true", help="verify the checksums of the downloaded images (ignored with --online)")
|
||||
parser.add_argument("--capabilities", nargs="+", metavar="CAP",
|
||||
help="add OS capabilities to enable features in the customization wizard (see below)")
|
||||
parser.add_argument("--device-capabilities", nargs="+", metavar="CAP",
|
||||
help="add device (hardware) capabilities to all devices (see below)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate OS capabilities if provided
|
||||
if args.capabilities:
|
||||
invalid_caps = set(args.capabilities) - VALID_OS_CAPABILITIES.keys()
|
||||
if invalid_caps:
|
||||
fatal_error(f"Invalid OS capabilities: {', '.join(sorted(invalid_caps))}. Valid capabilities are: {', '.join(VALID_OS_CAPABILITIES.keys())}")
|
||||
|
||||
# Validate device capabilities if provided
|
||||
if args.device_capabilities:
|
||||
invalid_caps = set(args.device_capabilities) - VALID_DEVICE_CAPABILITIES.keys()
|
||||
if invalid_caps:
|
||||
fatal_error(f"Invalid device capabilities: {', '.join(sorted(invalid_caps))}. Valid capabilities are: {', '.join(VALID_DEVICE_CAPABILITIES.keys())}")
|
||||
|
||||
repo_urlparts = urllib.parse.urlparse(args.repo)
|
||||
if not repo_urlparts.netloc or repo_urlparts.scheme not in ("http", "https"):
|
||||
fatal_error("Expected repo to be a http:// or https:// URL")
|
||||
|
||||
repo_filename = os.path.basename(repo_urlparts.path)
|
||||
if os.path.exists(repo_filename):
|
||||
online_json_file = repo_filename
|
||||
else:
|
||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||
online_json_file = os.path.join(CACHE_DIR, repo_filename)
|
||||
if os.path.exists(online_json_file):
|
||||
print(f"Info: {online_json_file} already exists, so using local file")
|
||||
else:
|
||||
if not download_file(args.repo, online_json_file):
|
||||
fatal_error(f"Couldn't download {args.repo}")
|
||||
|
||||
with open(online_json_file) as online_fh:
|
||||
online_data = json.load(online_fh)
|
||||
# Find image filenames in the online JSON (discarding duplicates)
|
||||
online_os_entries, duplicate_online_filenames = scan_os_list(online_data["os_list"])
|
||||
if not online_os_entries:
|
||||
fatal_error(f"No matching OSes found in {online_json_file}")
|
||||
if args.download_icons:
|
||||
os.makedirs(ICONS_DIR, exist_ok=True)
|
||||
|
||||
local_os_entries = dict()
|
||||
local_device_tags = set()
|
||||
|
||||
if args.online:
|
||||
# Use all OS entries from online manifest with their original URLs
|
||||
for name, os_entry in online_os_entries.items():
|
||||
if args.dry_run:
|
||||
print(f"Including {os_entry['name']}")
|
||||
local_os_entries[name] = os_entry
|
||||
if "devices" in os_entry:
|
||||
for tag in os_entry["devices"]:
|
||||
local_device_tags.add(tag)
|
||||
if args.download_icons and "icon" in os_entry:
|
||||
local_icon_filename = download_icon(os_entry["icon"])
|
||||
if local_icon_filename:
|
||||
os_entry["icon"] = pathlib.Path(os.path.abspath(local_icon_filename)).as_uri()
|
||||
else:
|
||||
# Recursively search for any matching local filenames
|
||||
duplicate_local_filenames = set()
|
||||
abs_search_dir_len = len(os.path.abspath(args.search_dir)) + 1
|
||||
for root, dirs, files in os.walk(args.search_dir):
|
||||
for name in files:
|
||||
abs_local_image_filename = os.path.abspath(os.path.join(root, name))
|
||||
# remove leading search_dir prefix
|
||||
display_filename = abs_local_image_filename[abs_search_dir_len:]
|
||||
if name in duplicate_online_filenames:
|
||||
display_warning(f"Ignoring {display_filename} as it matched multiple filenames in {online_json_file}")
|
||||
elif name in duplicate_local_filenames:
|
||||
pass
|
||||
elif name in local_os_entries:
|
||||
display_warning(f"Ignoring {display_filename} as there are multiple local copies")
|
||||
del local_os_entries[name]
|
||||
duplicate_local_filenames.add(name)
|
||||
elif name in online_os_entries:
|
||||
os_entry = online_os_entries[name]
|
||||
if "image_download_size" in os_entry and os.path.getsize(abs_local_image_filename) != os_entry["image_download_size"]:
|
||||
display_warning(f"Ignoring {display_filename} as its size doesn't match with {online_json_file}")
|
||||
elif args.verify_checksums and "image_download_sha256" in os_entry and calculate_checksum(abs_local_image_filename) != os_entry["image_download_sha256"]:
|
||||
display_warning(f"Ignoring {display_filename} as its checksum doesn't match with {online_json_file}")
|
||||
else:
|
||||
if args.dry_run:
|
||||
print(f"Found {display_filename} ({os_entry['name']})")
|
||||
# point at our local file instead of the online URL
|
||||
os_entry["url"] = pathlib.Path(abs_local_image_filename).as_uri()
|
||||
# Auto-detect bmap sidecar file (e.g., image.img.xz.bmap or image.img.bmap)
|
||||
for bmap_suffix in [abs_local_image_filename + ".bmap",
|
||||
os.path.splitext(abs_local_image_filename)[0] + ".bmap"]:
|
||||
if os.path.exists(bmap_suffix):
|
||||
os_entry["bmap_url"] = pathlib.Path(bmap_suffix).as_uri()
|
||||
if args.dry_run:
|
||||
print(f" Found bmap sidecar: {bmap_suffix[abs_search_dir_len:]}")
|
||||
break
|
||||
local_os_entries[name] = os_entry
|
||||
if "devices" in os_entry:
|
||||
for tag in os_entry["devices"]:
|
||||
local_device_tags.add(tag)
|
||||
if args.download_icons and "icon" in os_entry:
|
||||
local_icon_filename = download_icon(os_entry["icon"])
|
||||
if local_icon_filename:
|
||||
os_entry["icon"] = pathlib.Path(os.path.abspath(local_icon_filename)).as_uri()
|
||||
|
||||
num_images = len(local_os_entries)
|
||||
if num_images < 1:
|
||||
if args.dry_run:
|
||||
fatal_error(f"No matching image files found")
|
||||
else:
|
||||
fatal_error(f"No matching image files found, so not creating {args.output_json}")
|
||||
# Create the output data structure
|
||||
local_data = dict()
|
||||
if "imager" in online_data and "devices" in online_data["imager"]:
|
||||
local_data["imager"] = dict()
|
||||
local_data["imager"]["devices"] = list()
|
||||
for device in online_data["imager"]["devices"]:
|
||||
if "tags" not in device or not device["tags"] or any(tag in local_device_tags for tag in device["tags"]):
|
||||
local_data["imager"]["devices"].append(device)
|
||||
if args.download_icons and "icon" in device:
|
||||
local_icon_filename = download_icon(device["icon"])
|
||||
if local_icon_filename:
|
||||
device["icon"] = pathlib.Path(os.path.abspath(local_icon_filename)).as_uri()
|
||||
# Add device capabilities if specified
|
||||
if args.device_capabilities:
|
||||
existing_caps = set(device.get("capabilities", []))
|
||||
merged_caps = existing_caps | set(args.device_capabilities)
|
||||
device["capabilities"] = sorted(merged_caps)
|
||||
# Sort the output OSes into the same order as the input OSes
|
||||
os_order = list(online_os_entries.keys())
|
||||
local_data["os_list"] = sorted(local_os_entries.values(), key=lambda x: os_order.index(os.path.basename(x["url"])))
|
||||
|
||||
# Add capabilities to OS entries if specified
|
||||
if args.capabilities:
|
||||
for os_entry in local_data["os_list"]:
|
||||
# Merge with existing capabilities if present
|
||||
existing_caps = set(os_entry.get("capabilities", []))
|
||||
merged_caps = existing_caps | set(args.capabilities)
|
||||
os_entry["capabilities"] = sorted(merged_caps)
|
||||
# And finally write the local JSON file
|
||||
if args.dry_run:
|
||||
print(f"Would write {num_images} image{'s' if num_images > 1 else ''} to {args.output_json}")
|
||||
else:
|
||||
with open(args.output_json, "w") as local_fh:
|
||||
json.dump(local_data, local_fh, indent=2)
|
||||
print(f"Wrote {num_images} image{'s' if num_images > 1 else ''} to {args.output_json}")
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
Loading…
Reference in New Issue