Skip to content

Instantly share code, notes, and snippets.

@guyfawcus
Last active May 3, 2021 01:17
Show Gist options
  • Select an option

  • Save guyfawcus/6c9c3fe966322494fbaa1589de68624d to your computer and use it in GitHub Desktop.

Select an option

Save guyfawcus/6c9c3fe966322494fbaa1589de68624d to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Zoom H6 remote control"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import serial\n",
"import time\n",
"from collections import namedtuple\n",
"\n",
"s = serial.Serial('/dev/ttyUSB1')\n",
"s.baudrate = 2400\n",
"s.timeout = 0.032"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Remote to Recorder\n",
"####################\n",
"stop = bytearray([0b10010000, 0b00000000])\n",
"forward = bytearray([0b10001000, 0b00000000])\n",
"reverse = bytearray([0b10000100, 0b00000000])\n",
"play_pause = bytearray([0b10000010, 0b00000000])\n",
"record = bytearray([0b10000001, 0b00000000])\n",
"\n",
"vol_up = bytearray([0b10000000, 0b10000000])\n",
"vol_down = bytearray([0b10000000, 0b01000000])\n",
"\n",
"ch4 = bytearray([0b10000000, 0b00100000])\n",
"ch3 = bytearray([0b10000000, 0b00010000])\n",
"ch2 = bytearray([0b10000000, 0b00001000])\n",
"ch1 = bytearray([0b10000000, 0b00000100])\n",
"right = bytearray([0b10000000, 0b00000010])\n",
"left = bytearray([0b10000000, 0b00000001])\n",
"\n",
"release_button = bytearray([0b10000000, 0b00000000])\n",
"\n",
"\n",
"# Recorder to remote\n",
"####################\n",
"Button = namedtuple('Button', 'name value')\n",
"\n",
"play = Button(value=bytearray([0b00000000, 0b00000001]), name='play')\n",
"rec = Button(value=bytearray([0b00000001, 0b00000000]), name='record')\n",
"\n",
"# r = record = red\n",
"rl = Button(value=bytearray([0b00100000, 0b00000000]), name='rl')\n",
"rr = Button(value=bytearray([0b00001000, 0b00000000]), name='rr')\n",
"r1 = Button(value=bytearray([0b00000010, 0b00000000]), name='r1')\n",
"r2 = Button(value=bytearray([0b00000000, 0b00100000]), name='r2')\n",
"r3 = Button(value=bytearray([0b00000000, 0b00001000]), name='r3')\n",
"r4 = Button(value=bytearray([0b00000000, 0b00000010]), name='r4')\n",
"\n",
"# s = solo = yellow\n",
"sl = Button(value=bytearray([0b01100000, 0b00000000]), name='sl')\n",
"sr = Button(value=bytearray([0b00011000, 0b00000000]), name='sr')\n",
"s1 = Button(value=bytearray([0b00000110, 0b00000000]), name='s1')\n",
"s2 = Button(value=bytearray([0b00000000, 0b01100000]), name='s2')\n",
"s3 = Button(value=bytearray([0b00000000, 0b00011000]), name='s3')\n",
"s4 = Button(value=bytearray([0b00000000, 0b00000110]), name='s4')\n",
"\n",
"# p = play = green\n",
"pl = Button(value=bytearray([0b01000000, 0b00000000]), name='pl')\n",
"pr = Button(value=bytearray([0b00010000, 0b00000000]), name='pr')\n",
"p1 = Button(value=bytearray([0b00000100, 0b00000000]), name='p1')\n",
"p2 = Button(value=bytearray([0b00000000, 0b01000000]), name='p2')\n",
"p3 = Button(value=bytearray([0b00000000, 0b00010000]), name='p3')\n",
"p4 = Button(value=bytearray([0b00000000, 0b00000100]), name='p4')\n",
"\n",
"solo_bytes_list = [sl, sr, s1, s2, s3, s4]\n",
"\n",
"data_bytes_list = [play, rec,\n",
" rl, rr, r1, r2, r3, r4,\n",
" pl, pr, p1, p2, p3, p4]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def to_bin(byte):\n",
" '''Handy function for formatting a byte as binary'''\n",
" return bin(byte)[2:].zfill(8)\n",
"\n",
"\n",
"def combine(commands = []):\n",
" '''This does a bitwise 'or' on each byte to comine commands.\n",
" For example: ch1 = 10000000 00000100\n",
" ch2 = 10000000 00001000\n",
" result = 10000000 00001100 \n",
" '''\n",
" final_command = bytearray([0b00000000, 0b00000000])\n",
" \n",
" for command in commands:\n",
" byte_one = final_command[0] | command[0]\n",
" byte_two = final_command[1] | command[1]\n",
" final_command = bytearray([byte_one, byte_two])\n",
" \n",
" return final_command\n",
"\n",
"\n",
"def send(command):\n",
" '''Send a command to the recorder and return the status'''\n",
" s.reset_input_buffer()\n",
" s.write(command)\n",
" s.write(release_button)\n",
" time.sleep(0.5)\n",
" while s.in_waiting:\n",
" print(get_data())\n",
"\n",
"\n",
"def get_data():\n",
" '''Intended to be run in a loop'''\n",
" init_byte = s.read(1)\n",
" \n",
" # If the there was no data, start over\n",
" if init_byte == b'':\n",
" return\n",
"\n",
" # We can ingore these messages as they don't contain data\n",
" elif init_byte == b'\\x86':\n",
" rx_bytes = s.read(2)\n",
"\n",
" # These are the bytes we're looking for\n",
" elif init_byte == b'\\x84': \n",
" rx_bytes = s.read(2)\n",
" \n",
" # Check the 'clear all' case\n",
" if rx_bytes == b'\\x00\\x00':\n",
" return ['clear']\n",
"\n",
" else:\n",
" state = []\n",
"\n",
" # First, check if any channels are in 'solo' mode\n",
" for data_bytes in solo_bytes_list:\n",
" if rx_bytes[0] & data_bytes.value[0] == data_bytes.value[0] and rx_bytes[1] & data_bytes.value[1] == data_bytes.value[1]:\n",
" state.append(data_bytes.name)\n",
" \n",
" \n",
" # Then check all of the other possible values\n",
" for data_bytes in data_bytes_list:\n",
" if rx_bytes[0] & data_bytes.value[0] == data_bytes.value[0] and rx_bytes[1] & data_bytes.value[1] == data_bytes.value[1]:\n",
" # Ignore data if the channel is soloed\n",
" if 's' + data_bytes.name[-1] not in state:\n",
" state.append(data_bytes.name)\n",
"\n",
" return state\n",
"\n",
" else:\n",
" # Cover any unforseen data (haven't come across any yet)\n",
" print('Unknown sequence:', init_byte)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Initialise the connection using the handshake\n",
"while True:\n",
" s.write(b'\\00')\n",
" \n",
" if s.read(1) == b'\\x82':\n",
" s.write(b'\\xC2')\n",
" \n",
" if s.read(1) == b'\\x83':\n",
" s.write(b'\\xE1\\x31\\x2E\\x30\\x30')\n",
" \n",
" if s.read(1) == b'\\x80':\n",
" s.write(b'\\xA1')\n",
" \n",
" if s.read(1) == b'\\x81':\n",
" s.write(b'\\x80\\x80')\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# Emulate pressing all of the channel buttons\n",
"send(left)\n",
"#send(right)\n",
"print()\n",
"send(ch1)\n",
"print()\n",
"send(ch2)\n",
"print()\n",
"send(ch3)\n",
"print()\n",
"send(ch4)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# This command will pair or unpair channels 1 and 2\n",
"send(combine([ch1, ch2]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Wait for data and decode it\n",
"# press a button on the recorder to see output\n",
"# Press stop to interrupt the kernel when finished\n",
"while True:\n",
" data = get_data()\n",
" if data is not None: print(data)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment