Skip to content

Releases: skuep/AIOC

v1.4.1

27 Sep 16:14

Choose a tag to compare

This Release is a small bugfix on top of v1.4.0

  • Fix wrong LED blinking speed
  • Reduce priority of LED interrupt to not interfere with actual function

Please see the full release notes for v1.4.0 here

v1.4.0

26 Sep 14:35
f41b1aa

Choose a tag to compare

This Release brings the following features

  • MacOS compatibility bugfix targeting issues with stuttery TX (#13)
  • Another doubling of serial buffer size, due to issues with programming firmware on some HTs (#56)
  • Hardware Rev 1.2 Support (dynamic input biasing) (#93)
  • Adjustable Audio gain on the Receive path (1X-16X). Default is 1X (matches HT speaker level), but can be increased for line-level applications (This feature requires Hardware Revision >= v1.2)
  • Adjustable Audio gain on the TX path with a "booster" function. Default is no booster (matches HT mic level), but can be boosted to line levels (This feature requires Hardware Revision >= v1.2)
  • Fox Hunt mode where the AIOC beacons a morse code message in a specific interval without requiring a PC connection (just USB-C Power) (#103)

Python Example Script for new features

import sys
import hid
from struct import Struct
from enum import IntEnum, IntFlag

class Register(IntEnum):
    MAGIC = 0x00
    USBID = 0x08
    AIOC_IOMUX0 = 0x24
    AIOC_IOMUX1 = 0x25
    CM108_IOMUX0 = 0x44
    CM108_IOMUX1 = 0x45
    CM108_IOMUX2 = 0x46
    CM108_IOMUX3 = 0x47
    SERIAL_CTRL = 0x60
    SERIAL_IOMUX0 = 0x64
    SERIAL_IOMUX1 = 0x65
    SERIAL_IOMUX2 = 0x66
    SERIAL_IOMUX3 = 0x67
    AUDIO_RX = 0x72
    AUDIO_TX = 0x78
    VPTT_LVLCTRL = 0x82
    VPTT_TIMCTRL = 0x84
    VCOS_LVLCTRL = 0x92
    VCOS_TIMCTRL = 0x94
    FOXHUNT_CTRL = 0xA0
    FOXHUNT_MSG0 = 0xA2
    FOXHUNT_MSG1 = 0xA3
    FOXHUNT_MSG2 = 0xA4
    FOXHUNT_MSG3 = 0xA5

class Command(IntFlag):
    NONE = 0x00
    WRITESTROBE = 0x01
    DEFAULTS = 0x10
    REBOOT = 0x20
    RECALL = 0x40
    STORE = 0x80

class PTTSource(IntFlag):
    NONE = 0x00000000
    CM108GPIO1 = 0x00000001
    CM108GPIO2 = 0x00000002
    CM108GPIO3 = 0x00000004
    CM108GPIO4 = 0x00000008
    SERIALDTR = 0x00000100
    SERIALRTS = 0x00000200
    SERIALDTRNRTS = 0x00000400
    SERIALNDTRRTS = 0x00000800
    VPTT = 0x00001000

class CM108ButtonSource(IntFlag):
    NONE = 0x00000000
    IN1 =  0x00010000
    IN2 =  0x00020000
    VCOS = 0x01000000

class RXGain(IntEnum):
    RXGAIN1X = 0x00000000
    RXGAIN2X = 0x00010000
    RXGAIN4X = 0x00020000
    RXGAIN8X = 0x00030000
    RXGAIN16X = 0x00040000

class TXBoost(IntEnum):
   TXBOOSTOFF = 0x00000000
   TXBOOSTON = 0x00000100

def read(device, address):
    # Set address and read
    request = Struct('<BBBL').pack(0, Command.NONE, address, 0x00000000)
    device.send_feature_report(request)
    data = device.get_feature_report(0, 7)
    _, _, _, value = Struct('<BBBL').unpack(data)
    return value

def write(device, address, value):
    data = Struct('<BBBL').pack(0, Command.WRITESTROBE, address, value)
    device.send_feature_report(data)

def cmd(device, cmd):
    data = Struct('<BBBL').pack(0, cmd, 0x00, 0x00000000)
    device.send_feature_report(data)

def dump(device):
    for r in Register:
        print(f'Reg. {r.value:02x}: {read(device, r.value):08x}')

aioc = hid.Device(vid=0x1209, pid=0x7388)

magic = Struct("<L").pack(read(aioc, Register.MAGIC))

if (magic != b'AIOC'):
    print(f'Unexpected magic: {magic}')
    sys.exit(-1)

print(f'Manufacturer: {aioc.manufacturer}')
print(f'Product: {aioc.product}')
print(f'Serial No: {aioc.serial}')
print(f'Magic: {magic}')

if False:
    # Load the hardware defaults
    print(f'Loading Defaults...')
    cmd(aioc, Command.DEFAULTS)

if False:
    # Dump all known registers
    dump(aioc)

if False:
    # Set RX settings
    rxgain = RXGain.RXGAIN4X
    print(f'Setting Audio RX gain to {str(rxgain)}')
    write(aioc, Register.AUDIO_RX, rxgain)

if False:
    # Set TX settings
    txboost = TXBoost.TXBOOSTON
    print(f'Setting Audio TX boost to {str(txboost)}')
    write(aioc, Register.AUDIO_TX, txboost)

if False:
    # Set Fox Hunt settings
    interval = 10  # beacon interval in seconds, make sure it is large enough
    id_msg = "TEST FOXHUNT"
    volume = 32768 # 0 to 65535
    wpm = 20 # words per minute

    # convert the string to ASCII bytes and pad it out with nulls to 16 characters
    id_bytes = bytearray(id_msg, 'ascii')
    while len(id_bytes) < 16:
        id_bytes.append(0)

    # convert the bytearray to four uint32s
    id0, id1, id2, id3 = Struct('<LLLL').unpack(id_bytes)

    print(f'Setting Fox Hunt interval to {interval} seconds and ID to {id_msg}')
    write(aioc, Register.FOXHUNT_CTRL, (volume << 16) | (wpm << 8) | (interval << 0))
    write(aioc, Register.FOXHUNT_MSG0, id0)
    write(aioc, Register.FOXHUNT_MSG1, id1)
    write(aioc, Register.FOXHUNT_MSG2, id2)
    write(aioc, Register.FOXHUNT_MSG3, id3)

if False:
    # Store settings into flash
    print(f'Storing...')
    cmd(aioc, Command.STORE)

if False:
    # Reboot AIOC
    print(f'Rebooting...')
    cmd(aioc, Command.REBOOT)

v1.3.0

02 Jan 09:29

Choose a tag to compare

This is essentially the official release of the v1.3.0-rc.2 release.

See
https://github.com/skuep/AIOC/releases/tag/v1.3.0-rc.2
for changes.

For reference (copied from v1.3.0-rc.2):

Still a pre-release due to the need for testing stuff. The following was changed:

  • Increased serial buffer size due to issues with some radios (#56)
  • Implemented hardware logic input capability for use in e.g. hardware COS scenarios. Currently, this is mostly relevant for people using their own hardware and people using the K1 AIOC version 1.1 hardware (hidden away in this branch: https://github.com/skuep/AIOC/tree/k1-aioc-rev1.1
  • Fixed/Overhauled the HID configuration interface due to issues on Windows platforms running the Python scripts. Note that all current scripts will not work anymore. Use this adapted script as an example on how to change stuff.

v1.3.0-rc.2

18 Sep 11:56

Choose a tag to compare

v1.3.0-rc.2 Pre-release
Pre-release

Still a pre-release due to the need for testing stuff. The following was changed:

  • Increased serial buffer size due to issues with some radios (#56)
  • Implemented hardware logic input capability for use in e.g. hardware COS scenarios. Currently, this is mostly relevant for people using their own hardware and people using the K1 AIOC version 1.1 hardware (hidden away in this branch: https://github.com/skuep/AIOC/tree/k1-aioc-rev1.1
  • Fixed/Overhauled the HID configuration interface due to issues on Windows platforms running the Python scripts. Note that all current scripts will not work anymore. Use this adapted script as an example on how to change stuff.

Updated Python Script

import sys
import hid
from struct import Struct
from enum import IntEnum, IntFlag

class Register(IntEnum):
    MAGIC = 0x00
    USBID = 0x08
    AIOC_IOMUX0 = 0x24
    AIOC_IOMUX1 = 0x25
    CM108_IOMUX0 = 0x44
    CM108_IOMUX1 = 0x45
    CM108_IOMUX2 = 0x46
    CM108_IOMUX3 = 0x47
    SERIAL_CTRL = 0x60
    SERIAL_IOMUX0 = 0x64
    SERIAL_IOMUX1 = 0x65
    SERIAL_IOMUX2 = 0x66
    SERIAL_IOMUX3 = 0x67
    VPTT_LVLCTRL = 0x82
    VPTT_TIMCTRL = 0x84
    VCOS_LVLCTRL = 0x92
    VCOS_TIMCTRL = 0x94

class Command(IntFlag):
    NONE = 0x00
    WRITESTROBE = 0x01
    DEFAULTS = 0x10
    RECALL = 0x40
    STORE = 0x80

class PTTSource(IntFlag):
    NONE = 0x00000000
    CM108GPIO1 = 0x00000001
    CM108GPIO2 = 0x00000002
    CM108GPIO3 = 0x00000004
    CM108GPIO4 = 0x00000008
    SERIALDTR = 0x00000100
    SERIALRTS = 0x00000200
    SERIALDTRNRTS = 0x00000400
    SERIALNDTRRTS = 0x00000800
    VPTT = 0x00001000

class CM108ButtonSource(IntFlag):
    NONE = 0x00000000
    IN1 =  0x00010000
    IN2 =  0x00020000
    VCOS = 0x01000000

def read(device, address):
    # Set address and read
    request = Struct('<BBBL').pack(0, Command.NONE, address, 0x00000000)
    device.send_feature_report(request)
    data = device.get_feature_report(0, 7)
    _, _, _, value = Struct('<BBBL').unpack(data)
    return value

def write(device, address, value):
    data = Struct('<BBBL').pack(0, Command.WRITESTROBE, address, value)
    device.send_feature_report(data)

def cmd(device, cmd):
    data = Struct('<BBBL').pack(0, cmd, 0x00, 0x00000000)
    device.send_feature_report(data)

def dump(device):
    for r in Register:
        print(f'Reg. {r.value:02x}: {read(device, r.value):08x}')

aioc = hid.Device(vid=0x1209, pid=0x7388)

magic = Struct("<L").pack(read(aioc, Register.MAGIC))

if (magic != b'AIOC'):
    print(f'Unexpected magic: {magic}')
    sys.exit(-1)

print(f'Manufacturer: {aioc.manufacturer}')
print(f'Product: {aioc.product}')
print(f'Serial No: {aioc.serial}')
print(f'Magic: {magic}')

if False:
    # Load the hardware defaults
    print(f'Loading Defaults...')
    cmd(aioc, Command.DEFAULTS)

if False:
    # Dump all known registers
    dump(aioc)

ptt1_source = PTTSource(read(aioc, Register.AIOC_IOMUX0))
ptt2_source = PTTSource(read(aioc, Register.AIOC_IOMUX1))

print(f'Current PTT1 Source: {str(ptt1_source)}')
print(f'Current PTT2 Source: {str(ptt2_source)}')

btn1_source = CM108ButtonSource(read(aioc, Register.CM108_IOMUX0))
btn2_source = CM108ButtonSource(read(aioc, Register.CM108_IOMUX1))
btn3_source = CM108ButtonSource(read(aioc, Register.CM108_IOMUX2))
btn4_source = CM108ButtonSource(read(aioc, Register.CM108_IOMUX3))

print(f'Current CM108 Button 1 (VolUP) Source: {str(btn1_source)}')
print(f'Current CM108 Button 2 (VolDN) Source: {str(btn2_source)}')
print(f'Current CM108 Button 3 (PlbMute) Source: {str(btn3_source)}')
print(f'Current CM108 Button 4 (RecMute) Source: {str(btn4_source)}')

if False:
    # Swap PTT1/PTT2
    ptt1_source, ptt2_source = ptt2_source, ptt1_source
    print(f'Setting PTT1 Source to {str(ptt1_source)}')
    write(aioc, Register.AIOC_IOMUX0, ptt1_source)
    print(f'Setting PTT2 Source to {str(ptt2_source)}')
    write(aioc, Register.AIOC_IOMUX1, ptt2_source)

    print(f'Now PTT1 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX0)))}')
    print(f'Now PTT2 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX1)))}')

if False:
    # Set to AutoPTT on PTT1
    print(f'Setting PTT1 Source to {str(PTTSource.VPTT)}')
    write(aioc, Register.AIOC_IOMUX0, PTTSource.VPTT)
    print(f'Now PTT1 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX0)))}')
    print(f'Now PTT2 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX1)))}')

if False:
    # Set USB VID and PID (use with caution. Will need changes above to be able to re-configure the AIOC)
    write(aioc, Register.USBID, (0x1209 << 0) | (0x7388 << 16))
    print(f'Now USBID: {read(aioc, Register.USBID):08x}')

if False:
   # Set Volume Button Down to IN2 for HWCOS instead of VCOS and set Volume Up to NONE
   print(f'Setting VolUP button source to {str(CM108ButtonSource.NONE)}')
   print(f'Setting VolDN button source to {str(CM108ButtonSource.IN2)}')
   write(aioc, Register.CM108_IOMUX0, CM108ButtonSource.NONE)
   write(aioc, Register.CM108_IOMUX1, CM108ButtonSource.IN2)
   print(f'Now VolUP button source: {str(CM108ButtonSource(read(aioc, Register.CM108_IOMUX0)))}')
   print(f'Now VolDN button source: {str(CM108ButtonSource(read(aioc, Register.CM108_IOMUX1)))}')

if False:
    # Store settings into flash
    print(f'Storing...')
    cmd(aioc, Command.STORE)

v1.3.0-rc.1

01 Jan 16:23

Choose a tag to compare

v1.3.0-rc.1 Pre-release
Pre-release

This is a quite extensive pre-release delivering a firmware implementation of the promised

  • HID configuration system as discussed in PR #37 and
  • virtual PTT as discussed in PR #25 and issue #19.

Configurability

This enables the user to configure all kinds of functionality within the AIOC. Currently this is

  • USB VID:PID (although not recommended, but can be helpful in a pinch)
  • PTT1/PTT2 signal source (CM108 GPIOs 1-4, serial DTR/RTS or the new virtual PTT which serves as a high-performance VOX)
  • Configure CM108 buttons (Volume Up/Down, Playback/Record Mute) and serial inputs to PTT1/PTT2 inputs (supported with next hardware revision) or virtual COS)
  • Configure UART lockout on PTT activity, which is enabled by default but can be disabled. This is required to be able to use the AIOC as both a programming cable (UART) and soundcard.
  • Configure Virtual PTT and Virtual COS settings such as threshold value and timeout (tail) time
  • A whole slew of debug register showing whats going on inside the AIOC

Although all these things are now configurable, the default configuration is kept such that people that don't want to use this system, won't even notice it's there. E.g. default for PTT1 is still CM108 GPIO3 or DTR & !NRTS. CM108 Volume Down has the virtual COS.

The configuration can be stored in flash and is thus kept across power cycles. If you flash a new firmware in the future however, the settings will be lost. This is a bit inconvenient, but required to avoid having to be backwards compatible with the binary settings inside the flash across firmware versions.

How to configure

This part of the release is still a bit lacking. I plan there to be a very extensive python command line application that can be used to programm all the new configuration values of the AIOC. However currently, I only have this debug script, that I use for my purposes, that I can show you.
Please have a look in the stm32/aioc-fw/Src/settings.hfile on the details of the contents in those registers.

import sys
import hid
from struct import Struct
from enum import IntEnum, IntFlag

class Register(IntEnum):
    MAGIC = 0x00
    USBID = 0x08
    AIOC_IOMUX0 = 0x24
    AIOC_IOMUX1 = 0x25
    CM108_IOMUX0 = 0x44
    CM108_IOMUX1 = 0x45
    CM108_IOMUX2 = 0x46
    CM108_IOMUX3 = 0x47
    SERIAL_CTRL = 0x60
    SERIAL_IOMUX0 = 0x64
    SERIAL_IOMUX1 = 0x65
    SERIAL_IOMUX2 = 0x66
    SERIAL_IOMUX3 = 0x67
    VPTT_LVLCTRL = 0x82
    VPTT_TIMCTRL = 0x84
    VCOS_LVLCTRL = 0x92
    VCOS_TIMCTRL = 0x94

    
class Command(IntFlag):
    DEFAULTS = 0x10
    RECALL = 0x40
    STORE = 0x80

class PTTSource(IntFlag):
    NONE = 0x00000000
    CM108GPIO1 = 0x00000001
    CM108GPIO2 = 0x00000002
    CM108GPIO3 = 0x00000004
    CM108GPIO4 = 0x00000008
    SERIALDTR = 0x00000100
    SERIALRTS = 0x00000200
    SERIALDTRNRTS = 0x00000400
    SERIALNDTRRTS = 0x00000800
    VPTT = 0x00001000


def read(device, address):
    data = device.get_feature_report(int(address), 5)
    address, value = Struct('<BL').unpack(data)
    return value

def write(device, address, value):
    data = Struct('<BL').pack(address, value)
    device.send_feature_report(data)

def dump(device):
    for r in Register:
        print(f'Reg. {r.value:02x}: {read(device, r.value):08x}')

aioc = hid.Device(vid=0x1209, pid=0x7388)

magic = Struct("<L").pack(read(aioc, Register.MAGIC))

if (magic != b'AIOC'):
    sys.exit(-1)

print(f'Manufacturer: {aioc.manufacturer}')
print(f'Product: {aioc.product}')
print(f'Serial No: {aioc.serial}')
print(f'Magic: {magic}')

if True:
    # Load the hardware defaults
    print(f'Loading Defaults...')
    write(aioc, 0, Command.DEFAULTS)

if True:
    # Dump all known registers
    dump(aioc)

ptt1_source = PTTSource(read(aioc, Register.AIOC_IOMUX0))
ptt2_source = PTTSource(read(aioc, Register.AIOC_IOMUX1))

print(f'Current PTT1 Source: {str(ptt1_source)}')
print(f'Current PTT2 Source: {str(ptt2_source)}')

if False:
    # Swap PTT1/PTT2
    ptt1_source, ptt2_source = ptt2_source, ptt1_source
    print(f'Setting PTT1 Source to {str(ptt1_source)}')
    write(aioc, Register.AIOC_IOMUX0, ptt1_source)
    print(f'Setting PTT2 Source to {str(ptt2_source)}')
    write(aioc, Register.AIOC_IOMUX1, ptt2_source)

    print(f'Now PTT1 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX0)))}')
    print(f'Now PTT2 Source: {str(PTTSource(read(aioc, Register.AIOC_IOMUX1)))}')

if False:
    # Set to AutoPTT on PTT1
    write(aioc, Register.AIOC_IOMUX0, PTTSource.VPTT)


if False:
    # Set USB VID and PID (use with caution. Will need changes above to be able to re-configure the AIOC)
    write(aioc, Register.USBID, (0x1209 << 0) | (0x7388 << 16))
    print(f'Now USBID: {read(aioc, Register.USBID):08x}')

if False:
    # Store settings into flash
    print(f'Storing...')
    write(aioc, 0, Command.STORE)

Should you have any issues or questions, feel free to open an issue.

v1.2.0

19 Mar 15:44

Choose a tag to compare

New features in this Release

  • CM108-style PTT
  • Live automatic DFU firmware updates
  • serial PTT has changed from DTR=1to DTR=1& RTS=0, due to some issues with certain radios, where programming them with CHIRP would trigger the PTT and leave asserted for the entire programming sequence.

v1.2.0 RC2 CM108 PTT Preview

26 Feb 13:59

Choose a tag to compare

Pre-release

CM108 PTT control

See Release v1.2.0-rc.1 on CM108 PTT details (https://github.com/skuep/AIOC/releases/tag/v1.2.0-rc.1)

  • Merged v1.1.1
  • Correctly implemented CM108 interrupt endpoint behavior

v1.1.1

24 Feb 15:05

Choose a tag to compare

Additional Sample Rates

16 kHz and 32 kHz sample rates have been added

v1.2.0 RC1 CM108 PTT Preview

23 Feb 15:22

Choose a tag to compare

Pre-release

CM108 PTT control

This is a release-candidate, i.e. currently not tested well enough for a Release.

This is the first version of CM108 style PTT support. It works "in parallel" with the regular COM-Port DTR PTT support, which should not be an issue.
CM108 PTT control has been tested in direwolf. The following needs to be adapted in direwolf.conf

    #PTT /dev/ttyACM0 DTR # comment out any lines similar to this
    PTT CM108 # add this line

In addition, look in the direwolf manual for configuring the correct permissions on the hidraw device. For a quick and dirty test, run direwolf, it will report a message like this.

    could not open /dev/hidraw13 for write, errno=13

Now, we now that the AIOC hid device is hidraw13. Run the following command.

    sudo chmod 666 /dev/hidraw13

Restart direwolf and this error disappears (Note: This method has the disadvantage that it has to be repeated manually each time you plug in the AIOC).

Note that currently, direwolf still shows the following warnings:

    Warning: USB audio card 5 (AllInOneCable) is not a device known to work with GPIO PTT.
    ....
    ioctl HIDIOCGRAWINFO failed for /dev/hidraw13. errno = 0.

The first error is understandable, since the AIOC does not use the USB VID:PID pair that any of the CM108 (or similar) chipsets use. This warning can only be made to disappear at the hands of the direwolf developers. You can ignore this.

The second warning is, in my opinion, a bug in direwolf and is currently only a minor annoyance and not a deal-breaker. It is related to a "sanity check" whether we are working with a "supported device", however the if/else clauses seem to be jambled up. See this issue: wb2osz/direwolf#448

This should get you started immediately. Note that as per direwolf's default, the GPIO3 (at CM108) is used for PTT control. GPIO4 is used for PTT2 control (should your radio have a second PTT).

Should you encounter any problems or have suggestions, please use this issue: #17

v1.1.0

21 Feb 12:17

Choose a tag to compare

A few changes since previous release:

  • PTT is now asserted on DTR=1 (instead of RTS=1 & DTR=0 as before). Please adjust your configurations.
    This was done to support applications that cannot use both RTS and DTR simultaneously for the PTT. See #11 for a lengthy discussion.
  • Additional sample rates have been added (12000 Hz, 11025 Hz and 8000 Hz) specifically for applications that only work with sample rates of 12000 Hz and below (e.g. SoundModem).
    Note that 11025 Hz has a frequency error of approx. 90 ppm due to odd divider ratio from the AIOC system frequency.