Last year, with your help, I managed to configure quisk for an embedded Raspberry + Softrock standalone transceiver, with external encoder and pushbuttons for various functions (tune, PTT etc).
Ever since I had to patch all relevant python files on every Quisk update and sometimes, even re-write the necessary files because the patch reference mismatch.
Can you please tell me if you're considering adding support for external input (maybe via API?) at least for encoder based tuning? That would help a lot for embedded Quisk setups.
Thank you very much, have a nice day, 73!
I thought that external input could be put into your hardware file, and so subsequent Quisk releases would not overwrite your hardware file and there would be no problems. If that is not the case we need a new API for external input. But I can not design it unless I have your physical hardware, and I do not.
Could you design an external input API? Could you send me your Quisk patches so I can see what you are doing?
Actually you are right. I am patching hardware_usb.py (the patch at the end of the reply) for the rotary encoder tunning and PTT, but also the main quisk.py to add some usefull stuff (I added a shutdown button directly from Quisk and some info like the IP address). Indeed the hardware_usb.py file is not changed, therefore I don't really need to patch it, just make sure that file is not overwritten on updates.
Now, in order to interface some buttons with Quisk (eq switching bands, activating VOX, NB, changing modes) I found a way to use GPIO buttons on raspberry as standard keyboard inputs, then I don't need to change anything in Quisk itself, but just assign keyboard shortcuts for different functions.
Is this possible? Can I assign keyboard shortcuts for Quisk?
Thank you very much for quick reply!
import usb.core, usb.util
+import RPi.GPIO as GPIO
+from rotary_class import RotaryEncoder
+# Define GPIO inputs
+PIN_A = 17 # Pin A enconder
+PIN_B = 27 # Pin B encoder
+BUTTON = 18 # PTT button
+#Band select, 1 is 80m, 0 is 40m
DEBUG = 0
@@ -46,7 +62,13 @@
self.is_cw = False
self.key_thread = None
self.si570_i2c_address = conf.si570_i2c_address
+ self.EncMod = conf.mouse_wheelmod # Round frequency when using mouse wheel
+ # Keep track of the current tune frequency and VFO
+ self.tune_freq = 0
+ self.vfo_freq = 0
def open(self): # Called once to open the Hardware
+ # Make a rotary control when opening the hardware
+ self.rswitch = RotaryEncoder(PIN_A, PIN_B, BUTTON, self.EncTune)
# find our device
usb_dev = usb.core.find(idVendor=self.conf.usb_vendor_id, idProduct=self.conf.usb_product_id)
if usb_dev is None:
@@ -87,10 +109,14 @@
print ('Smooth tune', sm)
def close(self): # Called once to close the Hardware
+ self.rswitch.event_removal(PIN_A, PIN_B, BUTTON)
self.key_thread = None
def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
+ # When Quisk changes the frequency, record the new frequency
+ self.tune_freq = tune
+ self.vfo_freq = vfo
if self.usb_dev and self.vfo != vfo:
if self.SetFreqByDirect(vfo - self.transverter_offset):
@@ -105,7 +131,27 @@
# Return the current tuning and VFO frequency. If neither have changed,
# you can return (None, None). This is called at about 10 Hz by the main.
# return (tune, vfo) # return changed frequencies
- return None, None # frequencies have not changed
+ #return None, None # frequencies have not changed
+ #return self.tune_freq, self.vfo_freq
+ if self.tune_freq:
+ return self.tune_freq, self.vfo_freq
+ return None, None
+ def EncTune(self, event):
+ # When the control changes the frequency, adjust it
+ if event == RotaryEncoder.CLOCKWISE:
+ self.tune_freq += self.EncMod
+ elif event == RotaryEncoder.ANTICLOCKWISE:
+ self.tune_freq -= self.EncMod
+ elif event == RotaryEncoder.BUTTONDOWN:
+ self.ptt_button = 1
+ GPIO.output(23, True)
+ elif event == RotaryEncoder.BUTTONUP:
+ self.ptt_button = 0
+ GPIO.output(23, False)
+ if self.key_thread:
+ self.tune_freq = self.tune_freq // self.EncMod * self.EncMod
def RepeaterOffset(self, offset=None): # Change frequency for repeater offset during Tx
if offset is None: # Return True if frequency change is complete
if time.time() > self.repeater_time0 + self.repeater_delay:
@@ -134,6 +180,10 @@
def ChangeBand(self, band):
# band is a string: "60", "40", "WWV", etc.
+ if band in ('160', '80', '60'):
+ GPIO.output(24, False)
+ GPIO.output(24, True)
def OnSpot(self, level):
@@ -143,8 +193,10 @@
self.ptt_button = 1
+ GPIO.output(23, True)
self.ptt_button = 0
+ GPIO.output(23, False)
I just tested it and it works perfectly! I've configured GPIO keyboard input for key "q" and I used it in PTT key configuration.
If possible, it would be great to have a key to toggle the bands, one for VOX, one for modes (ideally each mode a different key, at least for the most important ones LSB, USB, AM, FM, FDV, CW) and one to toggle NB.
Thank you very much, 73'!
Oh I found the issue, some of the keyboard shortcuts (eq "modes") do not work in Small Form window. Unfortunately I can't use the full view on the 800x600 LCD screen, therefore I can't use the keyboard shortcuts for modes etc.
Is there a way to have keyboard shortcuts on Small form window mode?
Thank you again!
I am using the touch screen with small window settings, however I want also to be able to use external buttons for the common used functions for faster access. On a 800x600 LCD, the Quisk own buttons are quite small and sometimes prone for errors (eq pressing the wrong button), this is why I want to use external buttons for them.
Thank you for your patience and great work!
Hi Jim, thanks a lot for your support, prompt as always. Unfortunately my programming skills are quite basic, so your suggestion is helpful but too difficult for me to implement.
I managed to map all my 8 keys to the all the shortcuts of the small window version + PTT. I had to replace the wx.ACCEL_ALT with wx.ACCEL_NORMAL on quisk.py because I don't use a normal keyboard, therefore I don't use key modifiers (that would be really cool if one could select the modifier key in the menu).
Having so few keys at disposal, I can't really use the mode shortcut keys even in large window mode because I would consume all my keys :)))). The only way would be to have a key to toggle the modes or bands, but because modes and bands are dynamically defined (based on user's choices or availability of libraries) I don't thing it's worth it.
So for now I am really pleased, the only changes are the modifier keys in quisk.py and a custom hardware_usb.py file for the rotary encoder setup. All the rest it's being handled by the OS (mapped the GPIO pins to keyboard codes), this means much less patching for newer versions of Quisk.
I know it might sound silly, but how can I use the FreeDV mode in Quisk?
Thanks again for great support! See my page on qrz.com for the first version of Quisk based transceiver, the version two is on the way ;).
I was wondering if you could please include the patch below in the next versions of quisk. Basically it handles the telnet connection errors for dxcluster. What happends is that my Wifi board connects to the network after 30-40 seconds after Quisk is started, therefore quisk is throwing some errors (cannot resolve hostname) in separate windows which are really annoying.
Please have a look at the patch, it might be useful for other users as well. Maybe you can find a more elegant solution to this problem, I am not a very good programmer...
--- /home/pi/tmp/quisk-4.1.19/dxcluster.py 2015-11-28 13:40:34.000000000 +0000
+++ /home/pi/quisk/dxcluster.py 2018-06-24 20:48:15.182931927 +0000
@@ -4,6 +4,7 @@
import quisk_conf_defaults as conf
+from time import sleep
Sorry, I was too fast in jumping to conclusions.
After a couple of restarts, it still trows the error:
File ./dxcluster.py, line 115, in telnetConnect
File "/usb/lib/python2.7/telnetlib.py", line 271, in fileno
Attribute error: 'NoneType' object has no attribute 'fileno'
But a very simple change solves the problem:
--- ../tmp/dxcluster.py 2018-06-25 16:57:40.000000000 +0000
+++ dxcluster.py 2018-07-02 17:14:20.524298790 +0000
@@ -109,11 +109,12 @@
for i in range(10):
self.tn.open(conf.dxClHost, conf.dxClPort, 10)
+ self.tn.read_until('login:', 10)
+ self.tn.write(str(conf.user_call_sign) + "\n") # user_call_sign may be Unicode
- self.tn.read_until('login:', 10)
- self.tn.write(str(conf.user_call_sign) + "\n") # user_call_sign may be Unicode