838 self, port: int = DEFAULT_PORT, ref_voltage: float = DEFAULT_REF_VOLTAGE
841 self.setWindowTitle(
"Sense Testing GUI")
842 self.resize(1000, 620)
851 i:
True for i
in range(1, MAX_DISPLAY_CHANNELS + 1)
875 self.
_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
876 self.
_send_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
882 self.
_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
887 central = QtWidgets.QWidget()
888 self.setCentralWidget(central)
889 layout = QtWidgets.QVBoxLayout(central)
892 toolbar = QtWidgets.QHBoxLayout()
893 toolbar.addWidget(QtWidgets.QLabel(
"Port:"))
900 toolbar.addSpacing(20)
901 toolbar.addWidget(QtWidgets.QLabel(
"Reference voltage (V):"))
907 "Internal ref / PT: 2.5 V (default); TC_Board VDD: 3.3 V"
912 toolbar.addSpacing(20)
913 toolbar.addWidget(QtWidgets.QLabel(
"Time window (s):"))
923 toolbar.addSpacing(20)
926 "Convert voltage to °C using K-type thermocouple calibration."
933 "Convert differential voltage to °C for Pt1000 RTD (1000 µA excitation)."
940 toolbar.addSpacing(20)
943 "Ratiometric: force from raw 32-bit ADC code (no voltage step). Uses Reference voltage (V) as ADC ref, plus excitation, sensitivity (mV/V), PGA gain, full scale force."
950 "Interpret raw uint32 as AS5600 12-bit angle (0–4095) and display as degrees (0–360°)."
957 toolbar.addSpacing(20)
963 layout.addLayout(toolbar)
966 force_row = QtWidgets.QHBoxLayout()
967 force_row.addWidget(QtWidgets.QLabel(
"Excitation (V):"))
974 force_row.addWidget(QtWidgets.QLabel(
"Sensitivity (mV/V):"))
980 "Load cell sensitivity at full scale, mV per V of excitation."
983 force_row.addWidget(QtWidgets.QLabel(
"PGA gain:"))
989 "PGA gain used on the board (e.g. 32 for ADS126X_GAIN_32)."
992 force_row.addWidget(QtWidgets.QLabel(
"Full scale force:"))
998 "Force at full scale (same unit as desired display, e.g. N or lbf)."
1001 force_row.addStretch()
1002 layout.addLayout(force_row)
1006 "To receive data: set this PC's Ethernet adapter to 192.168.2.20, subnet 255.255.255.0. "
1007 "Boards send to 192.168.2.20:5006. Boards listen on port 5005 — set Target port to 5005 to send commands."
1009 self.
network_hint.setStyleSheet(
"color: #8ab; font-size: 11px;")
1014 channel_row = QtWidgets.QHBoxLayout()
1015 channel_row.addWidget(QtWidgets.QLabel(
"Graph channels:"))
1016 for ch
in range(1, MAX_DISPLAY_CHANNELS + 1):
1017 cb = QtWidgets.QCheckBox(f
"Ch {ch}")
1019 r, g, b = SENSOR_COLORS[(ch - 1) % len(SENSOR_COLORS)]
1021 f
"QCheckBox {{ color: rgb({r},{g},{b}); font-weight: bold; }}"
1023 cb.stateChanged.connect(
1027 channel_row.addWidget(cb)
1028 channel_row.addStretch()
1029 layout.addLayout(channel_row)
1032 content = QtWidgets.QHBoxLayout()
1035 _axis_style = {
"color":
"#FFFFFF",
"font-size":
"11pt"}
1036 self.
plot_widget.setLabel(
"left",
"Voltage (V)", **_axis_style)
1037 self.
plot_widget.setLabel(
"bottom",
"Time (s)", **_axis_style)
1038 for axis_name
in (
"left",
"bottom"):
1039 ax = self.
plot_widget.getPlotItem().getAxis(axis_name)
1041 if hasattr(ax,
"setStyle"):
1042 ax.setStyle(tickTextColor=
"#FFFFFF", tickFont=QtGui.QFont(
None, 10))
1046 if hasattr(ax,
"label")
and hasattr(ax.label,
"setColor"):
1047 ax.label.setColor(
"#FFFFFF")
1050 self.
plot_widget.showGrid(x=
True, y=
True, alpha=0.3)
1056 board_panel = QtWidgets.QGroupBox(
"Board status")
1057 board_panel.setMinimumWidth(220)
1058 board_layout = QtWidgets.QVBoxLayout(board_panel)
1073 "color: #8ab; font-size: 11px; font-family: monospace;"
1089 board_layout.addStretch()
1090 content.addWidget(board_panel)
1093 cmd_panel = QtWidgets.QGroupBox(
"Server & commands")
1094 cmd_panel.setMinimumWidth(260)
1095 cmd_layout = QtWidgets.QVBoxLayout(cmd_panel)
1101 target_row = QtWidgets.QHBoxLayout()
1102 target_row.addWidget(QtWidgets.QLabel(
"Target IP:"))
1107 cmd_layout.addLayout(target_row)
1109 port_row = QtWidgets.QHBoxLayout()
1110 port_row.addWidget(QtWidgets.QLabel(
"Target port:"))
1115 "Board listens on 5005. Use 5005 to send server heartbeat and commands."
1118 cmd_layout.addLayout(port_row)
1125 sens_grp = QtWidgets.QGroupBox(
"Sensor config packet")
1126 sens_layout = QtWidgets.QVBoxLayout(sens_grp)
1130 "Enable serial printing on board"
1133 "If checked, sensor config packet tells the board to enable Serial output (DAQv2-Comms field)."
1136 sens_layout.addWidget(QtWidgets.QLabel(
"Sensor IDs (comma-separated):"))
1139 "e.g. 1,2,3,10 (leave empty for board default)"
1143 sens_layout.addWidget(QtWidgets.QLabel(
"Reference voltage:"))
1150 "Reference voltage enum sent in SENSOR_CONFIG:\n"
1151 " - Internal 2.5 V\n"
1152 " - VDD (e.g. 3.3 V)\n"
1153 " - Ignore / board default"
1156 sens_layout.addWidget(QtWidgets.QLabel(
"Actuator controller IP:"))
1164 cmd_layout.addWidget(sens_grp)
1167 abort_grp = QtWidgets.QGroupBox(
"Abort packets")
1168 abort_layout = QtWidgets.QVBoxLayout(abort_grp)
1184 cmd_layout.addWidget(abort_grp)
1186 cmd_layout.addStretch()
1189 right_column = QtWidgets.QWidget()
1190 right_column.setMinimumWidth(260)
1191 right_column_layout = QtWidgets.QVBoxLayout(right_column)
1192 right_column_layout.setContentsMargins(0, 0, 0, 0)
1193 right_column_layout.addWidget(cmd_panel)
1196 ota_grp = QtWidgets.QGroupBox(
"Ethernet OTA")
1197 ota_layout = QtWidgets.QVBoxLayout(ota_grp)
1199 ota_proj_row = QtWidgets.QHBoxLayout()
1200 ota_proj_row.addWidget(QtWidgets.QLabel(
"PlatformIO project:"))
1203 "Path to PlatformIO project folder"
1209 ota_layout.addLayout(ota_proj_row)
1211 ota_env_row = QtWidgets.QHBoxLayout()
1212 ota_env_row.addWidget(QtWidgets.QLabel(
"Env name:"))
1214 self.
ota_env_edit.setPlaceholderText(
"e.g. adafruit_feather_esp32s3")
1217 ota_layout.addLayout(ota_env_row)
1219 ota_port_row = QtWidgets.QHBoxLayout()
1220 ota_port_row.addWidget(QtWidgets.QLabel(
"OTA TCP port:"))
1225 ota_layout.addLayout(ota_port_row)
1227 ota_board_id_row = QtWidgets.QHBoxLayout()
1228 ota_board_id_row.addWidget(QtWidgets.QLabel(
"Board ID for OTA:"))
1233 "Board ID to compile into firmware via BOARD_ID.\n"
1234 "0 = use default ID from platformio.ini (no override)."
1237 ota_layout.addLayout(ota_board_id_row)
1241 "Compile selected PlatformIO project and upload firmware via Ethernet OTA to the Target IP."
1247 self.
ota_log.setReadOnly(
True)
1248 self.
ota_log.setFixedHeight(130)
1249 self.
ota_log.setPlaceholderText(
"OTA output will appear here…")
1252 " background: #1a1a1a; color: #c8c8c8;"
1253 " font-family: monospace; font-size: 11px;"
1254 " border: 1px solid #333;"
1257 ota_layout.addWidget(self.
ota_log)
1259 right_column_layout.addWidget(ota_grp)
1261 leaderboard_grp = QtWidgets.QGroupBox(
"Max force")
1262 leaderboard_grp.setStyleSheet(
1263 "QGroupBox { font-size: 10px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px 2px 4px; }"
1265 leaderboard_grp.setSizePolicy(
1266 QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Maximum
1268 leaderboard_layout = QtWidgets.QVBoxLayout(leaderboard_grp)
1269 leaderboard_layout.setContentsMargins(6, 4, 6, 4)
1270 leaderboard_layout.setSpacing(2)
1271 leaderboard_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
1272 lb_row = QtWidgets.QHBoxLayout()
1280 "Add max force in current time window to leaderboard."
1284 leaderboard_layout.addLayout(lb_row)
1289 right_column_layout.addWidget(leaderboard_grp)
1290 right_column_layout.addStretch()
1292 content.addWidget(right_column)
1293 layout.addLayout(content)
1298 "Packets: 0 | Pkts/s: 0 | Bytes: 0 | B/s: 0"
1314 self.
receiver.sensor_data_packet_status.connect(