From 954361b19fe41db97339ecd501d9982aa719fd8e Mon Sep 17 00:00:00 2001
From: scriptorron <22291722+scriptorron@users.noreply.github.com>
Date: Tue, 22 Nov 2022 20:13:21 +0100
Subject: [PATCH 1/3] implemented direct UART interface
---
make_UI.bat | 1 +
src/ASCOM.py | 68 ++++++++++++++
src/AutoSTAR_remote.py | 175 ++++++++++++++++++------------------
src/AutoSTAR_remote_ui.py | 16 ++--
src/AutoSTAR_remote_ui.ui | 14 ++-
src/UART.py | 182 ++++++++++++++++++++++++++++++++++++++
src/UART_ui.py | 100 +++++++++++++++++++++
src/UART_ui.ui | 156 ++++++++++++++++++++++++++++++++
8 files changed, 618 insertions(+), 94 deletions(-)
create mode 100644 src/ASCOM.py
create mode 100644 src/UART.py
create mode 100644 src/UART_ui.py
create mode 100644 src/UART_ui.ui
diff --git a/make_UI.bat b/make_UI.bat
index fca046c..1db08f8 100644
--- a/make_UI.bat
+++ b/make_UI.bat
@@ -1 +1,2 @@
call pyuic5 -x src\AutoSTAR_remote_ui.ui -o src\AutoSTAR_remote_ui.py
+call pyuic5 -x src\UART_ui.ui -o src\UART_ui.py
diff --git a/src/ASCOM.py b/src/ASCOM.py
new file mode 100644
index 0000000..4323048
--- /dev/null
+++ b/src/ASCOM.py
@@ -0,0 +1,68 @@
+"""
+ASCOM interface for AutoSTAR_remote
+"""
+
+from PyQt5 import QtWidgets
+import win32com.client
+
+
+class ASCOM:
+
+ def __init__(self):
+ self.Telescope = None
+ self.Name = ""
+
+ def open(self):
+ self.close()
+ try:
+ Chooser = win32com.client.Dispatch("ASCOM.Utilities.Chooser")
+ except win32com.client.pywintypes.com_error:
+ QtWidgets.QMessageBox.critical(None, "Can not call ASCOM!",
+ f"Is ASCOM installed?")
+ else:
+ Chooser.DeviceType = 'Telescope'
+ self.Name = Chooser.Choose(None)
+ self.Telescope = win32com.client.Dispatch(self.Name)
+ self.Telescope.Connected = True
+ if not self.Telescope.Connected:
+ QtWidgets.QMessageBox.critical(None, "Can not connect to telescope!",
+ f"Please check connection to\n{self.Name}.\nMaybe it is already in use.")
+ self.Telescope = None
+
+ def get_Parameter(self):
+ # has no parameter
+ return dict()
+
+ def is_open(self):
+ if self.Telescope is not None:
+ if self.Telescope.Connected:
+ return True
+ return False
+
+ def close(self):
+ if self.is_open():
+ self.Telescope.Connected = False
+ self.Telescope = None
+ self.Name = ""
+
+ def sendCommandBlind(self, cmd):
+ if self.is_open():
+ try:
+ ret = self.Telescope.CommandBlind(cmd, False)
+ except win32com.client.pywintypes.com_error as e:
+ print(f'sendCommandBlind: {e}')
+ return None
+ else:
+ return ret
+ return None
+
+ def sendCommandString(self, cmd):
+ if self.is_open():
+ try:
+ return self.Telescope.CommandString(cmd, False)
+ except win32com.client.pywintypes.com_error as e:
+ # Sometimes the handbox needs long time for calculations and does not
+ # send the LCD contents before the ASCOM driver trows a timeout exception.
+ # Here we catch these timeout exceptions.
+ print(f'updateLCD: {e}')
+ return None
diff --git a/src/AutoSTAR_remote.py b/src/AutoSTAR_remote.py
index 5a6a52f..fecbb68 100644
--- a/src/AutoSTAR_remote.py
+++ b/src/AutoSTAR_remote.py
@@ -5,15 +5,23 @@
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
-import win32com.client
+
+try:
+ import ASCOM
+except ImportError:
+ has_ASCOM = False
+else:
+ has_ASCOM = True
+
+import UART
import AutoSTAR_remote_ui
-version = "V1.0.1"
+version = "V1.1.0"
-theme_selection = "Dark" # "Dark", "Light"
-LCD_polling_time = 1000 # milliseconds
-LCD_earlyUpdate_time = 200 # milliseconds
+theme_selection = "Dark" # "Dark", "Light"
+LCD_polling_time = 1000 # milliseconds
+LCD_earlyUpdate_time = 200 # milliseconds
"""
By watching the RS232 communication of the AutoStart Suit telescope control I found the following commands:
@@ -49,6 +57,7 @@
+ ? :EK63#
"""
+
class MainWin(QtWidgets.QMainWindow):
"""
AutoSTAR_remote main window.
@@ -63,9 +72,12 @@ def __init__(self):
font.setStyleHint(QtGui.QFont.TypeWriter)
font.setPointSizeF(10)
self.ui.plainTextEdit_LCD.setFont(font)
- # states
- self.Telescope = None
- self.TelescopeName = ""
+ self.ui.actionconnect_ASCOM.setEnabled(has_ASCOM)
+ # communication interface
+ self.Interface = None
+ # persistent settings
+ self.Settings = QtCore.QSettings()
+ print(f'QSettings file: {self.Settings.fileName()}')
# LCD polling timer
self.PollingTimer = QtCore.QTimer()
self.PollingTimer.setSingleShot(True)
@@ -105,70 +117,71 @@ def __init__(self):
self.ui.pushButton_FocOut.pressed.connect(lambda: self.sendCommandBlind("F+"))
self.ui.pushButton_FocOut.released.connect(lambda: self.sendCommandBlind("FQ"))
-
@QtCore.pyqtSlot()
def closeEvent(self, event):
self.PollingTimer.stop()
- if self.Telescope is not None:
- if self.Telescope.Connected:
- self.Telescope.Connected = False
+ if self.Interface is not None:
+ self.Interface.close()
# proceed with close
event.accept()
+ def update_GuiOpenInterface(self):
+ """ update GUI elements after opening interface
+ """
+ self.ui.statusbar.showMessage(self.Interface.Name)
+ self.ui.actionconnect_ASCOM.setEnabled(False)
+ self.ui.actionconnect_UART.setEnabled(False)
+ self.ui.actiondisconnect.setEnabled(True)
+ self.ui.centralwidget.setEnabled(True)
+ if self.ui.actionpoll.isChecked():
+ if not self.PollingTimer.isActive():
+ self.PollingTimer.setInterval(LCD_earlyUpdate_time)
+ self.PollingTimer.start()
+ self.ui.actionupdate_now.setEnabled(True)
+
@QtCore.pyqtSlot()
- def on_actionconnect_triggered(self):
- try:
- Chooser = win32com.client.Dispatch("ASCOM.Utilities.Chooser")
- except win32com.client.pywintypes.com_error:
- QtWidgets.QMessageBox.critical(None, "Can not call ASCOM!",
- f"Is ASCOM installed?")
- return
- Chooser.DeviceType = 'Telescope'
- self.TelescopeName = Chooser.Choose(None)
- self.ui.statusbar.showMessage(self.TelescopeName)
- self.Telescope = win32com.client.Dispatch(self.TelescopeName)
- self.Telescope.Connected = True
- if not self.Telescope.Connected:
- QtWidgets.QMessageBox.critical(None, "Can not connect to telescope!",
- f"Please check connection to\n{self.TelescopeName}.\nMaybe it is already in use.")
+ def on_actionconnect_ASCOM_triggered(self):
+ self.Interface = ASCOM.ASCOM()
+ self.Interface.open()
+ if self.Interface.is_open():
+ self.update_GuiOpenInterface()
else:
- self.ui.actionconnect.setEnabled(False)
- self.ui.actiondisconnect.setEnabled(True)
- self.ui.centralwidget.setEnabled(True)
- if self.ui.actionpoll.isChecked():
- if not self.PollingTimer.isActive():
- self.PollingTimer.setInterval(LCD_earlyUpdate_time)
- self.PollingTimer.start()
- self.ui.actionupdate_now.setEnabled(True)
+ self.Interface.close()
+ self.Interface = None
+
+ @QtCore.pyqtSlot()
+ def on_actionconnect_UART_triggered(self):
+ Parameter = {k: self.Settings.value(k) for k in self.Settings.allKeys()}
+ self.Interface = UART.UART(Parameter=Parameter)
+ self.Interface.open()
+ if self.Interface.is_open():
+ print("DBG: UART is open")
+ self.update_GuiOpenInterface()
+ else:
+ self.Interface.close()
+ self.Interface = None
@QtCore.pyqtSlot()
def on_actiondisconnect_triggered(self):
self.PollingTimer.stop()
- if self.Telescope is not None:
- if self.Telescope.Connected:
- self.Telescope.Connected = False
- self.ui.actionconnect.setEnabled(True)
- self.ui.actiondisconnect.setEnabled(False)
- self.ui.centralwidget.setEnabled(False)
- self.ui.actionupdate_now.setEnabled(False)
-
- def sendAction(self, param):
- if self.Telescope is not None:
- if self.Telescope.Connected:
- return self.Telescope.Action("handbox", param)
- return None
+ if self.Interface is not None:
+ Parameter = self.Interface.get_Parameter()
+ for k, v in Parameter.items():
+ self.Settings.setValue(k, v)
+ self.Settings.sync()
+ self.Interface.close()
+ self.Interface = None
+ self.ui.actionconnect_ASCOM.setEnabled(has_ASCOM)
+ self.ui.actionconnect_UART.setEnabled(True)
+ self.ui.actiondisconnect.setEnabled(False)
+ self.ui.centralwidget.setEnabled(False)
+ self.ui.actionupdate_now.setEnabled(False)
def sendCommandBlind(self, cmd):
- if self.Telescope is not None:
- if self.Telescope.Connected:
- try:
- ret = self.Telescope.CommandBlind(cmd, False)
- except win32com.client.pywintypes.com_error as e:
- print(f'sendCommandBlind: {e}')
- return None
- else:
- return ret
- return None
+ if self.Interface is not None:
+ return self.Interface.sendCommandBlind(cmd)
+ else:
+ return None
def buttonAction(self, cmd, long_cmd=None):
"""
@@ -192,40 +205,35 @@ def buttonAction(self, cmd, long_cmd=None):
# with a translation table:
CharacterTranslationTable = {
0x0d: ord('\n'),
- #0x2020: ord(' '),
+ # 0x2020: ord(' '),
0xDF: ord('°'),
- 0x7E: 0x2192, #ord('>'),
- 0x7F: 0x2190, #ord('<'),
- 0x18: 0x2191, #ord('^'),
- 0x19: 0x2193, #ord('v'),
+ 0x7E: 0x2192, # ord('>'),
+ 0x7F: 0x2190, # ord('<'),
+ 0x18: 0x2191, # ord('^'),
+ 0x19: 0x2193, # ord('v'),
# bar graph symbols
0x5F: 0x2582,
0x81: 0x2583,
- 0x201A: 0x2584, # raw: 0x82
- 0x0192: 0x2585, # raw: 0x83
- 0x201E: 0x2586, # raw: 0x84
- 0x2026: 0x2587, # raw: 0x85
- 0x2020: 0x2588, # raw: 0x86
+ 0x201A: 0x2584, # raw: 0x82
+ 0x0192: 0x2585, # raw: 0x83
+ 0x201E: 0x2586, # raw: 0x84
+ 0x2026: 0x2587, # raw: 0x85
+ 0x2020: 0x2588, # raw: 0x86
}
def updateLCD(self):
- try:
- LcdText = self.Telescope.CommandString("ED", False)
- except win32com.client.pywintypes.com_error as e:
- # Sometimes the handbox needs long time for calculations and does not
- # send the LCD contents bfore the ASCOM driver trows a timeout exception.
- # Here we catch these timeout exceptions.
- print(f'updateLCD: {e}')
- LcdText = None
+ LcdText = None
+ if self.Interface is not None:
+ LcdText = self.Interface.sendCommandString("ED")
if LcdText is not None:
LcdText = LcdText.translate(self.CharacterTranslationTable)
Unknown = ord(LcdText[0])
Line1 = LcdText[1:17]
Line2 = LcdText[17:]
self.ui.plainTextEdit_LCD.setPlainText(f'{Line1}\n{Line2}')
- #print(f'{Unknown}: >{Line1}< >{Line2}<')
- #print(", ".join([f'{ord(c):02X}' for c in LcdText]))
- #print(bytes(LcdText, 'utf-8'))
+ # print(f'{Unknown}: >{Line1}< >{Line2}<')
+ # print(", ".join([f'{ord(c):02X}' for c in LcdText]))
+ # print(bytes(LcdText, 'utf-8'))
if self.ui.actionpoll.isChecked():
if not self.PollingTimer.isActive():
self.PollingTimer.setInterval(LCD_polling_time)
@@ -245,7 +253,7 @@ def on_actionpoll_toggled(self, isChecked):
self.PollingTimer.stop()
-## Start Qt event loop unless running in interactive mode.
+# Start Qt event loop unless running in interactive mode.
def main():
# build application
App = QtWidgets.QApplication(sys.argv)
@@ -253,7 +261,7 @@ def main():
App.setOrganizationDomain("Astro")
App.setApplicationName("AutoSTAR_remote")
#
- # stolen from https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
+ # copied from https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
if theme_selection == 'Dark':
App.setStyle("Fusion")
#
@@ -285,12 +293,11 @@ def main():
pass
#
MainWindow = MainWin()
- #MainWindow.resize(1400, 900)
+ # MainWindow.resize(1400, 900)
MainWindow.show()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
- #QtGui.QApplication.instance().exec_()
sys.exit(App.exec_())
if __name__ == '__main__':
- main()
\ No newline at end of file
+ main()
diff --git a/src/AutoSTAR_remote_ui.py b/src/AutoSTAR_remote_ui.py
index 15d4e4f..c0969a1 100644
--- a/src/AutoSTAR_remote_ui.py
+++ b/src/AutoSTAR_remote_ui.py
@@ -11,7 +11,7 @@
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
- MainWindow.resize(315, 475)
+ MainWindow.resize(315, 503)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setEnabled(False)
self.centralwidget.setObjectName("centralwidget")
@@ -111,9 +111,9 @@ def setupUi(self, MainWindow):
MainWindow.setStatusBar(self.statusbar)
self.actionselect = QtWidgets.QAction(MainWindow)
self.actionselect.setObjectName("actionselect")
- self.actionconnect = QtWidgets.QAction(MainWindow)
- self.actionconnect.setEnabled(True)
- self.actionconnect.setObjectName("actionconnect")
+ self.actionconnect_ASCOM = QtWidgets.QAction(MainWindow)
+ self.actionconnect_ASCOM.setEnabled(True)
+ self.actionconnect_ASCOM.setObjectName("actionconnect_ASCOM")
self.actiondisconnect = QtWidgets.QAction(MainWindow)
self.actiondisconnect.setEnabled(False)
self.actiondisconnect.setObjectName("actiondisconnect")
@@ -124,7 +124,10 @@ def setupUi(self, MainWindow):
self.actionupdate_now = QtWidgets.QAction(MainWindow)
self.actionupdate_now.setEnabled(False)
self.actionupdate_now.setObjectName("actionupdate_now")
- self.menuTelescope.addAction(self.actionconnect)
+ self.actionconnect_UART = QtWidgets.QAction(MainWindow)
+ self.actionconnect_UART.setObjectName("actionconnect_UART")
+ self.menuTelescope.addAction(self.actionconnect_ASCOM)
+ self.menuTelescope.addAction(self.actionconnect_UART)
self.menuTelescope.addAction(self.actiondisconnect)
self.menuDisplay.addAction(self.actionpoll)
self.menuDisplay.addAction(self.actionupdate_now)
@@ -186,10 +189,11 @@ def retranslateUi(self, MainWindow):
self.menuTelescope.setTitle(_translate("MainWindow", "Telescope"))
self.menuDisplay.setTitle(_translate("MainWindow", "LCD"))
self.actionselect.setText(_translate("MainWindow", "select"))
- self.actionconnect.setText(_translate("MainWindow", "connect"))
+ self.actionconnect_ASCOM.setText(_translate("MainWindow", "connect ASCOM"))
self.actiondisconnect.setText(_translate("MainWindow", "disconnect"))
self.actionpoll.setText(_translate("MainWindow", "poll"))
self.actionupdate_now.setText(_translate("MainWindow", "update now"))
+ self.actionconnect_UART.setText(_translate("MainWindow", "connect UART"))
if __name__ == "__main__":
diff --git a/src/AutoSTAR_remote_ui.ui b/src/AutoSTAR_remote_ui.ui
index 2138b18..4863f99 100644
--- a/src/AutoSTAR_remote_ui.ui
+++ b/src/AutoSTAR_remote_ui.ui
@@ -7,7 +7,7 @@
0
0
315
- 475
+ 503
@@ -282,7 +282,8 @@
Telescope
-
+
+
-
+
true
- connect
+ connect ASCOM
@@ -336,6 +337,11 @@
update now
+
+
+ connect UART
+
+
diff --git a/src/UART.py b/src/UART.py
new file mode 100644
index 0000000..6cf565c
--- /dev/null
+++ b/src/UART.py
@@ -0,0 +1,182 @@
+"""
+UART interface for AutoSTAR_remote
+"""
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+import sys
+import os
+import serial
+import serial.tools.list_ports
+
+import UART_ui
+
+
+class UART(QtWidgets.QDialog):
+
+ def __init__(self, Parameter={}):
+ super().__init__()
+ self.ui = UART_ui.Ui_Dialog()
+ self.ui.setupUi(self)
+ # populate GUI
+ self.find_Ports()
+ self.ui.comboBox_ComPort.addItems([p["desc"] for p in self.KnownPorts])
+ self.ui.comboBox_Speed.addItems([f'{s}' for s in [
+ 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+ 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200
+ ]])
+ self.ui.comboBox_DataBits.addItems([f'{b}' for b in [5, 6, 7, 8]])
+ self.ui.comboBox_StopBits.addItems(["1", "1.5", "2"])
+ self.ui.comboBox_Parity.addItems(["none", "even", "odd", "mark", "space"])
+ self.ui.checkBox_RtsCts.setChecked(False)
+ self.ui.checkBox_DsrDtr.setChecked(False)
+ self.ui.checkBox_XonXoff.setChecked(False)
+ self.ui.checkBox_SetTimeDate.setChecked(True)
+ # set defaults
+ self.set_Parameter(Parameter)
+ # no port opened
+ self.PortHandle = None
+ self.Name = ""
+
+ def get_Parameter(self):
+ Parameter = {
+ "port": self.KnownPorts[self.ui.comboBox_ComPort.currentIndex()]["port"],
+ "baudrate": self.ui.comboBox_Speed.currentText(),
+ "bytesize": self.ui.comboBox_DataBits.currentText(),
+ "parity": self.ui.comboBox_Parity.currentText(),
+ "stopbits": self.ui.comboBox_StopBits.currentText(),
+ "xonxoff": str(self.ui.checkBox_XonXoff.isChecked()),
+ "rtscts": str(self.ui.checkBox_RtsCts.isChecked()),
+ "dsrdtr": str(self.ui.checkBox_DsrDtr.isChecked()),
+ "setTimeDate": str(self.ui.checkBox_SetTimeDate.isChecked()),
+ }
+ return Parameter
+
+ def set_Parameter(self, Parameter):
+ for i in range(self.ui.comboBox_ComPort.count()):
+ if self.KnownPorts[i]["port"] == Parameter.get("port", ""):
+ self.ui.comboBox_ComPort.setCurrentIndex(i)
+ break
+ def set_Current(cb, text):
+ for j in range(cb.count()):
+ if cb.itemText(j) == text:
+ cb.setCurrentIndex(j)
+ break
+ set_Current(self.ui.comboBox_Speed, Parameter.get("baudrate", "9600"))
+ set_Current(self.ui.comboBox_DataBits, Parameter.get("bytesize", "8"))
+ set_Current(self.ui.comboBox_Parity, Parameter.get("parity", "none"))
+ set_Current(self.ui.comboBox_StopBits, Parameter.get("stopbits", "1"))
+ self.ui.checkBox_XonXoff.setChecked(Parameter.get("xonxoff", "") == "True")
+ self.ui.checkBox_RtsCts.setChecked(Parameter.get("rtscts", "") == "True")
+ self.ui.checkBox_DsrDtr.setChecked(Parameter.get("dsrdtr", "") == "True")
+ self.ui.checkBox_SetTimeDate.setChecked(Parameter.get("setDateTime", "True") == "True")
+
+ def find_Ports(self, include_links=False):
+ iterator = sorted(serial.tools.list_ports.comports(include_links=include_links))
+ self.KnownPorts = []
+ for port, desc, hwid in iterator:
+ self.KnownPorts.append({"port": port, "desc": desc, "hwid": hwid})
+
+ def open(self, timeout=2.0, write_timeout=None, inter_byte_timeout=None, exclusive=False):
+ ret = self.exec_()
+ if ret == self.Accepted:
+ self.Name = self.ui.comboBox_ComPort.currentText()
+ port = self.KnownPorts[self.ui.comboBox_ComPort.currentIndex()]["port"]
+ baudrate = int(self.ui.comboBox_Speed.currentText())
+ bytesize = {
+ "5": serial.FIVEBITS, "6": serial.SIXBITS, "7": serial.SEVENBITS, "8": serial.EIGHTBITS
+ }[self.ui.comboBox_DataBits.currentText()]
+ parity = {
+ "none": serial.PARITY_NONE, "even": serial.PARITY_EVEN, "odd": serial.PARITY_ODD,
+ "mark": serial.PARITY_MARK, "space": serial.PARITY_SPACE
+ }[self.ui.comboBox_Parity.currentText()]
+ stopbits = {
+ "1": serial.STOPBITS_ONE, "1.5": serial.STOPBITS_ONE_POINT_FIVE, "2": serial.STOPBITS_TWO
+ }[self.ui.comboBox_StopBits.currentText()]
+ xonxoff = self.ui.checkBox_XonXoff.isChecked()
+ rtscts = self.ui.checkBox_RtsCts.isChecked()
+ dsrdtr = self.ui.checkBox_DsrDtr.isChecked()
+ if os.name == "nt":
+ # windows supports exclusive access only!
+ exclusive = True
+ try:
+ self.PortHandle = serial.Serial(
+ port=port, baudrate=baudrate, bytesize=bytesize, parity=parity, stopbits=stopbits,
+ timeout=timeout, xonxoff=xonxoff, rtscts=rtscts, dsrdtr=dsrdtr, write_timeout=write_timeout,
+ inter_byte_timeout=inter_byte_timeout, exclusive=exclusive
+ )
+ except serial.SerialException as e:
+ QtWidgets.QMessageBox.critical(None, "Can not open UART port!", f"{e}")
+ self.PortHandle = None
+
+ def is_open(self):
+ if self.PortHandle is not None:
+ return self.PortHandle.is_open
+ else:
+ return False
+
+ def close(self):
+ if self.PortHandle is not None:
+ self.PortHandle.close()
+ self.PortHandle = None
+ self.Name = ""
+
+ def sendCommandBlind(self, cmd):
+ if self.is_open():
+ MeadeCmd = f'#:{cmd}#'.encode("ascii")
+ self.PortHandle.write(MeadeCmd)
+
+ def sendCommandString(self, cmd):
+ if self.is_open():
+ MeadeCmd = f'#:{cmd}#'.encode("ascii")
+ print(f'sendCommandString command: {MeadeCmd}')
+ self.PortHandle.write(MeadeCmd)
+ Response = self.PortHandle.read_until("#".encode("ascii"))
+ print(f'sendCommandString response: {Response}')
+ Response = Response.rstrip("#".encode("ascii"))
+ Response = Response.decode("utf-16")
+ return Response
+ return None
+
+
+if __name__ == '__main__':
+ # for debugging only
+ theme_selection = "Dark" # "Dark", "Light"
+ App = QtWidgets.QApplication(sys.argv)
+ App.setOrganizationName("GeierSoft")
+ App.setOrganizationDomain("Astro")
+ App.setApplicationName("AutoSTAR_remote")
+ # copied from https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
+ if theme_selection == 'Dark':
+ App.setStyle("Fusion")
+ #
+ # # Now use a palette to switch to dark colors:
+ dark_palette = QtGui.QPalette()
+ dark_palette.setColor(QtGui.QPalette.Window, QtGui.QColor(53, 53, 53))
+ dark_palette.setColor(QtGui.QPalette.WindowText, QtCore.Qt.white)
+ dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(35, 35, 35))
+ dark_palette.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(53, 53, 53))
+ dark_palette.setColor(QtGui.QPalette.ToolTipBase, QtGui.QColor(25, 25, 25))
+ dark_palette.setColor(QtGui.QPalette.ToolTipText, QtCore.Qt.white)
+ dark_palette.setColor(QtGui.QPalette.Text, QtCore.Qt.white)
+ dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(53, 53, 53))
+ dark_palette.setColor(QtGui.QPalette.ButtonText, QtCore.Qt.white)
+ dark_palette.setColor(QtGui.QPalette.BrightText, QtCore.Qt.red)
+ dark_palette.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218))
+ dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218))
+ dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(35, 35, 35))
+ dark_palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Button, QtGui.QColor(53, 53, 53))
+ dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, QtCore.Qt.darkGray)
+ dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, QtCore.Qt.darkGray)
+ dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Text, QtCore.Qt.darkGray)
+ dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Light, QtGui.QColor(53, 53, 53))
+ App.setPalette(dark_palette)
+ elif theme_selection == 'Light':
+ App.setStyle("")
+ pass
+ else:
+ pass
+ #
+ UartDialog = UART()
+ UartDialog.show()
+ if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
+ sys.exit(App.exec_())
diff --git a/src/UART_ui.py b/src/UART_ui.py
new file mode 100644
index 0000000..db6bef2
--- /dev/null
+++ b/src/UART_ui.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'src\UART_ui.ui'
+#
+# Created by: PyQt5 UI code generator 5.9.2
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(390, 139)
+ self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.label_5 = QtWidgets.QLabel(Dialog)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout.addWidget(self.label_5, 0, 4, 1, 1)
+ self.comboBox_Speed = QtWidgets.QComboBox(Dialog)
+ self.comboBox_Speed.setObjectName("comboBox_Speed")
+ self.gridLayout.addWidget(self.comboBox_Speed, 0, 5, 1, 1)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ self.comboBox_DataBits = QtWidgets.QComboBox(Dialog)
+ self.comboBox_DataBits.setObjectName("comboBox_DataBits")
+ self.gridLayout.addWidget(self.comboBox_DataBits, 1, 1, 1, 1)
+ self.label_3 = QtWidgets.QLabel(Dialog)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 1, 2, 1, 1)
+ self.comboBox_StopBits = QtWidgets.QComboBox(Dialog)
+ self.comboBox_StopBits.setObjectName("comboBox_StopBits")
+ self.gridLayout.addWidget(self.comboBox_StopBits, 1, 3, 1, 1)
+ self.label_4 = QtWidgets.QLabel(Dialog)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 1, 4, 1, 1)
+ self.comboBox_Parity = QtWidgets.QComboBox(Dialog)
+ self.comboBox_Parity.setObjectName("comboBox_Parity")
+ self.gridLayout.addWidget(self.comboBox_Parity, 1, 5, 1, 1)
+ self.label_6 = QtWidgets.QLabel(Dialog)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout.addWidget(self.label_6, 2, 0, 1, 1)
+ self.checkBox_RtsCts = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_RtsCts.setObjectName("checkBox_RtsCts")
+ self.gridLayout.addWidget(self.checkBox_RtsCts, 2, 1, 1, 1)
+ self.checkBox_DsrDtr = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_DsrDtr.setObjectName("checkBox_DsrDtr")
+ self.gridLayout.addWidget(self.checkBox_DsrDtr, 2, 3, 1, 1)
+ self.checkBox_XonXoff = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_XonXoff.setObjectName("checkBox_XonXoff")
+ self.gridLayout.addWidget(self.checkBox_XonXoff, 2, 5, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 6)
+ self.comboBox_ComPort = QtWidgets.QComboBox(Dialog)
+ self.comboBox_ComPort.setObjectName("comboBox_ComPort")
+ self.gridLayout.addWidget(self.comboBox_ComPort, 0, 1, 1, 3)
+ self.label_7 = QtWidgets.QLabel(Dialog)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout.addWidget(self.label_7, 3, 0, 1, 1)
+ self.checkBox_SetTimeDate = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_SetTimeDate.setObjectName("checkBox_SetTimeDate")
+ self.gridLayout.addWidget(self.checkBox_SetTimeDate, 3, 1, 1, 3)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.label.setText(_translate("Dialog", "COM port"))
+ self.label_5.setText(_translate("Dialog", "Speed"))
+ self.label_2.setText(_translate("Dialog", "Data bits"))
+ self.label_3.setText(_translate("Dialog", "Stop bits"))
+ self.label_4.setText(_translate("Dialog", "Parity"))
+ self.label_6.setText(_translate("Dialog", "Flow control"))
+ self.checkBox_RtsCts.setText(_translate("Dialog", "RTS/CTS"))
+ self.checkBox_DsrDtr.setText(_translate("Dialog", "DSR/DTR"))
+ self.checkBox_XonXoff.setText(_translate("Dialog", "XON/XOFF"))
+ self.label_7.setText(_translate("Dialog", "Telescope"))
+ self.checkBox_SetTimeDate.setText(_translate("Dialog", "Set current date and time"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ Dialog = QtWidgets.QDialog()
+ ui = Ui_Dialog()
+ ui.setupUi(Dialog)
+ Dialog.show()
+ sys.exit(app.exec_())
+
diff --git a/src/UART_ui.ui b/src/UART_ui.ui
new file mode 100644
index 0000000..f5e894c
--- /dev/null
+++ b/src/UART_ui.ui
@@ -0,0 +1,156 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 390
+ 139
+
+
+
+ Dialog
+
+
+ -
+
+
+ COM port
+
+
+
+ -
+
+
+ Speed
+
+
+
+ -
+
+
+ -
+
+
+ Data bits
+
+
+
+ -
+
+
+ -
+
+
+ Stop bits
+
+
+
+ -
+
+
+ -
+
+
+ Parity
+
+
+
+ -
+
+
+ -
+
+
+ Flow control
+
+
+
+ -
+
+
+ RTS/CTS
+
+
+
+ -
+
+
+ DSR/DTR
+
+
+
+ -
+
+
+ XON/XOFF
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ -
+
+
+ Telescope
+
+
+
+ -
+
+
+ Set current date and time
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
From 6312ac49df6bcaaff62754c6ab95b0050546fcf9 Mon Sep 17 00:00:00 2001
From: scriptorron <22291722+scriptorron@users.noreply.github.com>
Date: Wed, 30 Nov 2022 09:39:45 +0100
Subject: [PATCH 2/3] catching error when aborting ASCOM connect; improved
reception of LCD contents; persistent storing of UART settings; better
telescope initialization when connecting with UART
---
src/ASCOM.py | 43 ++++++++++++++++++++++------
src/AutoSTAR_remote.py | 46 ++++++++++++------------------
src/UART.py | 65 ++++++++++++++++++++++++++++++++++++------
3 files changed, 109 insertions(+), 45 deletions(-)
diff --git a/src/ASCOM.py b/src/ASCOM.py
index 4323048..a628406 100644
--- a/src/ASCOM.py
+++ b/src/ASCOM.py
@@ -22,12 +22,13 @@ def open(self):
else:
Chooser.DeviceType = 'Telescope'
self.Name = Chooser.Choose(None)
- self.Telescope = win32com.client.Dispatch(self.Name)
- self.Telescope.Connected = True
- if not self.Telescope.Connected:
- QtWidgets.QMessageBox.critical(None, "Can not connect to telescope!",
- f"Please check connection to\n{self.Name}.\nMaybe it is already in use.")
- self.Telescope = None
+ if self.Name != "":
+ self.Telescope = win32com.client.Dispatch(self.Name)
+ self.Telescope.Connected = True
+ if not self.Telescope.Connected:
+ QtWidgets.QMessageBox.critical(None, "Can not connect to telescope!",
+ f"Please check connection to\n{self.Name}.\nMaybe it is already in use.")
+ self.Telescope = None
def get_Parameter(self):
# has no parameter
@@ -56,13 +57,37 @@ def sendCommandBlind(self, cmd):
return ret
return None
- def sendCommandString(self, cmd):
+ # The :ED# command sends the LCD contents, coded with the char table of the SED1233 LCD controller.
+ # For any reason the COM interface or the win32com transforms this into unicode. Unfortunately the
+ # special characters of the SED1233 controller get mapped to the wrong unicode. Here we fix this
+ # with a translation table:
+ CharacterTranslationTable = {
+ 0x0d: ord('\n'),
+ # 0x2020: ord(' '),
+ 0xDF: ord('°'),
+ 0x7E: 0x2192, # ord('>'),
+ 0x7F: 0x2190, # ord('<'),
+ 0x18: 0x2191, # ord('^'),
+ 0x19: 0x2193, # ord('v'),
+ # bar graph symbols
+ 0x5F: 0x2582,
+ 0x81: 0x2583,
+ 0x201A: 0x2584, # raw: 0x82
+ 0x0192: 0x2585, # raw: 0x83
+ 0x201E: 0x2586, # raw: 0x84
+ 0x2026: 0x2587, # raw: 0x85
+ 0x2020: 0x2588, # raw: 0x86
+ }
+
+ def get_LCD(self):
if self.is_open():
try:
- return self.Telescope.CommandString(cmd, False)
+ Response = self.Telescope.CommandString("ED", False)
except win32com.client.pywintypes.com_error as e:
# Sometimes the handbox needs long time for calculations and does not
# send the LCD contents before the ASCOM driver trows a timeout exception.
# Here we catch these timeout exceptions.
- print(f'updateLCD: {e}')
+ print(f'ERROR in get_LCD: {e}')
+ #print(f'sendCommandString response: {Response}')
+ return Response[1:].translate(self.CharacterTranslationTable)
return None
diff --git a/src/AutoSTAR_remote.py b/src/AutoSTAR_remote.py
index fecbb68..44cc9c1 100644
--- a/src/AutoSTAR_remote.py
+++ b/src/AutoSTAR_remote.py
@@ -57,6 +57,16 @@
+ ? :EK63#
"""
+"""
+21:06:26.686 UTCDate Set - 11.22.22 20:06:26
+21:06:26.686 SendString Transmitting #:GG#
+21:06:26.704 SendString Received -01
+21:06:26.708 SendChars Transmitting #:SL21:06:26# <-- 21:06
+21:06:26.744 SendChars Received 1
+21:06:26.744 SendChars Transmitting #:SC11.22.22# <-- 22. Nov 2022
+21:06:26.962 SendChars Received 1
+"""
+
class MainWin(QtWidgets.QMainWindow):
"""
@@ -156,6 +166,10 @@ def on_actionconnect_UART_triggered(self):
self.Interface.open()
if self.Interface.is_open():
print("DBG: UART is open")
+ Parameter = self.Interface.get_Parameter()
+ for k, v in Parameter.items():
+ self.Settings.setValue(k, v)
+ self.Settings.sync()
self.update_GuiOpenInterface()
else:
self.Interface.close()
@@ -199,41 +213,19 @@ def buttonAction(self, cmd, long_cmd=None):
self.PollingTimer.setInterval(LCD_earlyUpdate_time)
self.PollingTimer.start()
- # The :ED# command sends the LCD contents, coded with the char table of the SED1233 LCD controller.
- # For any reason the COM interface or the win32com transforms this into unicode. Unfortunately the
- # special characters of the SED1233 controller get mapped to the wrong unicode. Here we fix this
- # with a translation table:
- CharacterTranslationTable = {
- 0x0d: ord('\n'),
- # 0x2020: ord(' '),
- 0xDF: ord('°'),
- 0x7E: 0x2192, # ord('>'),
- 0x7F: 0x2190, # ord('<'),
- 0x18: 0x2191, # ord('^'),
- 0x19: 0x2193, # ord('v'),
- # bar graph symbols
- 0x5F: 0x2582,
- 0x81: 0x2583,
- 0x201A: 0x2584, # raw: 0x82
- 0x0192: 0x2585, # raw: 0x83
- 0x201E: 0x2586, # raw: 0x84
- 0x2026: 0x2587, # raw: 0x85
- 0x2020: 0x2588, # raw: 0x86
- }
-
def updateLCD(self):
LcdText = None
if self.Interface is not None:
- LcdText = self.Interface.sendCommandString("ED")
+ LcdText = self.Interface.get_LCD()
if LcdText is not None:
- LcdText = LcdText.translate(self.CharacterTranslationTable)
- Unknown = ord(LcdText[0])
- Line1 = LcdText[1:17]
- Line2 = LcdText[17:]
+ Line1 = LcdText[0:16]
+ Line2 = LcdText[16:]
self.ui.plainTextEdit_LCD.setPlainText(f'{Line1}\n{Line2}')
# print(f'{Unknown}: >{Line1}< >{Line2}<')
# print(", ".join([f'{ord(c):02X}' for c in LcdText]))
# print(bytes(LcdText, 'utf-8'))
+ else:
+ print('DBG: no response from get_LCD.')
if self.ui.actionpoll.isChecked():
if not self.PollingTimer.isActive():
self.PollingTimer.setInterval(LCD_polling_time)
diff --git a/src/UART.py b/src/UART.py
index 6cf565c..00671ce 100644
--- a/src/UART.py
+++ b/src/UART.py
@@ -5,6 +5,7 @@
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import os
+import datetime
import serial
import serial.tools.list_ports
@@ -78,7 +79,7 @@ def find_Ports(self, include_links=False):
def open(self, timeout=2.0, write_timeout=None, inter_byte_timeout=None, exclusive=False):
ret = self.exec_()
- if ret == self.Accepted:
+ if (ret == self.Accepted) and (len(self.KnownPorts) > 0):
self.Name = self.ui.comboBox_ComPort.currentText()
port = self.KnownPorts[self.ui.comboBox_ComPort.currentIndex()]["port"]
baudrate = int(self.ui.comboBox_Speed.currentText())
@@ -107,6 +108,29 @@ def open(self, timeout=2.0, write_timeout=None, inter_byte_timeout=None, exclusi
except serial.SerialException as e:
QtWidgets.QMessageBox.critical(None, "Can not open UART port!", f"{e}")
self.PortHandle = None
+ self.initializeCommunication()
+
+ def initializeCommunication(self):
+ if self.is_open():
+ # clear the buffer
+ self.PortHandle.write('#'.encode("ascii"))
+ # Attempting manual bypass of prompts
+ for i in range(10):
+ self.sendCommandBlind("EK9")
+ self.sendCommandString("ED")
+ # set date and time
+ if self.ui.checkBox_SetTimeDate.isChecked():
+ # TODO: make this aware of the daylight saving setting of the controller!
+ now = datetime.datetime.now()
+ time = now.strftime("%H:%M:%S")
+ self.PortHandle.write(f'#:SL{time}#'.encode("ascii"))
+ Response = self.PortHandle.read(size=1)
+ print(f'DBG: #:SL{time}# --> {Response}')
+ # MM/DD/YY
+ date = now.strftime("%m/%d/%y")
+ self.PortHandle.write(f'#:SC{date}#'.encode("ascii"))
+ Response = self.PortHandle.read(size=1)
+ print(f'DBG: #:SC{date}# --> {Response}')
def is_open(self):
if self.PortHandle is not None:
@@ -125,19 +149,42 @@ def sendCommandBlind(self, cmd):
MeadeCmd = f'#:{cmd}#'.encode("ascii")
self.PortHandle.write(MeadeCmd)
- def sendCommandString(self, cmd):
+ # The :ED# command sends the LCD contents, coded with the char table of the SED1233 LCD controller.
+ # For any reason the COM interface or the win32com transforms this into unicode. Unfortunately the
+ # special characters of the SED1233 controller get mapped to the wrong unicode. Here we fix this
+ # with a translation table:
+ CharacterTranslationTable = {
+ 0x0d: ord('\n'),
+ # 0x2020: ord(' '),
+ 0xDF: ord('°'),
+ 0x7E: 0x2192, # ord('>'),
+ 0x7F: 0x2190, # ord('<'),
+ 0x18: 0x2191, # ord('^'),
+ 0x19: 0x2193, # ord('v'),
+ # bar graph symbols
+ 0x5F: 0x2582,
+ 0x81: 0x2583,
+ 0x82: 0x2584, # raw: 0x82
+ 0x83: 0x2585, # raw: 0x83
+ 0x84: 0x2586, # raw: 0x84
+ 0x85: 0x2587, # raw: 0x85
+ 0x86: 0x2588, # raw: 0x86
+ }
+
+ def get_LCD(self):
if self.is_open():
- MeadeCmd = f'#:{cmd}#'.encode("ascii")
- print(f'sendCommandString command: {MeadeCmd}')
+ MeadeCmd = b'#:ED#'
+ #print(f'sendCommandString command: {MeadeCmd}')
self.PortHandle.write(MeadeCmd)
- Response = self.PortHandle.read_until("#".encode("ascii"))
- print(f'sendCommandString response: {Response}')
- Response = Response.rstrip("#".encode("ascii"))
- Response = Response.decode("utf-16")
- return Response
+ Response = self.PortHandle.read_until(b"#")
+ print(f'DBG: get_LCD response: {Response}')
+ Response = Response[1:].rstrip(b"#")
+ Response = Response.decode("latin-1")
+ return Response.translate(self.CharacterTranslationTable)
return None
+
if __name__ == '__main__':
# for debugging only
theme_selection = "Dark" # "Dark", "Light"
From 75e204290490b1041d5e0982eea43cabcf3116bd Mon Sep 17 00:00:00 2001
From: scriptorron <22291722+scriptorron@users.noreply.github.com>
Date: Wed, 30 Nov 2022 13:00:15 +0100
Subject: [PATCH 3/3] more documentation; command line switch for debugging
messages; improved response time when UART connected
---
README.md | 18 +++++++++++++++++-
src/ASCOM.py | 13 ++++++++++---
src/AutoSTAR_remote.py | 28 +++++++++++++++++++---------
src/UART.py | 30 ++++++++++++++++++++----------
4 files changed, 66 insertions(+), 23 deletions(-)
diff --git a/README.md b/README.md
index 4b35b96..4888ec7 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,24 @@
# AutoSTAR_remote
-This is a GUI to remote control (using ASCOM) the Meade AutoSTAR #497 handheld.
+This is a GUI to remote control (using ASCOM or serial interface) the Meade AutoSTAR #497 handheld.

Press [SHIFT] when clicking on "ENTER", "MODE" or "GO TO" to generate a long key press.
+You can change the serial port parameters when connecting with UART. Default parameters for the MEADE AutoSTAR #497 are:
+- Speed: 9600 baud
+- 8 data bits
+- 1 stop bit
+- no parity
+- no flow control
+
+When connecting with the serial port you have the option to set the time and date of the AutoSTAR to the computer clock. This feature is not fully tested. Especially the daylight saving may be wrong. Please check the AutoSTAR settings if you see strange errors when doing GOTO to an object.
+
The compiled binary just needs to be unpacked. No installation and no Python is needed. ASCOM driver https://bitbucket.org/cjdskunkworks/meadeautostar497 must be installed and off course you need to connect your #497 AutoSTAR with your computer.
+
+For running the Python source code you need the following packages:
+- PyQt5
+- pyserial
+- win32com when using ASCOM on Windows
+
+The Python source code runs also on Raspberry Pi (Astroberry).
diff --git a/src/ASCOM.py b/src/ASCOM.py
index a628406..686c8d4 100644
--- a/src/ASCOM.py
+++ b/src/ASCOM.py
@@ -8,9 +8,15 @@
class ASCOM:
- def __init__(self):
+ def __init__(self, showDebugMessages=False):
self.Telescope = None
self.Name = ""
+ #
+ self.showDebugMessages = showDebugMessages
+
+ def dbgMsg(self, msg):
+ if self.showDebugMessages:
+ print(f'DEBUG: {msg}')
def open(self):
self.close()
@@ -48,10 +54,11 @@ def close(self):
def sendCommandBlind(self, cmd):
if self.is_open():
+ self.dbgMsg(f'sendCommandBlind: {cmd}')
try:
ret = self.Telescope.CommandBlind(cmd, False)
except win32com.client.pywintypes.com_error as e:
- print(f'sendCommandBlind: {e}')
+ print(f'ERROR in sendCommandBlind: {e}')
return None
else:
return ret
@@ -88,6 +95,6 @@ def get_LCD(self):
# send the LCD contents before the ASCOM driver trows a timeout exception.
# Here we catch these timeout exceptions.
print(f'ERROR in get_LCD: {e}')
- #print(f'sendCommandString response: {Response}')
+ self.dbgMsg(f'get_LCD response: ED --> {Response}')
return Response[1:].translate(self.CharacterTranslationTable)
return None
diff --git a/src/AutoSTAR_remote.py b/src/AutoSTAR_remote.py
index 44cc9c1..f58ce7c 100644
--- a/src/AutoSTAR_remote.py
+++ b/src/AutoSTAR_remote.py
@@ -21,7 +21,7 @@
theme_selection = "Dark" # "Dark", "Light"
LCD_polling_time = 1000 # milliseconds
-LCD_earlyUpdate_time = 200 # milliseconds
+LCD_earlyUpdate_time = 50 # milliseconds
"""
By watching the RS232 communication of the AutoStart Suit telescope control I found the following commands:
@@ -58,6 +58,8 @@
"""
"""
+How the ASCOM driver sets time and date:
+
21:06:26.686 UTCDate Set - 11.22.22 20:06:26
21:06:26.686 SendString Transmitting #:GG#
21:06:26.704 SendString Received -01
@@ -73,8 +75,9 @@ class MainWin(QtWidgets.QMainWindow):
AutoSTAR_remote main window.
"""
- def __init__(self):
+ def __init__(self, showDebugMessages=False):
super(MainWin, self).__init__()
+ self.showDebugMessages = showDebugMessages
self.ui = AutoSTAR_remote_ui.Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowTitle(f'AutoSTAR_remote {version}')
@@ -87,7 +90,7 @@ def __init__(self):
self.Interface = None
# persistent settings
self.Settings = QtCore.QSettings()
- print(f'QSettings file: {self.Settings.fileName()}')
+ self.dbgMsg(f'QSettings file: {self.Settings.fileName()}')
# LCD polling timer
self.PollingTimer = QtCore.QTimer()
self.PollingTimer.setSingleShot(True)
@@ -127,6 +130,10 @@ def __init__(self):
self.ui.pushButton_FocOut.pressed.connect(lambda: self.sendCommandBlind("F+"))
self.ui.pushButton_FocOut.released.connect(lambda: self.sendCommandBlind("FQ"))
+ def dbgMsg(self, msg):
+ if self.showDebugMessages:
+ print(f'DEBUG: {msg}')
+
@QtCore.pyqtSlot()
def closeEvent(self, event):
self.PollingTimer.stop()
@@ -151,7 +158,7 @@ def update_GuiOpenInterface(self):
@QtCore.pyqtSlot()
def on_actionconnect_ASCOM_triggered(self):
- self.Interface = ASCOM.ASCOM()
+ self.Interface = ASCOM.ASCOM(showDebugMessages=self.showDebugMessages)
self.Interface.open()
if self.Interface.is_open():
self.update_GuiOpenInterface()
@@ -162,10 +169,9 @@ def on_actionconnect_ASCOM_triggered(self):
@QtCore.pyqtSlot()
def on_actionconnect_UART_triggered(self):
Parameter = {k: self.Settings.value(k) for k in self.Settings.allKeys()}
- self.Interface = UART.UART(Parameter=Parameter)
+ self.Interface = UART.UART(Parameter=Parameter, showDebugMessages=self.showDebugMessages)
self.Interface.open()
if self.Interface.is_open():
- print("DBG: UART is open")
Parameter = self.Interface.get_Parameter()
for k, v in Parameter.items():
self.Settings.setValue(k, v)
@@ -221,11 +227,10 @@ def updateLCD(self):
Line1 = LcdText[0:16]
Line2 = LcdText[16:]
self.ui.plainTextEdit_LCD.setPlainText(f'{Line1}\n{Line2}')
- # print(f'{Unknown}: >{Line1}< >{Line2}<')
# print(", ".join([f'{ord(c):02X}' for c in LcdText]))
# print(bytes(LcdText, 'utf-8'))
else:
- print('DBG: no response from get_LCD.')
+ self.dbgMsg('No response from get_LCD.')
if self.ui.actionpoll.isChecked():
if not self.PollingTimer.isActive():
self.PollingTimer.setInterval(LCD_polling_time)
@@ -247,6 +252,11 @@ def on_actionpoll_toggled(self, isChecked):
# Start Qt event loop unless running in interactive mode.
def main():
+ import argparse
+ parser = argparse.ArgumentParser(description="remote control for MEADE AutoSTAR #497")
+ parser.add_argument("-d", "--debug", action="store_true",
+ help="enable debug messages")
+ args = parser.parse_args()
# build application
App = QtWidgets.QApplication(sys.argv)
App.setOrganizationName("GeierSoft")
@@ -284,7 +294,7 @@ def main():
else:
pass
#
- MainWindow = MainWin()
+ MainWindow = MainWin(showDebugMessages=args.debug)
# MainWindow.resize(1400, 900)
MainWindow.show()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
diff --git a/src/UART.py b/src/UART.py
index 00671ce..9575e82 100644
--- a/src/UART.py
+++ b/src/UART.py
@@ -14,7 +14,7 @@
class UART(QtWidgets.QDialog):
- def __init__(self, Parameter={}):
+ def __init__(self, Parameter={}, showDebugMessages=False):
super().__init__()
self.ui = UART_ui.Ui_Dialog()
self.ui.setupUi(self)
@@ -37,6 +37,12 @@ def __init__(self, Parameter={}):
# no port opened
self.PortHandle = None
self.Name = ""
+ #
+ self.showDebugMessages = showDebugMessages
+
+ def dbgMsg(self, msg):
+ if self.showDebugMessages:
+ print(f'DEBUG: {msg}')
def get_Parameter(self):
Parameter = {
@@ -117,20 +123,22 @@ def initializeCommunication(self):
# Attempting manual bypass of prompts
for i in range(10):
self.sendCommandBlind("EK9")
- self.sendCommandString("ED")
+ self.get_LCD()
# set date and time
if self.ui.checkBox_SetTimeDate.isChecked():
# TODO: make this aware of the daylight saving setting of the controller!
now = datetime.datetime.now()
time = now.strftime("%H:%M:%S")
- self.PortHandle.write(f'#:SL{time}#'.encode("ascii"))
+ cmd = f'#:SL{time}#'.encode("ascii")
+ self.PortHandle.write(cmd)
Response = self.PortHandle.read(size=1)
- print(f'DBG: #:SL{time}# --> {Response}')
+ self.dbgMsg(f'{cmd} --> {Response}')
# MM/DD/YY
- date = now.strftime("%m/%d/%y")
- self.PortHandle.write(f'#:SC{date}#'.encode("ascii"))
- Response = self.PortHandle.read(size=1)
- print(f'DBG: #:SC{date}# --> {Response}')
+ date = now.strftime("%m.%d.%y")
+ cmd = f'#:SC{date}#'.encode("ascii")
+ self.PortHandle.write(cmd)
+ Response = self.PortHandle.read(size=66)
+ self.dbgMsg(f'{cmd} --> {Response}')
def is_open(self):
if self.PortHandle is not None:
@@ -148,6 +156,8 @@ def sendCommandBlind(self, cmd):
if self.is_open():
MeadeCmd = f'#:{cmd}#'.encode("ascii")
self.PortHandle.write(MeadeCmd)
+ self.PortHandle.flush()
+ self.dbgMsg(f'sendCommandBlind: {MeadeCmd}')
# The :ED# command sends the LCD contents, coded with the char table of the SED1233 LCD controller.
# For any reason the COM interface or the win32com transforms this into unicode. Unfortunately the
@@ -174,10 +184,10 @@ def sendCommandBlind(self, cmd):
def get_LCD(self):
if self.is_open():
MeadeCmd = b'#:ED#'
- #print(f'sendCommandString command: {MeadeCmd}')
self.PortHandle.write(MeadeCmd)
+ self.PortHandle.flush()
Response = self.PortHandle.read_until(b"#")
- print(f'DBG: get_LCD response: {Response}')
+ self.dbgMsg(f'get_LCD response: {MeadeCmd} --> {Response}')
Response = Response[1:].rstrip(b"#")
Response = Response.decode("latin-1")
return Response.translate(self.CharacterTranslationTable)