So recently (actually three months ago), I bought a used Sony PXW-FS7. It’s very heavy, but also produces great photos, which is what I wanted from it.

(It looks like rigging, I have less stuff. Picture from the internet)
it has this Strange if you’re not used to ENG cameras The side grip for holding the camera when it’s on your shoulder and then controlling it is quite ergonomic. My only real problem with it is that some of the buttons can Only This can be accessed using removable accessories. Especially user buttons 4-6 (If you know the old Sony menu system you know the need for shortcut buttons.) This becomes apparent when No Shooting from the shoulder, for example from the hip. You hold it by the top handle and can’t reach the button I use for focus zooming Necessary When using a camera that is Zero Usable auto focus (with manual lenses).
I thought a bit and came to the conclusion that since this is a device used in an industry that expects expandability and modularity (to a degree, but think about RED’s business model for example) and the camera is already over 10 years old, it wouldn’t be an exceptionally complex/secure protocol that the camera uses to communicate with the electronics in the grip.

And so, without attempting anything resembling this complexity and only knowing how an oscilloscope works in theory, I made it my mission to reverse engineer the protocol and develop my own hardware to place another set of buttons on the top handle (or wherever, to be honest.)

I had 23N27 who coincidentally was in Kaosdorf and he held my hand a bit so we immediately worked out that it looked to be a serial protocol Weird Same as UART at 9600 baud. Of course we only had one wire which was a little strange to me at first but that means it is an open drain bus. The camera toggles the voltage to a sort of “placeholder” for the eight bytes of all zeros and whenever it gets low, the catch can pull it up.
This is very clearly visible when pressing one of the buttons on the grip. The third (and fourth?) byte is changed by the grip to reflect one of the unique IDs present in each button and dial actuation. This signal remains quite permanent as long as the button is pressed. Only one action can occur at a time.

After a long and annoying process of getting the traces from the oscilloscope into Sigrock/Pulseview (I ordered a logic analyzer immediately after doing this once) and recording for a while we can see the difference between pressing one of the buttons and the idle signal. There’s also a cyclical thing happening every two packets, but it doesn’t matter to the camera.

From this, we can also extract the first identifier for a single button! the second byte is 0xd7 and third 0x48,
first simulator

For my first prototype I used an STM32 F-series board I had laying around. We only need two pins (for which the STM32 is completely overkill): GND and a GPIO. The only thing that really makes it harder than turning it on/off is that we need to both read and write the voltage, but this protocol is so slow that you can literally just change the type on the fly lol – or as we’re doing, just making it both in and output.
So finally we arrived at some code!
fn write_byte(io: &mut P, byte: u8, delay: &mut Delay) {
let theoretical_delay_us = 104;
let write_duration_us = 3;
let delay_us = theoretical_delay_us - write_duration_us;
for i in 0..8 {
if (byte >> i) & 1 == 1 {
io.set_high().ok();
} else {
io.set_low().ok();
}
delay.delay_us(delay_us);
}
}
pub fn write_lanc(io: &mut P, cmd: &LancCmd, delay: &mut Delay) {
let repeat_count = 5;
for _ in 0..repeat_count {
// wait for start bit
loop {
let last_low = embassy_time::Instant::now();
while !io.is_low().unwrap() {
nop();
}
let elapsed = last_low.elapsed();
if elapsed.as_micros() > 5000 {
// debug!("Start bit detected after {} us", elapsed.as_micros());
break;
}
}
delay.delay_us(104);
write_byte(io, cmd.mode, delay);
let _ = io.set_high();
loop {
let last_low = embassy_time::Instant::now();
while !io.is_low().unwrap() {
nop();
}
let elapsed = last_low.elapsed();
if elapsed.as_micros() > 200 {
break;
}
}
delay.delay_us(104);
write_byte(io, cmd.cmd, delay);
let _ = io.set_high();
}
}
Disclaimer: This is not strictly following best practices as I’m not very good at Rust, but this version works and works even after completely changing the architecture to WCH32V003. Basically what we are doing is waiting for the start bit (each packet is kept quite separate) and then overwriting the second byte Method and with the third PermissionMode is always constant, depends on command button)
pcb

As the setup with STM32 on breadboard is not perfect run and gun I started working on a PCB based on the WCH32V003 because it seemed wasteful to use a full-on stm32 with 30 GPIOs and a lot of things I don’t need. Also the STM32F103C8T6 I used costs over $1 on LCSC while a pack of 50 WCH costs less than $10.
I wanted it to fit on the top handle crossbar between the top handle and the monitor mount, so I was quite short on space. I was pretty sure I could fit a 25mm PCB on it.
I came up with a design in an afternoon or two and it was shipped from China. The first time after soldering it I noticed it turned on and took the firmware, but very rarely and none of the buttons worked. The footprint I used for the buttons was misoriented so I had to figure out their ground connection somewhere else.
It still shuts down randomly and flashing/debugging only works when pressing random buttons on the PCB and only after trying 20 times. After several days of debugging and looking at datasheets and my Kicad project I noticed that the reset and ground pins were floating. So I fixed that too :3 and ported my STM32 Rust code to this MCU, which was surprisingly easy coming from C development as both have crates implementing embedded_hal traits.
This is what the final PCB looks like in its burdened-down form. Not the prettiest but working reliably. The only real problem now is that none of the 2.5mm audio jacks work because I ordered ones that don’t expose all three pins so have to reflash.

hardware
This PCB is still not really operator friendly so I need a housing. I designed a clamp that clamps onto a 15mm rod that is often used when rigging cameras and a two part design to hold the PCB and connect the surfaces for clicking the button.

I’d make it easier to make it out of aluminum using hand tools (hacksaw, drill, tap, reamer), but the 3D printed version I made for the prototype is proving to be quite sturdy.
appendix
Link
git repo
Table: button ID
| Identification | button |
|---|---|
0x48 |
user button 4 |
Function: If I’m interested in inverting other buttons I’ll add them here.