
FM1Kit
Peaking95/100A Swift toolkit for the M-Vave FM-1 synthesiser
FM1Kit is custom firmware for the M-VAVE FM-1, by @ocean. Open source under MIT, latest release 0.5.1 (November 2026), 1 star on GitHub.
forM-VAVE FM-1·by@ocean
Unclaimed. Is this yours? Sign in with GitHub as @ocean to claim it.
No release files yet. Build from source.
MIT· ocean/FM1Kit·downloads come from the creator’s GitHub releases
0.5.1FM1Kit 0.5.1Latest0 files
TransitFM1 refuses V16, as tested on an FM-1 in stage S, so the switching guide now sends people from it to V16 through MIDIMON 0.4 or LOOM-8.
Fixed
LeavingGuidenow sends people from TransitFM1 to V16 through MIDIMON 0.4 or LOOM-8: TransitFM1 refuses V16 at the header (tested on an FM-1 in stage S), since it takes only firmware images the size of V15's. Its steps to V16 no longer say the FM-1 keeps its bootloader, or that TransitFM1 must be confirmed as not V15. A TransitFM1 guide now has twowarnings, not one.packageRefused's explanation names TransitFM1, and says MIDIMON 0.4 and LOOM-8 take V16 too.fm1probe install --reinstall-officialdescribes the install by the package's identity, since it can't tell whether TransitFM1 is being installed over V15 or V15 over TransitFM1.
No API changes: move from 0.5.0 with
.upToNextMinor(from: "0.5.1").0.5.0FM1Kit 0.5.00 files
M-VAVE's V16, a guide to switching away from each firmware, and a fresh start that clears every firmware's saved data, all tested on an FM-1 in stage R. It also includes 0.5.0-beta.1's choice of device, which has been tested only with simulated devices so far.
Added
- From 0.5.0-beta.1: choosing a device when more than one is connected (
FM1Updater.devices(),FM1Updater(transport:timing:device:),FM1Advice.Action.chooseDevice, andfm1probe devicesand--device). It's tested with simulated devices only so far. LeavingGuide: how to switch away from a firmware, worked out from its package before it's installed.FwscPackage.updateCodereads how the firmware installs others (UpdateCode: M-VAVE's, Felucca's original or fixed check, V15 recognised by hash, or a loader of its own), and the guide combines it with the FM-1's bootloader (FM1BootArea) to give aLeavingRouteto V15, V16, open firmware, other custom firmwares and m-bootloader,warningsto show before installing, and step-by-stepinstructions(to:). It'sCodable, so apps can save it with the FM-1, which also tells TransitFM1 apart from V15.refusesV15OverOtherBootloadersis nowupdateCode == .feluccaOriginalCheck. Stage R confirmed its routes to and from V16 on hardware.OfficialFirmware.v16: M-VAVE's V16 (FM-1_016), which their downloads page has served since 21 October 2026, withFM1Identity.officialV16andFM1Identity.isOfficial.OfficialFirmwaregainsidentity,isRedistributable(only V15 may be included in an app; download V16 from M-VAVE) andofficial(V15 and V16).FM1DeviceSummarynames V16, and V16 over V16 is areinstall, as for V15. Tested on hardware in stage R.FlashBackup.firmwarenames V16 ("V16 (FM-1_016)") by itsapp.bin's SHA-256, as read from a real FM-1, instead of "unknown, naming FM-1_016".InstallOptions.confirmsReinstallingOfficialandInstallCheck.canReinstallOfficial: installing M-VAVE's official firmware (V15) on an FM-1 that reports its identity, once the person confirms it's running a firmware that reports it on purpose, such as TransitFM1. Without it, FM1Kit couldn't take an FM-1 from TransitFM1 back to V15.alreadyInstalledfor V15 now says so, and its advice isconfirm.fm1probe install --reinstall-officialdoes the same.
Changed
FlashPlan.installing(_:onto:includingBootloader:erasingSettings:)is nowinstalling(_:onto:includingBootloader:erasingSavedData:), and erases more: everything from the end of the new image up to0xFBFFF, which covers every saved-data region firmwares declare (VM,BTIF, V15's voice banks inUSR, V16'sSEQ64) and what they leave outside them, so the new firmware starts from its defaults instead of meeting another firmware's leftovers. V16 leaves data in V15'sUSRregion that makes V15 play one note at a time, which only this clears (tested on hardware, stage R). It never touches the boot area,0xFC000onwards or the factory data. Installing V15 this way writes its factory voices and the rest of itsUSRdata straight back into the erased voice banks, so it's never silent and needs no voice restore.fm1probe flash-install --erase-settingsis now--erase-saved-data.bootloaderMismatchandrefusesV15OverOtherBootloadersexplain that V16 brings a different bootloader from V15, so firmwares with Felucca's original check refuse V16 on an FM-1 with V15's bootloader, and V15 once V16's is installed.packageRefusedsays firmwares based on Felucca 1.6.1 refuse V16 as well as open firmware, whatever the bootloader (stage R).bootloaderMismatchno longer says download mode keeps the FM-1's bootloader: after V16, V15 is installed there with its own bootloader, fresh.bootloaderMismatchandpackageRefusedname MIDIMON 0.4 as well as LOOM-8 as the firmware to install first, and put that before download mode: a firmware with Felucca's original check takes either, and they take V15 and open firmware (tested on hardware). Their advice is nowchooseAnotherFirmware, notneedsComputer, since the way out works from a phone.fm1probe inspectsuggests MIDIMON 0.4 or LOOM-8 for a firmware that refuses V15.
Fixed
DownloadModeError.accessRefusedpromised that macOS would ask to allow access to a removable volume. macOS asks only a program in the logged-in person's session; a launchd helper is refused without being asked, whatever its Full Disk Access.refusesV15OverOtherBootloaderswas true for Quixotic7's TransitFM1 and RemoteFM1. They install every package with a loader of their own, with no boot-area compare, so they take V15 whatever the bootloader (though not open firmware).
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.5.0")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.5...0.5.0
- From 0.5.0-beta.1: choosing a device when more than one is connected (
0.5.0-beta.1FM1Kit 0.5.0-beta.1Beta0 files
A pre-release for testing with more than one FM-1 (or other M-VAVE device) connected. It has been tested only with simulated devices; use 0.4.5 otherwise.
Added
- Choosing a device when more than one is connected:
FM1Updater.devices()lists every device that answers the handshake, andFM1Updater(transport:timing:device:)makes an updater that works with the chosen one alone. It leaves the other devices alone and follows the chosen one as it restarts into its update loader and back, on a new port.FM1DeviceSummaryisIdentifiableand has theportit answered on. FM1Advice.Action.chooseDevice, the advice formultipleDevices, which wasreconnect.fm1probe deviceslists the devices, numbered, and--device N|SERIALchooses one forinstall,install-bootloader,restore-voices,backupanddownload-mode.
Two devices in the chip's USB download mode at once are still refused.
Using it
Swift Package Manager doesn't pick pre-releases for a range, so pin it exactly:
.package(url: "https://github.com/ocean/FM1Kit.git", exact: "0.5.0-beta.1"),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.5...0.5.0-beta.1
- Choosing a device when more than one is connected:
0.4.5FM1Kit 0.4.50 files
FM1Kit no longer warns that SikkenWorks' firmwares refuse V15 when the FM-1's bootloader has been changed.
Fixed
refusesV15OverOtherBootloaderswas true for SikkenWorks' ETCH, BEETLE and B1. Their update code is on Felucca's platform with its original check, but it recognises V15 by its flash image's SHA-256 and takes it whatever the bootloader. They still refuse M-VAVE's open firmware over a changed bootloader.
Hardware testing (stage P)
A way out over USB-MIDI for firmwares with Felucca's original update check: Felucca 1.6.1, unable to install V15 over m-bootloader or Open V12's bootloader, took MIDIMON 0.4, which took Open V12 and V15. Any firmware on Felucca's fixed update code (MIDIMON, LOOM-8) works as that step between.
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.5")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.4...0.4.5
0.4.4FM1Kit 0.4.40 files
FM1Kit now tells a firmware refusing a package straight after its header apart from a stalled install, and its messages cover any changed bootloader: M-VAVE's open firmware traps firmwares with Felucca's original update check just as m-bootloader does.
Added
FM1UpdateError.packageRefused(requests:): the firmware read the package's header and stopped, refusing it. Firmwares with Felucca's original update check do this for m-bootloader's installer and for update loaders they don't recognise, and copies of Felucca 1.6.1 for M-VAVE's open firmware too. These were reported asstalled(.staging), whose advice was to try again; the advice is now.needsComputer, as forbootloaderMismatch.FM1Updater.installBootloader(_:fromAnyFirmware:events:)andfm1probe install-bootloader --from-any-firmware: send m-bootloader's installer whatever firmware is running, for hardware tests of how other firmwares treat it.SimulatedFM1.Options.refusesAfterHeader.
Changed
bootloaderMismatchandrefusesV15OverOtherBootloadersdescribe any changed bootloader (m-bootloader, or M-VAVE's open firmware's), and point to firmwares with Felucca's updated check, such as LOOM-8, as a way back. Estampie is no longer listed among the firmwares that refuse V15.
Fixed
- The README's and llms-full.txt's recovery sections describe download mode as FM1Rescue uses it.
Hardware testing (stage O)
After Open V12, DUB-1 1.3.0 refused V15 but took Open V12 again; Felucca 1.6.1 refused both (
FAILED -15,FAILED -5); LOOM-8 0.8 took Open V12 and V15. Melodee also refused V15 over m-bootloader.Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.4")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.3...0.4.4
0.4.3FM1Kit 0.4.30 files
Apps can now tell which firmwares really refuse M-VAVE's V15 over USB-MIDI while m-bootloader is installed. Firmwares built on the fixed update code of Felucca's platform take V15 and are no longer flagged.
Added
FwscPackage.refusesV15OverOtherBootloaders: true when a firmware has Felucca's update code without Charles Vestal's fix (felucca-bspafd83ed), so it refuses V15 over USB-MIDI once m-bootloader is installed. Firmwares on the fixed code, such as LOOM-8 0.8 and Del Rio 0.9, only check that the package's bootloader is one the FM-1 can start, and aren't flagged. Use this to warn before installing a firmware;usesFeluccaUpdateCodenow just says whether a firmware is on the platform.fm1probe inspecttells the two apart.- Hardware testing (stage M): LOOM-8 0.8 took V15 over USB-MIDI on an FM-1 with m-bootloader 1.2, where DUB-1 1.3.0 refused it.
On the packages tried, the new property is true for Felucca 1.6.1, AMB-1 1.3, DUB-1 1.3.0, X0X 1.1.0, FoMni 0.4, Sloop 2.8.1, Melodee 1.5.0, Skiff 2.0.1 and CS-1 0.3.2, and false for LOOM-8 0.8, Del Rio 0.9, V15 and m-bootloader's installer. TransitFM1, which installs every package with its own loader, takes V15 too but is still flagged.
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.3")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.2...0.4.3
0.4.2FM1Kit 0.4.20 files
Apps can now tell from a package whether a firmware uses Felucca's update code, which refuses M-VAVE's V15 over USB-MIDI while m-bootloader is installed, and warn before installing it.
Added
FwscPackage.usesFeluccaUpdateCode: whether a firmware package's code uses Felucca's update code (AMB-1, HYP-1, DUB-1, Estampie, LOF-1, Felucca), read from the text that code depends on. It works on iOS and macOS, andfm1probe inspectshows it. On the packages tried it's true for AMB-1 1.3, DUB-1 1.3.0, Estampie 0.1, Felucca 1.6.1 and LOF-1 0.0.77, and false for V15, Open V12 and m-bootloader's installer.- Reading a package's or backup's firmware code moved from FM1Rescue into FM1Kit, ported from M-Flasher's
formats.rswith its MIT notice, so both libraries share it.
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.2")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.1...0.4.2
0.4.1FM1Kit 0.4.10 files
FM1Kit now explains why some custom firmwares refuse to install M-VAVE's V15 while m-bootloader is installed, and installs over DUB-1 again.
Added
FM1UpdateError.bootloaderMismatch(requests:): firmwares built on Felucca's platform (AMB-1, HYP-1, DUB-1, Estampie) compare the boot area of a package with M-VAVE's update loader (V15, open firmware) with the FM-1's before installing it, so they refuse V15 while m-bootloader is installed. An install that stops on that comparison now says so, and that installing in download mode from a Mac keeps the FM-1's bootloader, instead of the general staging advice.SimulatedFM1.Options.comparesBootAreasimulates it.
Fixed
- Firmwares numbering their builds
FM-1_8and six digits, as DUB-1 does (FM-1_8010300for 1.3.0), were taken for M-VAVE's open firmware, so installing over them stopped withcapabilityUnavailable.FM1Identity.isOpenFirmwarenow takesFM-1_8000000toFM-1_8000999(andFM1_COMMUNITY_). FlashBackup.firmware.namenames a custom firmware only when its code also holds the name and version it shows, as forks share identities: HYP-1 numbers its builds as AMB-1 does, so a HYP-1 build could be named "AMB-1 1.3".FlashPlan.installing(_:onto:includingBootloader:erasingSettings:)no longer warns that the installed firmware keeps its settings elsewhere when it erases the new firmware's settings.- fm1probe's timestamps were on a 12-hour clock without AM or PM when run under sudo; they're always 24-hour now.
Tested on hardware
With m-bootloader installed, over USB-MIDI: V15 → DUB-1 1.3.0 → AMB-1 1.3 → Estampie 0.1 all installed and kept m-bootloader. AMB-1 refused Open V12 and Estampie refused V15 at the boot-area check (as HYP-1 did), and FM1Kit reported the bootloader mismatch. Open V12 ran on m-bootloader after a download-mode install, and Open V12 took V15 over MIDI, which put M-VAVE's bootloader back. With M-VAVE's bootloader, DUB-1 took V15. See HARDWARE_TESTING.md.
In short: with m-bootloader, firmwares built on Felucca's platform switch between each other over USB-MIDI, but going back to V15 from them needs download mode on a Mac.
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.1")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.4.0...0.4.1
0.4.0FM1Kit 0.4.00 files
FM1Kit can now install m-bootloader over USB-MIDI, and its new macOS library, FM1Rescue, backs up, restores and writes the FM-1's whole flash in the chip's USB download mode, whatever firmware is installed.
Added
FM1Updater.installBootloader(_:events:)installs m-bootloader from itsfm1-uboot.fwscpackage on an FM-1 running V15, keeping the firmware.BootloaderInstallerreads the package, decompresses its installer and checks every checksum, as the bootloader does before running it, and finds the bootloader it writes;BootloaderImagereads and checks m-bootloader's.bootfiles. FM1Kit can't read the bootloader back over USB-MIDI, so the FM-1's screen confirms the result.FwscPackage.isFirmware: packages that hold only an installer are now read, andinstall,resumeandcheckrefuse them (FM1UpdateError.notFirmware).InstallProgress: an install's events as one bar that never goes backwards, with four stages.FM1Advice(andFM1UpdateError.advice): any error as a headline, an explanation, whether the FM-1 is still fine, one action and technical details.FM1Updater.summary()andFM1DeviceSummary: the connected FM-1's firmware name and version (for V15 and open firmware), whether it's waiting mid-update, and its USB serial number (MIDIPortDescriptor.serialNumber).SimulatedFM1reports a serial number and runs bootloader installers (Options.serialNumber,bootloaderVersion;installedBootloaderVersion), and installing V15 on it puts M-VAVE's bootloader back, as on a real FM-1.fm1probe install-bootloader,inspectfor installers and.bootfiles, and serial numbers inidentify.- A second library,
FM1Rescue(macOS only; empty on iOS), for the chip's USB download mode:FM1Updater.enterDownloadMode()asks V15 or Felucca to switch with a MIDI message;USBDownloadModeConnectiontakes the FM-1 from macOS's disk driver (needs root) and speaks USB mass storage to it;FlashSessionuploads m-bootloader's flash loader (bundled, MIT) and reads the flash;FlashSession.backUp(progress:)reads it twice and checks both reads against the chip's CRC-32;FlashBackup.write(to:)saves it as M-Flasher does.SimulatedDownloadModestands in for the chip. FlashBackup.firmwaresays whether the firmware's files in a backup are whole (as m-bootloader's menu reports them), which identities its code names and whether it's V15, naming Felucca 1.6.1 and AMB-1 1.3 too (name), and the backup's.jsongains M-Flasher'sfirmwareandappfields.FlashBackup(contentsOf:)reads a saved backup.- Writing in download mode, which works whatever the firmware:
FlashPlanworks out a change against the flash just read (restoring(_:onto:)a backup,installing(_:onto:)m-bootloader,installingBootloader(from:onto:)M-VAVE's bootloader from a firmware package, orinstalling(_:onto:includingBootloader:erasingSettings:)a firmware), listing the 4 KB sectors that change.FlashSession.write(_:progress:)checks the flash hasn't changed, erases and writes only those sectors, checks each run against the chip's CRC-32 and shows progress on the FM-1's screen. Another FM-1's backup never replaces this one's factory data, and nothing is planned for flash that shows it's another model's (FlashBackup.model; the SMK-37 has the same chip). If writing stops part-way, the error says not to switch the FM-1 off but to restore the backup. New errors:FlashPlanError, andDownloadModeError.flashChanged,writeNotVerified(_:)andwriteInterrupted(at:_:).SimulatedDownloadModeerases and writes as NOR flash does. fm1probe download-modeandfm1probe flash-backup FOLDER, with--traceto print every USB transfer;fm1probe inspectshows the bootloader and firmware in a.binbackup;fm1probe flash-restore,flash-bootloaderandflash-installwrite in download mode after saving a backup and asking to confirm, or rehearse with--simulated BACKUP.bin.
Changed
- When an install stops before the FM-1 restarts, the error explains that a custom firmware's own update code may have refused the package, and that replacing it needs download mode.
fm1probe install-bootloadershows staging progress as a count of requests.
Fixed
- An install could wait forever when the FM-1 stopped asking for data but kept sending other SysEx: only read requests now count as activity, so it stops as stalled after the idle limit.
Tested on hardware
m-bootloader 1.2 installed over USB-MIDI from V15. Then in download mode: a verified 1 MB backup; Felucca 1.6.1 installed over AMB-1 (125 sectors in 8.6 s); a backup of V15 restored over Felucca; M-VAVE's bootloader put back from V15's package and m-bootloader installed again. The final flash was byte for byte the same as the backup from before. See HARDWARE_TESTING.md.
Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.4.0")),FM1Rescue needs root to take the FM-1 from macOS's disk driver, so apps reach it through a privileged helper.
Full changelog: https://github.com/ocean/FM1Kit/compare/0.3.0...0.4.0
0.3.0FM1Kit 0.3.00 files
FM1Kit can now back up an FM-1 running M-VAVE's open firmware before it's switched to another firmware.
Added
FM1Updater.backUpOpenFirmware(progress:)backs up open firmware's 16 project slots, the project being edited (with unsaved changes) and its sample bank, through the firmware's project library. Open firmware can only read the project being edited, so each slot is loaded in turn and the workspace is put back afterwards; no slot is saved and the sample bank isn't written.OpenFirmwareBackup.write(to:)saves the backup as the.fm1project and.fm1ssample files M-VAVE's workbench restores, with abackup.jsondescribing them. Open V13's per-slot tuning tables aren't included yet.fm1probe backup FOLDER(with--simulatedand--log FILE).SimulatedFM1answers the project library while running open firmware (Options.projectSlots,workspace,currentSlot,sampleBank).llms.txtandllms-full.txtdescribe the package for language models.
Fixed
SimulatedFM1could ignore the upgrade command from a host that found its loader very quickly after staging, which made an open firmware test fail now and then.
Tested on hardware
V15 → Open V12, then a backup that kept the unsaved tempo of the project being edited; M-VAVE's workbench read the backed-up project and wrote it back to the FM-1. Then V15 over Open V12 in one go, without
--resume(24 s), and V15's factory voices restored. See HARDWARE_TESTING.md.Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.3.0")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.2.0...0.3.0
0.2.0FM1Kit 0.2.00 files
Custom firmwares can now be reinstalled, or updated to another release that keeps the same identity.
Changed
- Installing the identity a custom firmware already reports is no longer refused. Felucca's update code, which Estampie inherits, doesn't compare identities, and firmwares that keep one identity across releases are updated this way. The new
InstallKind.sameIdentityneeds confirming withInstallOptions.confirmsSameIdentity(otherwiseFM1UpdateError.sameIdentityNotConfirmed). - Only V15 over itself is still refused as
reinstall(alreadyInstalled), and open firmware still compares builds. SimulatedFM1.Options.refusesRunningFirmwarenow applies only to V15.
Source-breaking: the new
InstallKindcase means exhaustive switches over it need updating.Tested on hardware
Estampie 0.1 (
FM-1_378001) installed over V15 (a firmware switch, 37 s), then over itself (sameIdentity, 31 s). See HARDWARE_TESTING.md.Using it
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.2.0")),Full changelog: https://github.com/ocean/FM1Kit/compare/0.1.0...0.2.0
- Installing the identity a custom firmware already reports is no longer refused. Felucca's update code, which Estampie inherits, doesn't compare identities, and firmwares that keep one identity across releases are updated this way. The new
0.1.0FM1Kit 0.1.00 files
FM1Kit's first release: a Swift package for installing firmware on the M-VAVE FM-1 over USB-MIDI, from iOS and macOS.
.package(url: "https://github.com/ocean/FM1Kit.git", .upToNextMinor(from: "0.1.0"))Tested on one FM-1 from a Mac: installing M-VAVE's official V15, Felucca 1.6.1 and M-VAVE's open firmware V12 and switching between them, pulling the cable part-way through writing and resuming, and restoring V15's factory voices. See HARDWARE_TESTING.md for the results. Reports from other devices and hosts are very welcome.
FM1Kit isn't affiliated with M-VAVE, and doesn't include M-VAVE's firmware. Installing custom firmware is at your own risk.
Added
FwscPackage: reads.fwscfirmware packages, their product identity, logical image and SHA-256.OfficialFirmware: recognises M-VAVE's official releases (V15, and Open V12) by size and checksum.FM1Identity: parses what a device or package reports, including theota-update loader.FM1Updater: installs a package over USB-MIDI, resumes an interrupted update or a device in USB recovery mode, and checks the device reports the new firmware afterwards.CoreMIDITransportfor real devices on iOS and macOS, andSimulatedFM1for the iOS Simulator and tests.fm1probe, a Mac command-line tool that lists ports, identifies a connected FM-1 (repeatedly, with timings), monitors SysEx, watches devices come and go, inspects packages, and installs a firmware after asking to confirm (or rehearses one againstSimulatedFM1).- Installs and resumes support task cancellation.
FM1IdentityisCodable(as its text) and can be made from a model and version.SimulatedFM1refuses a package with the identity it's running, as the real device does.UpdateEvent.checking(fraction:requests:)reports a loader reading the image a second time after writing, as Felucca's loader does.UpdateEvent.writing's fraction is the share of the firmware's application area read so far, in any order (V15's loader reads backwards), so both loaders seen reach 1.SimulatedFM1.Optionscan read backwards (writesBackwards) and check after writing (checksAfterWriting), as these loaders do.FwscPackagechecks the JieLi container inside a package before anything is sent: the header and table checksums, the chip (AC791N), each part's checksum, and that there's one firmware image and one update loader. NewFailurecases say what's wrong (tooLarge,damagedHeader,unexpectedChip,damagedPart,missingPart).FwscPackage.loaderSHA256identifies the loader a package brings, andOfficialFirmware.stockLoaderSHA256is M-VAVE's.fm1probe inspectshows both.FM1UpdateError.unexpectedFinish(phase:): the update stops if the device asks to finish the other half of the update.- M-VAVE's open firmware (
FM-1_8000012and builds of it,FM1Identity.isOpenFirmware): the updater asks it for itsFM1Capabilityand arms it with the package'sFM1BuildIDbefore the upgrade command, and checks the build it reports afterwards. New errors:capabilityUnavailable,deviceBusy,armRefused(code:)andbuildMismatch.OfficialFirmwaregainsbuildIDand recognises Open V12 (openV12).SimulatedFM1simulates open firmware. Tested on one FM-1: installing Open V12 over V15, and arming it to install V15 back. FM1Updater.check(_:buildID:)says what an install would do (InstallKind: an update, a switch to a different firmware, or a reinstall) without changing the device.install(_:options:events:)takesInstallOptions; switching firmware, which can erase projects and samples, throwsswitchNotConfirmedunlessconfirmsSwitchingFirmwareis set, and reinstalling the running firmware throwsalreadyInstalledbefore anything is sent.- Installs and resumes refuse to start while more than one FM-1 answers (
multipleDevices).FM1Updater.findAll(where:)lists every device that answers. - The updater also probes ports named after other firmwares' loaders: SLOOP, Clipper and SINCO.
@_spi(Testing)FwscPackage.sampleFile(product:flashSize:loaderSize:seed:)builds a small valid package for apps' tests withSimulatedFM1. Never install one on a real FM-1.FM1Identityreads the identity an armed open firmware's update loader reports, such asota-FM1C_db1642b2b6fa5c2c(armedBuildPrefix), so the updater finds that loader; an install checks it names the build it was armed with.FM1Updater.restoreFactoryVoices(from:progress:)rewrites V15's four voice banks with its 128 factory voices from the official V15 package, as M-VAVE's updater does after installing V15 (FwscPackage.hasFactoryVoices,FM1Identity.officialV15). New errors:factoryVoicesUnavailable,voiceRestoreNeedsV15,voiceRestoreRefusedandvoiceRestoreStalled.SimulatedFM1simulates V15's voice banks (currentVoices), andfm1probe restore-voicesruns a restore after asking to confirm. Tested on one FM-1.- Updates add no pauses, as M-VAVE's own updater doesn't: the device asks for each block when
it's ready (
UpdaterTiming.responseDelayandafterUpgradeCommandare zero by default). - After answering a finish request, the updater keeps the port open for
UpdaterTiming.finishSettle(600 ms), answering any repeat, so the reply isn't cut off. fm1probe installshows what the install would do and warns before switching firmware, and takes--build-id;fm1probe identifywarns when more than one FM-1 answers.