VMAC PiHat v2 Repeater Logic
VMAC PiHat V2 was originally designed as a ATV repeater controller and whilst it has seen other applications, the general overview as a controller is shown below.
Typical application:
Repeater example code will be developed in Python, but most of which is already available in the Wiki sections.
The below is a very work in progress version of what I am using on GB3JT / GB3FW and testing various functions – however, it may help others so will keep it updated.
# HAMKit VMAC PiHat v2.4 - ATV Repeater - Dave Williams # Hardware: PiHat v2.4, Pi3, EasyCap Video Capture # Initialise all GPIO inputs, outputs, I2C, SPI and UART # Reset, DTMF Decoder, Squelch Detect, Video Detect, Matrix, OSD, PTT # 23rd August 2018. GB3JT TEST # Thanks also to Paul Theunissen, PA5PT, for OSD code template # GB3JT stream can be found at https://batc.org.uk/live/gb3jt #!/usr/bin/python #DRAFT!!! VERY WIP ========================== #========================== | Imports / Libraries | ========================== import RPi.GPIO as GPIO #GPIO import smbus #I2C import spidev #SPI #import serial #UART import time #Time import datetime #RTC from ctypes import * #========================== | Definitions | ========================== #====== GPIO GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) #GPIO.cleanup() #Define GPIO Outputs GPIO_OSD_RST = 25 # OSD Reset / Enable GPIO_PTT = 27 # Red LED = PTT & FET GPIO_Status = 22 # Blue LED = Status GPIO_User = 17 # Amber LED = User (also defined as RTS) #Setup Outputs GPIO.setup(GPIO_OSD_RST,GPIO.OUT) GPIO.setup(GPIO_PTT,GPIO.OUT) GPIO.setup(GPIO_Status,GPIO.OUT) GPIO.setup(GPIO_User,GPIO.OUT) #Define GPIO Inputs GPIO_Button = 4 # PCB User Button GPIO_D0 = 26 # DTMF D0 8870 Data Bit 0 GPIO_D1 = 19 # DTMF D1 8870 Data Bit 1 GPIO_D2 = 13 # DTMF D2 8870 Data Bit 2 GPIO_D3 = 6 # DTMF D3 8870 Data Bit 3 GPIO_DINT = 5 # DTMF DINT 8870 StD Latch Output GPIO_SQ1 = 12 # PCB I/O 1 User Input SQ1 GPIO_SQ2 = 16 # PCB I/O 1 User Input SQ2 GPIO_SQ3 = 20 # PCB I/O 1 User Input SQ3 GPIO_SQ4 = 21 # PCB I/O 1 User Input SQ4 GPIO_VD_1 = 18 # VideoDetect from Video Input RCA AV1 GPIO_VD_2 = 23 # VideoDetect from Video Input RCA AV2 GPIO_VD_3 = 24 # VideoDetect from Video Input RCA AV3 #Setup Inputs with pull-ups enabled GPIO.setup(GPIO_Button, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_D0, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_D1, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_D2, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_D3, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_DINT, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_SQ1, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_SQ2, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_SQ3, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_SQ4, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_VD_1, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_VD_2, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(GPIO_VD_3, GPIO.IN, pull_up_down=GPIO.PUD_UP) #====== I2C Bus (FMS6501 / EEPROM) DEVICE_BUS = 1 # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1) i2cbus = smbus.SMBus(DEVICE_BUS) DEVICE_ADD_Matrix = 0x43 #0x43 or 0x03 FMS6501 Matrix DEVICE_ADD_EEPROM = 0x50 #0x50 EEPROM #====== SPI bus (MAX7456) #spi = spidev.SpiDev() #====== UART #"/dev/ttyAMA0" # Raspberry Pi 2 #"/dev/ttyS0" # Raspberry Pi 3 ##UARTSerial = serial.Serial( ## port='/dev/ttyS0', ## baudrate = 9600, ## parity=serial.PARITY_NONE, ## stopbits=serial.STOPBITS_ONE, ## bytesize=serial.EIGHTBITS, ## timeout=1 ##) # sp = serial.Serial ("/dev/tty01") # sp.baudrate = 9600 # data = sp.read(10) # 10 characters # sp.write(data) # echo all 10 characters back # sp.close() #====== Define Program Variables DValueStore = 0 PiHatAVin = {1:'V', 2:'V', 3:'V', 4:'A', 5:'A', 6:'A', 7:'A', 8:'A', 9:'V'} PiHatAVout = {1:'V', 2:'V', 3:'A', 4:'A'} dateString = '%H %M %S' #====== Define User Variables UserCallsign = "GB3JT" UserFreqTX = "1318 MHz" UserFreqRX = "1249 MHz" UserCTCSS = "" UserQRA = "JO00HV" UserNGR = "TQ825120" UserPostcode = "TN34 2AF" UserOverChar = "K" UserTX247 = True #Define DTMF Symbols DTMFLookup = ["1","2","3","4","5","6","7","8","9","0","*","#","A","B","C","D"] #Define Morse Symbols MorseLookup = { 'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----', '!': '-.-.--', '"': '.-..-.', '$': '...-..-', '&': '.-...', '(': '-.--.', ')': '-.--.-', ',': '--..--', '.': '.-.-.-', '/': '-..-.', '-': '-....-', ':': '---...', ';': '-.-.-.', '?': '..--..', '@': '.--.-.', '\'': '.----.', '_': '..--.-', '+': '.-.-.', '=': '-...-' } #========================== | Initialise | ========================== #====== GPIO GPIO.output(GPIO_OSD_RST,GPIO.HIGH) #Enable OSD Reset High #====== Serial UART #UARTSerial.write("VMAC PiHat") #Initiate LEDs GPIO.output(GPIO_Status,GPIO.HIGH) GPIO.output(GPIO_User,GPIO.HIGH) GPIO.output(GPIO_PTT,GPIO.HIGH) time.sleep(1) GPIO.output(GPIO_Status,GPIO.LOW) GPIO.output(GPIO_User,GPIO.LOW) if UserTX247 == False: GPIO.output(GPIO_PTT,GPIO.LOW) time.sleep(1) #Read 8870 StD Latch Output DINT = GPIO.input(GPIO_DINT) #========================== | Routines | ========================== def PowerDownAllPorts(): print ("PowerDownAllPorts") i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x01, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x02, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x03, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x04, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x05, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x08, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x09, 0x0) #Mute time.sleep(.01) def ReadDTMF(): #Check for High Pulse from the 8870 StD Latch Output if DINT == True: #Only Check GPIO if 8870 Data Bits have changed D0 = GPIO.input(GPIO_D0) D1 = GPIO.input(GPIO_D1) D2 = GPIO.input(GPIO_D2) D3 = GPIO.input(GPIO_D3) DValue = D0+(D1*2)+(D2*4)+(D3*8) #Only Display if DTMF Value has changed if DValueStore != DValue: DValueStore = DValue #Print Output print ("DTMF Value: %02d \tSymbol: " % (DValue) + DTMFLookup[DValue - 1]) #========================== | Main | ========================== #Disable and Powerdown All Matrix Outputs PowerDownAllPorts() # Setup Matrix Video Switching #i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x1d, 0xff) #clamp #i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x1e, 0xff) #clamp #i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x82) #Input 1 to OSD In i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x01, 0x86) #Input OSD to Output 1 #OSD MAX7456 class max7456(): # Create SPI bus (MAX7456) spi = spidev.SpiDev() # MAX7456 opcodes DMAH = 0x05 DMAL = 0x06 DMDI = 0x07 DMM_reg = 0x04 HOS_reg = 0x02 HOS_reg = 0x02 OSDM = 0x0C RB0 = 0x10 STATUS = 0xA0 VM0_reg = 0x00 VM1_reg = 0x01 VOS_reg = 0x03 # PAL - VM0_reg commands ENABLE_display = 0x48 ENABLE_display_vert = 0x4c MAX7456_reset = 0x42 DISABLE_display = 0x40 # Read command READ = 0x80 MAX_screen_rows = 16 # White levels WHITE_level_80 = 0x03 WHITE_level_90 = 0x02 WHITE_level_100 = 0x01 WHITE_level_120 = 0x00 chars = {' ':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '0':10, 'A':11, 'B':12, 'C':13, 'D':14, 'E':15, 'F':16, 'G':17, 'H':18, 'I':19, 'J':20, 'K':21, 'L':22, 'M':23, 'N':24, 'O':25, 'P':26, 'Q':27, 'R':28, 'S':29, 'T':30, 'U':31, 'V':32, 'W':33, 'X':34, 'Y':35, 'Z':36, 'a':37, 'b':38, 'c':39, 'd':40, 'e':41, 'f':42, 'g':43, 'h':44, 'i':45, 'j':46, 'k':47, 'l':48, 'm':49, 'n':50, 'o':51, 'p':52, 'q':53, 'r':54, 's':55, 't':56, 'u':57, 'v':58, 'x':59, 'y':60, 'z':61, '(':62, ')':63, '.':64, '?':65, ';':66, ':':67, ',':68, '\'':69, '/':70, '"':71, '-':72, '<':73, '>':74, '@':75, '\xa9':76 } def __init__(self): # Open a SPI port - max7456 connected on SPI0 self.spi.open(0, 0) self.spi.max_speed_hz = 1000000 self.spi.bits_per_word = 8 self.spi.cshigh = False self.spi.lsbfirst = False self.spi.mode = 0 # On init, reset max7456 self.reset() # Set all rows at the same white level for x in range (0, self.MAX_screen_rows): self.spi.xfer2([(self.RB0 + x), self.WHITE_level_90]) # Enable max7456 self.spi.xfer2([self.VM0_reg, self.ENABLE_display]); def printStr(self, X, Y, string, enable = True): disp = [] for char in string: disp.append(self.chars[char]) print (string) if enable == False: self.spi.xfer([self.VM0_reg, self.Disable_display]) # Enable 8 bit mode: dmm = self.spi.xfer2([self.DMM_reg + self.READ, 0x00]) dmm = self.setBit(dmm[1], 6) self.spi.xfer2([self.DMM_reg, dmm]) start = X * 30 + Y # Clear position self.spi.xfer2([self.DMAH, 0x00]) self.spi.xfer2([self.DMAL, 0x00]) for char in disp: # Write char dmah = self.spi.xfer2([self.DMAH + self.READ, 0x00]) dmah = self.clearBit(dmah[1], 1) self.spi.xfer2([self.DMAH, dmah]) dmah_pos = ((start >> 8) & 0x01) dmal = (start & 0xff) dmah = dmah | dmah_pos start = start + 1 # Select MSB self.spi.xfer2([self.DMAH, dmah]) self.spi.xfer2([self.DMAL, dmal]) self.spi.xfer2([self.DMDI, (char)]) def reset(self): self.spi.xfer2([self.VM0_reg, self.MAX7456_reset]) time.sleep(0.1) while True: r = self.spi.xfer([self.STATUS, 0x00]) stable = self.testBit(r[1], 1) if stable == 0: print ("Reset MAX7456 Ok...") break break def testBit(self, value, offset): mask = 1 << value return(value & mask) def setBit(self, value, offset): mask = 1 << offset return(value + mask) def clearBit(self, int_type, offset): mask = ~(1 << offset) return(int_type & mask) try: max7456 = max7456() #Display OSD Text max7456.printStr(2,10, "Hastings ATV", enable = True) #max7456.printStr(5,5, "OSD Test ABCDEFGHIJ", enable = True) #max7456.printStr(9,1, "1234567890123456789012345678", enable = True) max7456.printStr(13,3, "GB3JT VMAC PiHat Testing", enable = True) except KeyboardInterrupt: spi.close() i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x85) #Input 3 to OSD In #========================== | LOOP | ==========================# #Loop while True: VideoDetect_1 = GPIO.input(GPIO_VD_1) VideoDetect_2 = GPIO.input(GPIO_VD_2) VideoDetect_3 = GPIO.input(GPIO_VD_3) if VideoDetect_1 == True: print ("VideoDetect_1 23cm FM Analogue") i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x81) #Input 1 to OSD In i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x87) #Input 4 to TX A max7456.printStr(14,28, "1", enable = True) time.sleep(5) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x00) #Mute elif VideoDetect_2 == True: print ("VideoDetect_2 6cm FM Analogue") i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x83) #Input 2 to OSD In i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x88) #Input 5 to TX A max7456.printStr(14,28, "2", enable = True) time.sleep(5) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x00) #Mute elif VideoDetect_3 == False: print ("VideoDetect_3 23cm Digital") i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x85) #Input 3 to OSD In i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x89) #Input 6 to TX A max7456.printStr(14,28, "3", enable = True) time.sleep(5) i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x00) #Mute else: i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x07, 0x85) #Input 2 to OSD In i2cbus.write_byte_data(DEVICE_ADD_Matrix, 0x06, 0x00) #Mute max7456.printStr(14,28, "0", enable = True) #Heatbeat User LED GPIO.output(GPIO_User,GPIO.HIGH) time.sleep(.2) GPIO.output(GPIO_User,GPIO.LOW) time.sleep(1) max7456.printStr(14,1, datetime.datetime.now().strftime(dateString), enable = True)