AudunVN / zoom-h6-remote

Work in progress: This project will attempt to document the pinout and communications protocol of the Zoom H6 remote control, and then provide a wireless ESP8266-based alternative.
2 stars 0 forks source link

Remote notes #3

Open guyfawcus opened 4 years ago

guyfawcus commented 4 years ago

Pinout

TRRS Colour Description
Tip Blue Remote RX
Ring 1 Red Remote TX
Ring 2 Green GND)
Sleeve Brown +3V

(Colours are of the enameled wires in the extension cable that is part of the APH-6 accessory pack)

image (Image from Blueduino-RC4)

Signalling

Signalling is 3.3V TTL serial at 2,400 bit/s (baud) 8N1 (8 Data bits, No parity and 1 stop bit) LSB (Least Significant Bit first)

Protocol

On boot, the remote sends a zeroed byte (0x00) every 32ms until it receives a 0x82 (28.3ms between the end of one byte and the start of the next)

The handshake goes like this (including the 'last' zeored byte and the first reply):

Device ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Remote ... 00 C2 E1 31 2E 30 30 A1 80 80
Recorder 82 83 80 81

Bit Idle Button
1 1 <Always 1>
2 0 <Always 0>
3 0 <Always 0>
4 0 Stop
5 0 Next
6 0 Back
7 0 Play/Pause
8 0 Record

Second byte

Bit Idle Button
1 0 Vol Up
2 0 Vol Down
3 0 Ch. 4
4 0 Ch. 3
5 0 Ch. 2
6 0 Ch. 1
7 0 Right
8 0 Left

For example, pressing stop will transmit: 10010000 00000000 followed by: 10000000 00000000

and pressing 1 and 2 will transmit: 10000000 00001100 followed by: 10000000 00000000

Power

guyfawcus commented 4 years ago

Hi there by the way! If your still here at least :laughing: sorry I'm a bit late to the game!

guyfawcus commented 4 years ago

Here's a start for reversing the RX protocol:

N.B. The spacing and pipes are just there for emphasis. All of the pairs are hex and are grouped every 3 bytes (84 xx xx) for clarity.

L - ON  - 84 20 00  84 20 00  84 28 00  84 28 00
L - OFF - 84 28 00  84 08 00  84 08 00  84 00 00

R - ON  - 84 08 00  84 08 00  84 28 00  84 28 00
R - OFF - 84 28 00  84 20 00  84 20 00  84 00 00

1 - ON  - 84 02 00  84 02 00
1 - OFF - 84 02 00  84 00 00

2 - ON  - 84 00 20  84 00 20
2 - OFF - 84 00 20  84 00 00

3 - ON  - 84 00 08  84 00 08
3 - OFF - 84 00 08  84 00 00

4 - ON  - 84 00 02  84 00 02
4 - OFF - 84 00 02  84 00 00

L SOLO ON  - 84 28 00 |84 68 00  84 68 00 |84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00
L SOLO OFF - 84 38 00 |84 18 00 |84 38 00  84 38 00  84 38 00 |84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00

R SOLO ON  - 84 28 00 |84 68 00  84 68 00 |84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00  84 78 00
R SOLO OFF - 84 68 00 |84 60 00  84 60 00 |84 20 00 |84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00  84 28 00

1 SOLO ON  - 84 02 00  84 02 00  84 02 00  84 02 00  84 02 00 |84 06 00  84 06 00  84 06 00  84 06 00  84 06 00  84 06 00  84 06 00
1 SOLO OFF - 84 06 00  84 06 00  84 06 00  84 06 00  84 06 00 |84 02 00  84 02 00  84 02 00  84 02 00  84 02 00  84 02 00  84 02 00

2 SOLO ON  - 84 00 20  84 00 20  84 00 20  84 00 20  84 00 20  84 00 20  84 00 20 |84 00 60  84 00 60  84 00 60  84 00 60  84 00 60
2 SOLO OFF - 84 00 60  84 00 60  84 00 60  84 00 60  84 00 60  84 00 60  84 00 60 |84 00 20  84 00 20  84 00 20  84 00 20  84 00 20

3 SOLO ON  - 84 00 08  84 00 08  84 00 08  84 00 08  84 00 08  84 00 08  84 00 08  84 00 08  84 00 08 |84 00 18  84 00 18  84 00 18
3 SOLO OFF - 84 00 18  84 00 18  84 00 18  84 00 18  84 00 18  84 00 18  84 00 18  84 00 18  84 00 18 |84 00 08  84 00 08  84 00 08

4 SOLO ON  - 84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02  84 00 02 |84 00 06
4 SOLO OFF - 84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06  84 00 06 |84 00 02

P.S. This should all be correct but some of this was hand transcribed from my scopes decoder because I'm currently working through an issue with sigrok and my DS1054z :roll_eyes: .

P.P.S. Just pasting this here because I can't seem to work it out yet. It just seems so convoluted, especially considering how simple the TX commands are and how easy they were to figure out.

guyfawcus commented 4 years ago

It's a ̶U̶n̶i̶x̶ bit manipulation system, I know this!

As I suspected, like the TX commands, the RX communication is based on bit manipulation. Haven't quite got my head around some of the whys of the seemingly superfluous RX data yet, but I have confirmed that if you hook up a 3.3V TTL serial connection to the remote and use the following table, you can light up whatever LEDs you want! :tada:

Make sure it's 3.3V logic, and unplug the recorder. This is only for testing the remote!

decode-table

For example, to light up the LEDs for channels 1-4 in red using Python:

import serial
import time

s = serial.Serial('/dev/ttyUSB0')
s.baudrate = 2400

# Initialise the connection (only have to do this once)
s.write(bytes([0b10000010]))
time.sleep(0.5)
# Now send the data
s.write(bytes([0b10000100, 0b00000010, 0b00101010]))
AudunVN commented 4 years ago

Whoa; thank you very much for your feedback! I haven't poked at this project or my H6 in a good while, but this is making it very tempting to say hi to it again...

guyfawcus commented 4 years ago

Could I tempt you even more with this?.. Zoom-H6-OLA-bridge

You can now control the LEDs on the remote with lighting control software or one of these: :rofl:

Stephencraft [CC BY-SA (https://creativecommons.org/licenses/by-sa/4.0)]

I think I need to get back on track...

mattogodoy commented 4 years ago

Hi guys!

Any news about this? I'm also really interested in being able to control my Zoom H6 from my computer. I've connected it using an FTDI USB adapter, and I've tried sending some codes to it, but no luck. Also, I don't get any serial activity from it when I start recording or enable a channel, which is the strangest part.

@guyfawcus, were you able to find something else?

guyfawcus commented 4 years ago

@mattogodoy - Sorry dude, got a little distracted with another project (yet again!)

I stopped after getting the remote to light up like a Christmas tree and never actually implemented any control to the recorder (despite that being the main goal in all of this :roll_eyes: )

I'll take another look at this tonight and hopefully get you some code for tomorrow :ok_hand:

In the mean time, I've got a few questions to make sure that your on the right track:

If you're talking to the recorder, don't hook up the 3.3V pin. You should only need GND/TX/RX. It'd probably be fine but I wouldn't chance it because it's unnecessary and could lead to a bad day, especially if the adapter is in 5V mode!.

Just had a thought - I seem to remember that unless you send a zeroed byte (0x00) to the recorder it won't send any commands back. This initialisation is usually taken care of by the remote. Read though this post again, specifically the protocol section, that should cover most of it.

I know how tricky this sort of stuff can be if you don't have things like a scope and a remote to double check and test things but when boiled down it should be a pretty simple thing to get going, so after I write some docs you shouldn't have any issues (just try not to damage anything in the meantime :stuck_out_tongue_winking_eye:)

mattogodoy commented 4 years ago

Thanks a lot for your reply! If you get to make it work you'll make me a really happy man 😃

I'll answer your questions in order:

Here's a sample of the code I'm using:

import serial
import time

s = serial.Serial('/dev/cu.usbserial-alcdut1', 2400, parity='N', stopbits=1)

# Create a REC button press command
rec_packet = bytearray()
rec_packet.append(0x81)
rec_packet.append(0x00)

# Create a button release command
release_packet = bytearray()
release_packet.append(0x80)
release_packet.append(0x00)

# Send REC, wait for a bit and release
s.write(rec_packet)
time.sleep(0.1)
s.write(release_packet)

# Close serial connection
time.sleep(0.1)
s.close()

I did not know about the initialisation byte, I'll take a look at the link you posted.

If you manage to make it work please let me know! I'll report back if a get something.

Thanks again for the info 💪

guyfawcus commented 4 years ago

No problem at all, I love this stuff!

Cheers for that, looks like you're all set :+1:

The problem was indeed the handshake, the recorder won't do anything or send any data without it. I need to do a bit more testing but I got it to work so I thought I'd send over what I had so that you can get cracking.

s = serial.Serial('/dev/ttyUSB0') s.baudrate = 2400 s.timeout = 0.032


* Initialise the connection with the handshake 
``` python
handshake_complete = False

while handshake_complete != True:
    s.write(b'\00')

    if s.read(1) == b'\x82':
        s.write(b'\xC2')

        if s.read(1) == b'\x83':
            s.write(b'\xE1\x31\x2E\x30\x30')

            if s.read(1) == b'\x80':
                s.write(b'\xA1')

                if s.read(1) == b'\x81':
                    s.write(b'\x80\x80')
                    handshake_complete = True

Create a button release command

release_packet = bytearray() release_packet.append(0x80) release_packet.append(0x00)

Send REC, wait for a bit and release

s.write(rec_packet) time.sleep(0.1) s.write(release_packet)

guyfawcus commented 4 years ago

I'll try and get round to coding something to check the reply's using some stuff I wrote a while ago to light up the LEDs on the remote[1], but in the meantime I've verified that all of the bytes that I reverse engineered initially[2] are correct:

stop =       bytearray([0b10010000, 0b00000000])
forward =    bytearray([0b10001000, 0b00000000])
reverse =    bytearray([0b10000100, 0b00000000])
play_pause = bytearray([0b10000010, 0b00000000])
record =     bytearray([0b10000001, 0b00000000])

vol_up =     bytearray([0b10000000, 0b10000000])
vol_down =   bytearray([0b10000000, 0b01000000])

ch4 =        bytearray([0b10000000, 0b00100000])
ch3 =        bytearray([0b10000000, 0b00010000])
ch2 =        bytearray([0b10000000, 0b00001000])
ch1 =        bytearray([0b10000000, 0b00000100])
right =      bytearray([0b10000000, 0b00000010])
left =       bytearray([0b10000000, 0b00000001])

release_button = bytearray([0b10000000, 0b00000000])

def send(command):
    s.write(command)
    s.write(release_button)

# send(record)
mattogodoy commented 4 years ago

Ha! I was just trying when you posted the last message. IT WORKS!

The next step is to integrate this into a microcontroller, but having the logic and the commands it should not be that hard.

Thanks a lot for your help! You rock 🤘

guyfawcus commented 4 years ago

Glad to hear you got it working! Can't wait to read about it on your blog (love the airsoft bomb :stuck_out_tongue_winking_eye: ).

Just installed hacker-sounds as well by the way... hilarious :rofl: not sure how happy my other half is going to be though considering we're both working from home now and sitting at the same table!

mattogodoy commented 4 years ago

Hahaha I'm glad you like it. It makes you 200% more productive :D

guyfawcus commented 4 years ago

Here's some code* that I've been using to check things out. It's far from perfect but it has proved useful, it allows you to send commands and read the state back. Hope it's useful for you too! :man_technologist:

* same code on nbviewer

mattogodoy commented 4 years ago

Cool! That is in fact really useful to me. I'll try it tomorrow. Thanks a lot!

guyfawcus commented 4 years ago

No worries :metal: just added another little helper function by the way, it allows you to press multiple buttons simultaneously.

guyfawcus commented 4 years ago

Ooh, and just fixed a bug with displaying solo messages.

guyfawcus commented 4 years ago

A couple of small notes for things I've come across:

mattogodoy commented 4 years ago

That's great to know. Thanks for sharing!

Rangermania commented 3 years ago

Hello!

Following the discussion here I was able to start and stop recording on my Zoom H5 using a STM32 Nucleo Board (042K6)

1626881125646

Seems like Zoom H5 is skipping one step of the handshake though

            if s.read(1) == b'\x80':
                s.write(b'\xA1')

I made a simple code for now which sends start stop record with push of a button. I needed to create a simple debounce filter so any other button press in 1 second is ignored.

Next step I will try to trigger this with the flashing LED of my GO-PRO so I can record both video and audio just by using the GoPro remote!

The code is as follows:

#include "mbed.h"

InterruptIn btn(PA_12);
Serial serial(PA_9, PA_10);  // tx, rx
DigitalOut myled(LED1);

char request=0;

static void toggleLed()
{
    if(myled==1)
    {
        myled = 0;
    }
    else
    {
        myled = 1;
    }
}

static void onButtonPress()
{
    /*__disable_irq();
    toggleLed();
    char command=0x81;
    //device.printf("%x",command);
    serial.putc(command);
    command=0x00;
    serial.putc(command);
    wait(0.2); // 200 ms
    command=0x80;
    //device.printf("%x",command);
    serial.putc(command);
    command=0x00;
    serial.putc(command);
    wait(1);
    __enable_irq();*/
    request=1;
}

static void sendrecord()
{
    char command=0x81;
    //device.printf("%x",command);
    serial.putc(command);
    command=0x00;
    serial.putc(command);
    wait(0.2); // 200 ms
    command=0x80;
    //device.printf("%x",command);
    serial.putc(command);
    command=0x00;
    serial.putc(command);
    wait(1);
}

int main() {
    serial.baud(2400);
    btn.fall(&onButtonPress);
    char handshake_complete=0;
    char rcv;

    wait(1);
    toggleLed();
    wait(1);
    toggleLed();
    wait(1);
    toggleLed();
    while (handshake_complete != 1)
    {
        serial.putc(0x00);
        rcv=serial.getc();
        if(rcv==0x82)
        {
            serial.putc(0xC2);
            rcv=serial.getc();
            if(rcv==0x83)
            {
                serial.putc(0xE1);
                serial.putc(0x31);
                serial.putc(0x2E);
                serial.putc(0x30);
                serial.putc(0x30);
                rcv=serial.getc();
                if(rcv==0x81)
                {
                    serial.putc(0x80);
                    serial.putc(0x80);
                    handshake_complete=1;
                }
            }
        }
        wait(1);
        toggleLed();
    }

    while(1)
    {
        wait(0.2); // 200 ms
        toggleLed();

        if(request==1)
        {
            sendrecord();
            request=0;    
        }
    }

}

Thanks a lot!

z-l-p commented 2 years ago

Your work helped me immensely while I've been trying to build an Arduino-based controller for my Zoom H5. I intended to build a timer-based recording system (think of it like audio time-lapse, or an audio data-logger) but there's a hitch: The Zoom H5 can only record 500 files per folder. It won't automatically roll over to the next folder, so you must manually select a new folder in the menu every 500 files if you want to keep it going. Just mentioning it here in case it trips up somebody else.