Master
This page describes how to operate the lasershow master versions 1 and 2.
Version 1/2 fully supersedes the master version 0 which is now considered obsolete.
The main difference between version 1 and 2 is that version 1 only supports the old slave units whereas version 2 has two different transmitters built-in and support both old and new slaves. Version 2 is 100% backwards compatible with version 1.
In version 1, the valid address range is 80-FF and the valid data range is 00-7F whereas in version 2 both the valid address and data range is 00-FF. The extended range-codes will ONLY be transmitted on the new transmission modules and are not compatible with version 0 slave units.
The front control panel consists of a 4-digit display, a rotating knob/select push button, a ‘play’ button and a ‘stop’ button.
Worth noticing is that the front control panel is a module and can be replaced with another module with different functionality, although currently no such module exists.
The main purpose of the master is to interact with a switch-panel to send out user codes, however the new master can be operated without a keyboard.
The program runs in 4 different modes
- normal mode (shows ‘SEnd’ when booted or when flipping through modes)
- macro mode for repurposing switches or recording switch macro sequences
- playback mode for playing a switch program from out of 10 different slots
- recording mode which either records a switch sequence or actual RF-traffic in 1 out of 10 program slots
A long press (~0.5s) on the select push button toggles through the different modes (normal->macro->playback->record), a short press on select OR a press on the stop button toggles selected fields (address or data).
A long press on ‘play’ + ‘stop’ will function as the select key.
Normal mode
When booting the device or when flipping through the modes, the display shows ‘SEnd’ when this mode is active until a code is sent or selected on the knob.
Sending a code
- Rotate the knob to increase (right turn) or decrease (left turn) the address.
- Press ‘stop’ button or a short press on the select button to jump to data field, multiple presses toggles field
- Press ‘play’ button to send the code shown in the display
If the keyboard is attached and a switch is flipped, its active address/data is shown in the display and its code is sent to the receivers. A green LED indicates that it is a programmed switch that is being sent rather than an original switch code. If it is a macro sequence with > 1 flip, the display will flicker through all the codes and stop at the last code.
Pressing ‘play’ always re-sends the last code shown in the display.
Rotating knob/pressing ‘stop’ always changes code but doesn’t send anything by itself.
Macro mode
The purpose of macro mode is to program the switches as best fit the user.
When entering macro mode the display shows ‘PGn’.
Note that macro mode has no function without a keyboard attached.
Flipping a switch on the keyboard shows its original address/data and the program LED indicates if the switch has been programmed (red) or is currently in its default state (white), however it will always send its programmed content in normal mode.
Repurposing a key
- Flip the switch to be repurposed (flip it to the correct position, last flip counts)
- Rotate the knob to increase (right turn) or decrease (left turn) the address.
- Press ‘stop’ button to jump to data field
- Press ‘play’ button to program the switch with what the display shows
Recording a macro
- Flip the switch to be programmed (flip it to the correct position, last flip counts)
- Press ‘play’
- Flip the switch sequence to be programmed (max 16 switch flips, only the last 16 flips are stored)
- Press ‘stop’ to end recording
There is no mechanism to prevent starting a record in an already used slot so use with precaution.
Note that a single sequence macro can be recorded and is the same thing as a repurposed key but with the difference that the address/data cannot be chosen freely, only what’s already programmed.
Also note that macro recording only records original or repurposed keys, not macro sequences from other keys.
There is no time limit while recording a macro; a recording session will remain open indefinitely until 16 commands have been stored, the user presses ‘stop’ or exits macro mode. If macro mode is exited, what was recorded so far is stored.
Playback mode
Rotating the knob shows ‘Pb. 0’ – ‘Pb. 9’ and the LED indicates if there is a program in the slot (red) or if it is empty (white).
Playing back a session
- Rotate the knob to select slot 0-9 (red LED=occupied, white LED=empty)
- Press ‘play’ to start session (LED turns green). Rotate knob to toggle between last sent code or elapsed time.
- Press ‘play’ again to pause (LED turns blue), elapsed time pauses. Another press on ‘play’ resumes.
- Press ‘stop’ to end playback (LED turns red i.e program slot used).
When the program finishes, the display reverts to showing ‘Pb. n’ and the LED turns red again
Changing modes automatically stop playback.
An empty slot cannot be played back.
Each program slot can contain a maximum of 3000 codes with the time resolution of 0.1s during a maximum of 6553.5s or roughly 1h 49 min.
Record mode
Rotating the knob shows ‘rEc.0-rEc.9’ and ‘HF .0-HF. 9’. 0-9 indicates which slot is selected, the LED indicates if it’s used (red) or free (white). ‘rEc’ means that any switch sequence entered by the operator will be recorded and ‘HF’ means that all traffic will be recorded.
Recording a switch in ‘rec’ will use a repurposed key code but not a macro.
HF on the other hand will use a macro and it will also record any incoming RF traffic so it can also be used to eavesdrop on another master sending codes.
Worth noticing, slave units version 1 are capable of transmitting data when triggered. Disable that function while recording in HF mode to avoid unintentional traffic, alternatively edit the session before using it for playback.
The procedure to record a session is the same no matter the mode, it’s only what is recorded that differs.
During the recording, rotating the knob toggles between last sent code and elapsed time in seconds.
Recording a session
- Rotate the knob to select slot (red LED=occupied, white LED=empty)
- Press ‘play’ to start record (LED turns magenta). Rotate knob to toggle between last sent code or elapsed time.
- Press ‘play’ again to pause (LED turns blue), elapsed time pauses. Another press on ‘play’ resumes.
- Press ‘stop’ to end recording (LED turns red i.e memory now contains something).
The record timestamp will not change during a pause. Note also that the timestamp should only be used as a visual aid since it does not compensate for the device’s actual time drift. See under ‘Program editing’ below how to compensate timing in real life.
There is no mechanism to prevent recording over an already used program slot, so use with precaution.
Erasing a session
- Select slot to be erased
- Start recording by pressing ‘play’ (LED turns magenta)
- Stop recording by pressing ‘stop’ (LED turns white)
The program slot is now erased
Communication
The master supports remote communication with a PC through its USB-micro port.
The communication should be set to 230400 baud, 8 bits.
The master can be controlled through commands sent and there also exists a mechanism to download or upload memory content so that a recorded program slot can be edited or saved offline.
Command reference
Note below that address range ‘00’-‘FF’ and data range ‘00’-‘FF’ is permitted on version 2.
Multiple commands can be sent separated by a ; but the total length must be ≤ 16.
| Type | Syntax | What it does |
| !AADD | Send | Transmit one RF event: address `AA` (`80`-`FF`), data `DD` (`00`-`7F`) |
| Lwxyz or Lbbb | Set display | Show four hex digits `w x y z` on the display (temporary) or test the RGB led with the set bit sequence, e.g. L011 = cyan, L000 = off, L111 = white. |
| TAANN | Test burst | Transmit `NN` test events to address `AA` (`80`-`FF`) |
| V/v | Verbose on/off | Echo every transmitted RF event back to the terminal |
| D/d | Repeat on/off | Continuously re-send the last event while the link is idle |
| C | Clear counter | Reset the transmitted-event counter to 0 |
| Sx | Display mode | `S0` = show last sent address/data, `S1` = show event count |
| g | Dump key table | Print the current 128-entry key-code table |
| G | Reload + dump | Reload the key-code table from the built-in defaults, then print it |
| RxSSAAD1D2EE | Edit key table | Write one entry into the in-memory key-code table |
| PROG | Commit to flash | (not implemented on this hardware — see note below) |
| EAAAAADD | Write EEPROM | Write byte `DD` to EEPROM address `AAAAA` — Front panel’s chip normally, or the controller’s own internal EEPROM if `AAAAA`’s top bit is set (see below) |
| eAAAAA | Read EEPROM | Read the byte at EEPROM address `AAAAA`, same internal/external selection as `E` |
| I | I2C scan | Diagnostic: list I2C addresses that respond on Front panel’s bus |
| M/m/X/O/o/Y | Bulk transfer | Read/write macros and program slots in bulk — not for hand typing, see below |
| n | Macro count table | List every key’s macro step count in one reply — not for hand typing, see below |
| y | Program-slot count table | List every program slot’s entry count in one reply — not for hand typing, see below |
| rny/rs | Record control (keyboard) | Confirm (`y`) and start recording into program slot `n` (`0`-`9`) from the keyboard, or stop — same as Record mode’s Button 1/Button 2 (§3.6), usable from any front-panel mode |
| hny | Record control (RF traffic) | Confirm (`y`) and start recording into program slot `n` from RF traffic instead — stop with `rs` |
| pn/ps | Playback control | Start playing back program slot `n`, or stop — same as Playback mode’s Button 1/Button 2 (§3.5), usable from any front-panel mode; no confirmation needed, playback doesn’t erase anything |
| i | Device identity | Reply with `device.serial.version.revision.date` — firmware version/revision/build date and (on future hardware with the ID chip fitted) a per-unit serial number |
| U | RF module commands | Enter raw RF-module relay mode. (exit: type +++ and Enter or ESC on binary terminal). Used to communicate directly with RF module. USE WITH CARE, It may break things! |
| Bn | Baud rate | Switch the RF link’s baud rate (n=0..6: 2400/4800/9600/19200/38400/57600/115200) Temporary for session, defaults to 38400 on restart. |
| h | Help | Print this command reference to the terminal (also accepts `h0y`-`h9y`, see `hny` above) |
Command details
`!AADD` — Send one event.
`AA` is two hex digits, `80`–`FF`; `DD` is two hex digits, `00`–`7F`.
Note that in version 2, ‘00’-‘FF’ is valid in both fields.
Example: `!8002` queues address `0x80`,
data `0x02` — exactly as if a keypress mapped to that pair had happened.
Out of range (`AA` below `80`, or `DD` at or above `80`) replies `!?` and queues nothing — e.g. `!0701` (address `07` is too low).
`Lwxyz/Lbbb` — Set display.
Shows the four hex digits directly, one character each (not byte pairs). Example: `LBE99` shows `BE99`. This is temporary — the display reverts to its normal content (see `S`, below) as soon as the next event transmits.
The front panel RGB LED can also be tested by setting the RGB bit pattern directly e.g L110 sets the LED yellow and L000 turns it off.
The LED stays in that color until changed by other command or mode change.
`TAANN` — Test burst.
Queues `NN` events to address `AA` (`80`–`FF`), with data alternating `0x55`/`0x4A` (already valid data, no separate check needed there). Handy for soak-testing the RF link. `T8103` sends three events to address `0x81`: data `0x55`, `0x4A`, `0x55`. Out of range (`AA` below `80`) replies `T?` and queues nothing.
`V` / `v` — Verbose mode.
While on, every transmitted event (from any source — keyboard, front panel, or the host commands below) is echoed as one line: `TTTTAADD`, where `TTTT` is the time (in 5 ms ticks) since the previous event, and `AA`/`DD` are the address/data that went out. Off by
default.
`D` / `d` — Repeat mode.
`D` re-arms and continuously re-transmits whatever was last sent, any time the link goes idle — useful for RF range/reliability testing without babysitting the terminal. `d` turns it off (anything already queued still sends normally).
`C` — Clear counter.
Resets the transmitted-event counter used by `S1` (below) to 0.
`Sx` — Display mode.
Chooses what the display shows automatically after each transmission: `S0` = last address/data sent (two digits each),
`S1` = a running 4-digit hex count of events sent since the last `C`. Any other value freezes the display until it’s set back to `0` or `1`.
`g` / `G` — Dump the key-code table.
`g` prints the 128 entries currently held in memory; `G` first reloads them from the built-in
defaults (discarding any edits made with `R`), then prints. Each of the 128 lines is `R0SSAAD1D2EE` — switch number, address, key-down data, key-up data, and a reserved byte — which is exactly the format `R` accepts, so you can capture a dump, edit it, and feed it back.
`RxSSAAD1D2EE` — Edit one key-code entry.
`x` selects buffer `0` or `1`; any other value is silently ignored. This only edits the in-memory
copy — it has no effect on the running keyboard until committed with `PROG`.
`PROG` — Commit to flash.
Not implemented on this hardware revision; sending it replies with `PROG not implemented in this port`. Editing the key-code table with `R` currently has no lasting effect across a reboot.
`EAAAAADD` — Write EEPROM.
`AAAAA` is a 5-hex-digit byte address; `DD` is the byte to write there. No reply on success. This is raw, low-level EEPROM access — it bypasses the macro/program-slot structure entirely, so it’s really a diagnostics/field-programming tool rather than something you’d use in normal operation. Which chip: normally this targets Front panel’s own EEPROM (up to `0x1FFFF` on the standard AT24CM01) — but if `AAAAA`’s top bit is set (i.e. the address is
`80000`-`FFFFF`), it targets the controller’s own built-in EEPROM instead (the remaining bits select the byte within it), which is always present regardless of whether a Front panel accessory is attached at all. This is how the RF-triggered playback/record feature below stores its configuration — deliberately kept off Front panel’s chip so it survives even without one attached. Faults (see below) if the address is beyond the size of whichever EEPROM it targets, or — for the external, front panel-chip case only — if no accessory is attached.
`eAAAAA` — Read EEPROM.
Same addressing (including the internal/ external selection above) as `E`; replies with the byte at that address as 2 hex digits, e.g. `e00000` might reply `0A`.
Both `E` and `e` fault the same way unrecognized commands do if the address is out of range, or (for an external-EEPROM address) no front panel accessory is present: the command letter echoed back followed by `?` (`E?` or `e?`).
RF-triggered playback/record/stop.
The controller can now be configured to start or stop program-slot playback or recording purely by *receiving* a specific address/data pair over RF — useful for letting a remote unit (including the newer RC1180-based slave hardware) trigger a show sequence without a host computer involved at all. This is configured via 7 bytes in the controller’s internal EEPROM (write them with `E`, using the internal-EEPROM addressing above).
The address and data for the remote trigger commands are stored in the following locations:
| AddressPB | 0x115 |
| DataPB | 0x116 |
| AddressRec | 0x117 |
| AddressHF | 0x118 |
| DataRec | 0x119 |
| AddressStop | 0x11A |
| DataStop | 0x11B |
Note that these are the memory locations, not the addresses the command responds to!
By default they are inactive (empty memory is FF which is illegal data) until a valid value has been written.
Example:
To set playback trigger address to 0x85 and the data to 0x40, write the command E8011585 and E8011640. This means that if the master receives the code 8540, it will play back program slot 0 (slot 1 = data 0x41 to slot 9 = data 0x49).
`I` — I2C scan
A diagnostic, not something you’d use in normal operation: lists which I2C addresses answer on Front panel’s bus, e.g. I2C1 scan: 0x50 — 1 device(s)`, or `(none)` if nothing responds at all. Useful for telling apart “the module isn’t there/isn’t wired up” from “it’s there, but answering at an address the firmware doesn’t expect” — the AT24CM01 EEPROM should show up at `0x50` or `0x51`. Works even if front panel wasn’t detected at boot, and on demand (rather than only printing once at startup) specifically so you don’t have to race your terminal software to catch the output before it scrolls past.
`M`/`m`/`X`/`O`/`o`/`Y` — Bulk macro/program transfer.
Not something you’d type by hand — these exist so a PC application can read out and write back every macro and program slot’s contents, for offline editing (see `program-editing.md`). Each key’s press and release slots (§3.4) are independent, and `M`/`m`/`X` reach both: the key-index byte’s top bit selects which slot, `download_to_json.py` handles this automatically. Unlike the rest of this protocol, these always reply with a 2-byte acknowledgment — `!` for success, `?` for a fault — even when nothing went wrong, since they’re meant to be driven by software doing many requests in a row rather than a person typing occasional commands.
`n` / `y` — Macro/program-slot count tables.
Also not for hand typing — companions to the bulk-transfer commands above, letting a PC application ask “which keys/slots actually have anything in them” in one reply instead of checking all 128 keys or 10 slots individually.
`h` — Help.
Prints a command reference to the terminal + device information
`rny` / `hny` / `rs` / `pn` / `ps` — Record/playback control.
Lets a host script drive Record mode or Playback mode without touching the front panel: `rny` starts fresh keyboard-source recording into slot `n` (`0`-`9`), `hny` the same but from RF traffic, `rs` stops whichever of the two is running, `pn` starts slot `n` playing back, `ps`
stops it. Everything downstream behaves exactly as if you’d walked the knob there and pressed Button 1/Button 2 yourself — the same elapsed-time ceiling, the same slot-full behavior, and the front panel’s own Stop button still works too.
`r`/`h` require the `y` right after the slot digit to actually start
(`r4y`, not `r4`) — starting a recording erases the slot immediately, and unlike walking the knob to Record-select yourself (where you’d see the slot’s own white/red LED before pressing Button 1), a command arriving over the host link has nothing to catch a mistake first. `r4` on its own is rejected the same way a bad command is (`r?`) — it does **not** wait
for a follow-up confirmation on a separate line, since a bare `y` sent by itself would be mistaken for the existing `y` command (program-slot count table, above). This costs nothing extra for scripting: just always send the confirmed form, e.g. `r4y;p4` on one line records slot 4 and — once it stops, whether by the front-panel Stop key, the elapsed-time limit, or a later `rs` — plays it straight back. `p` needs no confirmation; playback never erases anything.
These commands can arrive while the front panel is doing something else entirely
mid-Macro-mode-recording, for instance — and will switch modes and take over immediately, discarding whatever was in progress, the same as if you’d used Select to leave that mode by hand. Fault (`r?`/`h?`/ `p?`) if no Front panel accessory is attached, `n` isn’t a single digit `0`-`9`, or (for `r`/`h`) the `y` is missing.
`i` — Device identity.
Replies with one line, `device.serial.version.revision.date` (decimal fields, literal dots, date as `YYYYMMDD`) — e.g. `0.0.1.10.20260831`. `device` is `0` on this (master) firmware. `serial` reads `0` here too: it’s meant for a per-unit ID chip that doesn’t exist on this hardware (exists on slave unit version 1). ‘version/’revision’/`date` identify the firmware build itself.
Always succeeds.
Unrecognized commands get echoed back with a `?`, e.g. sending `Q` replies `Q?`.
Example session
> (power-up)
< BOOT
> V enable verbose mode
> !8002 send address 0x80, data 0x02
< 00018002 <- verbose echo: 1 tick since last event, addr 80, data 02
> S1 show the event counter on the display
> T8103 burst-test 3 events to address 0x81
< 00048155
< 0001814A
< 00018155
> C clear the event counter
…
Bring-up of new units
New builds of both the master and slave must be configured before first use.
The RC1180-RC232 modules must be configured before use. The factory PACKET_TIMEOUT is 2 s, which delays every event.
Connect the board, set the terminal to 230400 8N1 CR and then send W0F04y and W1001y to set packet length 4 and a 32 ms timeout. Check with w10 — 7C means unconfigured, 01 means done. End session with +++.
The master must also be configured to know that it actually has a program memory.
Send the following commands to configure it:
E000000A (size code 0x0A)
E0000100 (module ID 0x00)
E0000202 (hw revision 0x02)
Program editing
A set of Python tools is supplied to facilitate bulk data transfer and offline editing.
Although all .py scripts are necessary for operation, the user only needs to interact with lasershow.py.
usage:
>lasershow.py [-h] (--read PORT | --write PORT) [--name NAME] [--alias FILE] [--timing TIMINGFILE] [--baud BAUD] [--yes]
lasershow.py – single end-user entry point wrapping download_to_json.py / json_to_excel.py / excel_to_json.py into one read/write round trip, driven by --name/--alias defaults instead of typing explicit file paths every time. The individual scripts still exist for debugging (they print more detail per step); this is the “just get the show on/off the device” front door.
--read PORT
Downloads every macro and program slot off the device on PORT, writes NAME.json, then converts that file straight into NAME.xlsx (json_to_excel.py’s layout) — one command instead of running download_to_json.py and json_to_excel.py back to back.
ALIASFILE (default “aliases.json”), if it exists, adds label text throughout; if it doesn’t exist, downloading proceeds without it (raw hex only) rather than erroring — same “only if it exists” rule for the default NAME.json/.xlsx pair below.
--write PORT
Uploads a show to the device on PORT. If NAME.xlsx exists, it’s converted to NAME.json first (excel_to_json.py’s validation applies, all-or-nothing — a bad row aborts before anything is written to either the .json file or the device), and that freshly-converted file is what gets uploaded, keeping the two in sync; NAME.xlsx is treated as the source of truth whenever it exists. If NAME.xlsx doesn’t exist, NAME.json is uploaded directly.
Upload makes the device an EXACT MIRROR of the file: every one of the 256 macro slots (128 keys x 2 edges) and 10 program slots ends up either matching the file or cleared if the file doesn’t mention it (skipping anything already empty on both sides, so this doesn’t cost extra round trips for a typical show). This is DESTRUCTIVE — content on the device the file doesn’t mention is erased. Prompts for confirmation first, showing the planned write/clear counts, unless --yes is given. Also checks the live device’s actual program-slot capacity (‘y’) against the file’s slot sizes before writing anything — a file from a differently-sized EEPROM would otherwise fail partway through instead of being caught up front.
Defaults: NAME=”lasershow” -> lasershow.json / lasershow.xlsx in the current directory. ALIASFILE=”aliases.json” (--read only).
Examples:
python lasershow.py --read COM5
python lasershow.py --read COM5 --timing master1.json --name friday_show
python lasershow.py --write COM5
python lasershow.py --write COM5 --yes # skip the confirmation prompt, for scripting
options:
-h, --help show this help message and exit
--read PORT download from the device on PORT into NAME.json/.xlsx
--write PORT upload NAME.xlsx (or NAME.json if no .xlsx) to the device on PORT
--name NAME file basename (default: lasershow)
--alias FILE device alias file, used only if it exists (default: aliases.json); –read only
--timing TIMINGFILE drift correction to apply to the device’s exported timestamp to account for drift; if used for download, also apply on upload.
--baud BAUD
-y, --yes write only: skip the confirmation prompt
The microcontroller used in the master is pin-limited because of compatibility with the old keyboard panel. Therefore an external crystal can’t be used for timekeeping reverting to rely on the internal oscillator. That oscillator has higher drift than an external crystal and will show up as an error of a few seconds per hour in the timekeeping. Each master has a label below it with the measured drift rate which should be applied in the timing.json compensation file. This allows the timestamp in the Excel-file to correspond to an actual stopwatch. If the timing correction is applied on the downloaded file, it must also be applied when it’s uploaded again to the device. It must also be applied to any manually created show to correlate the device’s timing to a clock.
For this reason the elapsed time counter should only be used as a visual reference, it cannot show the compensated time.
Note that the user file (NAME.xlsx) cannot be uploaded while open in Excel (file lock).
A default.xlsx is supplied mirroring the active keys as defined in version 0 but now defined as repurposed keys (same function, different mechanism).
Transferring programs/macros will take a little while and the display may flicker during the transmission.