ok, let's stop

This commit is contained in:
2025-12-01 21:46:39 +06:00
parent 63f6bb5b04
commit abe722f88c
2 changed files with 295 additions and 204 deletions

View File

@@ -111,7 +111,8 @@
"string.h": "c", "string.h": "c",
"math.h": "c", "math.h": "c",
"cctype": "c", "cctype": "c",
"stdlib.h": "c" "stdlib.h": "c",
"float.h": "c"
}, },
"cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip" "cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip"
} }

456
main.c
View File

@@ -1,3 +1,17 @@
/*
* HP3478A internal USB-GPIB adapter & extension
*
* DESCRIPTION:
* This firmware acts as a bridge between USB-CDC and GPIB while also working
* in standalone mode inside HP3478A and MITM-ing GPIB and adding features.
*
* TODO:
* - Implement a VTable for features, to replace the massive switch
* to make consistnet entry and exits
* - Break main() up into app_process_usb() and app_process_gpib()?
* - SCPI-compliant command set for the passthrough mode
* - Data logging?
*/
#include <ctype.h> #include <ctype.h>
#include <float.h> #include <float.h>
#include <math.h> #include <math.h>
@@ -35,6 +49,7 @@
#define MENU_COMMIT_DELAY_MS 2400 // Time to hold before entering mode #define MENU_COMMIT_DELAY_MS 2400 // Time to hold before entering mode
#define MENU_SUBLAYER_DELAY_MS 500 // Time to "hover" before dots start #define MENU_SUBLAYER_DELAY_MS 500 // Time to "hover" before dots start
#define MENU_DEBOUNCE_MS 200 // Button press dead-time #define MENU_DEBOUNCE_MS 200 // Button press dead-time
#define MENU_LOCKOUT_MS 1000 // How long to ignore SRQ for after exiting menu
// Polling // Polling
#define POLL_INTERVAL_MS 100 // 10Hz polling when in Passthrough #define POLL_INTERVAL_MS 100 // 10Hz polling when in Passthrough
@@ -51,13 +66,6 @@
#define RTD_B -5.775e-7 #define RTD_B -5.775e-7
#define RTD_R0 1000.0 #define RTD_R0 1000.0
// Thermistor
// TODO: different ranges? 5k, 10k etc.?
// This is a 5k NTC! kinda useless here
#define THERM_A 0.001286
#define THERM_B 0.00023595
#define THERM_C 0.0000000941
// Thermocouple // Thermocouple
#define CJC_FALLBACK_TEMP 22.0 // used if !app.env_sensor_present #define CJC_FALLBACK_TEMP 22.0 // used if !app.env_sensor_present
// this is just cursed but.. yeah, the PCB is near a transformer // this is just cursed but.. yeah, the PCB is near a transformer
@@ -181,6 +189,32 @@ typedef enum {
MENU_MAX_ITEMS MENU_MAX_ITEMS
} menu_item_t; } menu_item_t;
typedef enum {
// Generic
NTC_10K_3950 = 0, // Generic china 3950
NTC_10K_3435, // alt 10k
NTC_50K_3950, // alt 50k
NTC_100K_3950, // alt 100k
// YSI 44000
NTC_YSI_2252, // YSI 44004 (Mix B)
NTC_YSI_3K, // YSI 44005 (Mix B)
NTC_YSI_5K, // YSI 44007 (Mix B)
NTC_YSI_10K_A, // YSI 44006 (Mix H)
NTC_YSI_10K_B, // YSI 44016 (Mix B)
NTC_YSI_30K, // YSI 44008 (Mix H)
NTC_YSI_100K, // YSI 44011 (Mix H)
NTC_YSI_1MEG, // YSI 44015 (Mix H)
NTC_MAX_ITEMS
} ntc_preset_t;
typedef struct {
double r0; // R @ 25C
double beta; // beta coefficient (typ. 3000-4500)
const char* name; // display name
} ntc_def_t;
// UI Strings // UI Strings
static const char* MENU_NAMES[] = {"REL", "TEMP", "DBM", "CONT", static const char* MENU_NAMES[] = {"REL", "TEMP", "DBM", "CONT",
"DIODE", "XOHM", "STATS", "EXIT"}; "DIODE", "XOHM", "STATS", "EXIT"};
@@ -189,11 +223,30 @@ static const char* SENSOR_NAMES[] = {"PT1000", "THERM", "TYPE K"};
static const char* WIRE_NAMES[] = {"2-WIRE", "4-WIRE"}; static const char* WIRE_NAMES[] = {"2-WIRE", "4-WIRE"};
// some common NTC defs
static const ntc_def_t NTC_DEFS[] = {
// Generic/China
{10000.0, 3950.0, "10K 3950"}, // black bead)
{10000.0, 3435.0, "10K 3435"}, // euro?
{50000.0, 3950.0, "50K 3950"}, // generic
{100000.0, 3950.0, "100K 3950"}, // generic
// From YSI datasheet
{2252.0, 3891.0, "2.252K YSI"}, // 44004
{3000.0, 3891.0, "3K YSI"}, // 44005
{5000.0, 3891.0, "5K YSI"}, // 44007
{10000.0, 3574.0, "10K YSI A"}, // Mix H (YSI 10k)
{10000.0, 3891.0, "10K YSI B"}, // Mix B (matches 2.2K curve)
{30000.0, 3810.0, "30K YSI"}, // 44008
{100000.0, 3988.0, "100K YSI"}, // 44011
{1000000.0, 4582.0, "1MEG YSI"} // 44015
};
// Sub-menu states // Sub-menu states
typedef enum { typedef enum {
SUBMENU_NONE = 0, SUBMENU_NONE = 0,
SUBMENU_TEMP_SENS, // step 1: sensor Type SUBMENU_TEMP_SENS, // step 1: sensor Type
SUBMENU_TEMP_WIRE // step 2: wire mode (skipped for Type K) SUBMENU_TEMP_WIRE, // step 2a: wire mode (skipped for Type K)
SUBMENU_TEMP_NTC // step 2b: NTC Value (Thermistor only)
} submenu_state_t; } submenu_state_t;
// Sensor Types // Sensor Types
@@ -209,7 +262,6 @@ typedef enum {
DIODE_STATE_OPEN = 0, // probes open DIODE_STATE_OPEN = 0, // probes open
DIODE_STATE_CHECKING, // voltage @ diode range, checking stability DIODE_STATE_CHECKING, // voltage @ diode range, checking stability
DIODE_STATE_SHORT, // voltage too low, silent DIODE_STATE_SHORT, // voltage too low, silent
DIODE_STATE_PLAYING, // is a diode, chirping
DIODE_STATE_DONE // chirped, latched silent DIODE_STATE_DONE // chirped, latched silent
} diode_state_t; } diode_state_t;
@@ -271,7 +323,8 @@ typedef struct {
uint8_t auto_read : 1; uint8_t auto_read : 1;
uint8_t dmm_online : 1; uint8_t dmm_online : 1;
uint8_t has_saved_state : 1; uint8_t has_saved_state : 1;
uint8_t reserved : 2; uint8_t beep_active : 1;
uint8_t reserved : 1;
// Addresses // Addresses
uint8_t target_addr; uint8_t target_addr;
@@ -283,6 +336,9 @@ typedef struct {
uint32_t env_last_read; uint32_t env_last_read;
uint32_t last_poll_time; uint32_t last_poll_time;
uint32_t poll_interval; uint32_t poll_interval;
uint32_t ignore_input_start_ts;
uint32_t beep_start_ts; // buzzer
uint32_t beep_duration;
// Logic // Logic
work_mode_t current_mode; work_mode_t current_mode;
@@ -291,6 +347,7 @@ typedef struct {
// Config Selection // Config Selection
temp_sensor_t temp_sensor; temp_sensor_t temp_sensor;
wire_mode_t temp_wire_mode; wire_mode_t temp_wire_mode;
ntc_preset_t temp_ntc_preset;
// Environmental Data // Environmental Data
aht20_data current_env; aht20_data current_env;
@@ -298,7 +355,7 @@ typedef struct {
// DMM Restore // DMM Restore
uint8_t saved_state_bytes[5]; uint8_t saved_state_bytes[5];
// Display shadow buffer // Display shadow buffer (cache)
char last_disp_sent[HP_DISP_BUF_SIZE]; char last_disp_sent[HP_DISP_BUF_SIZE];
// Unionized Mode Data // Unionized Mode Data
@@ -310,19 +367,28 @@ static app_state_t app = {.dmm_addr = DEFAULT_DMM_ADDR,
.poll_interval = POLL_INTERVAL_MS, .poll_interval = POLL_INTERVAL_MS,
.gpib_timeout_ms = DEFAULT_GPIB_TIMEOUT_MS}; .gpib_timeout_ms = DEFAULT_GPIB_TIMEOUT_MS};
// Buffers typedef union {
// TODO: buffer usage is very inconsistent // command processing
// cmd and resp can probably be a union, and what do we even use tmp_buffer struct {
// for?.. char line_buf[128];
static char cmd_buffer[128]; char fmt_buf[128];
static char resp_buffer[256]; } cmd;
static char tmp_buffer[128];
// Max theoretic bytes for 12 visual chars is 24 // general io ctx
static char disp_buffer[HP_DISP_BUF_SIZE]; struct {
// Display Command Buffer // for gpib
// Size: "D3"(2) + Text(HP_DISP_BUF_SIZE) + "\n"(1) + Null(1) char raw_data[256];
// "D3" + "1.2....2." + "\n" + \0 } io;
static char disp_cmd_buffer[2 + HP_DISP_BUF_SIZE + 2];
// display ctx
struct {
char full_cmd[64];
} disp;
uint8_t raw[256];
} app_scratchpad_t;
static app_scratchpad_t scratch;
// USB Ring Buffer // USB Ring Buffer
#define USB_RX_BUF_SIZE 512 #define USB_RX_BUF_SIZE 512
@@ -385,9 +451,9 @@ void double_to_str(char* buf, size_t buf_size, double val, int prec) {
val = -val; val = -val;
} }
// 3. Multiplier (Integer Powers of 10) // multiplier
if (prec < 0) prec = 0; if (prec < 0) prec = 0;
if (prec > 9) prec = 9; // Limit precision to prevent overflow if (prec > 9) prec = 9; // limit precision
unsigned long long multiplier = 1; unsigned long long multiplier = 1;
for (int i = 0; i < prec; i++) multiplier *= 10; for (int i = 0; i < prec; i++) multiplier *= 10;
@@ -395,7 +461,7 @@ void double_to_str(char* buf, size_t buf_size, double val, int prec) {
// scale and round // scale and round
val = (val * (double)multiplier) + 0.5; val = (val * (double)multiplier) + 0.5;
// 5. split integer and fractional parts // split integer and fractional parts
unsigned long long full_scaled = (unsigned long long)val; unsigned long long full_scaled = (unsigned long long)val;
unsigned long int_part = (unsigned long)(full_scaled / multiplier); unsigned long int_part = (unsigned long)(full_scaled / multiplier);
unsigned long long frac_part = full_scaled % multiplier; unsigned long long frac_part = full_scaled % multiplier;
@@ -1271,7 +1337,6 @@ void TIM2_IRQHandler(void) {
} }
} }
// TODO: maybne don't sleep inside it
void tone(unsigned int freq_hz, unsigned int duration_ms) { void tone(unsigned int freq_hz, unsigned int duration_ms) {
if (freq_hz == 0) { if (freq_hz == 0) {
Delay_Ms(duration_ms); Delay_Ms(duration_ms);
@@ -1283,29 +1348,22 @@ void tone(unsigned int freq_hz, unsigned int duration_ms) {
buzzer_set(0); buzzer_set(0);
} }
void play_startup_tune() { void tone_nb(uint16_t freq, uint32_t duration_ms) {
buzzer_set(freq);
app.beep_start_ts = millis();
app.beep_duration = duration_ms;
app.beep_active = 1;
}
// "Boot Up" // "Boot Up"
void play_startup_tune() {
tone(1500, 100); tone(1500, 100);
Delay_Ms(20); Delay_Ms(25);
tone(2500, 100); tone(2500, 100);
Delay_Ms(20); Delay_Ms(25);
tone(4000, 100); tone(4000, 100);
} }
void play_connected_tune() {
// "Device Attached"
tone(3000, 100);
tone(4000, 100);
}
void play_disconnected_tune() {
// "Device Removed"
tone(4000, 100);
tone(3000, 100);
}
void beep(int ms) { tone(2500, ms); }
// ------------------------------------ // ------------------------------------
int HandleSetupCustom(struct _USBState* ctx, int setup_code) { int HandleSetupCustom(struct _USBState* ctx, int setup_code) {
@@ -1437,12 +1495,7 @@ static void handle_usb_state(void) {
if (app.usb_online != raw_status) { if (app.usb_online != raw_status) {
app.usb_online = raw_status; app.usb_online = raw_status;
if (app.usb_online) { if (app.usb_online) usb_rx_tail = usb_rx_head = 0;
usb_rx_tail = usb_rx_head = 0;
play_connected_tune();
} else {
play_disconnected_tune();
}
} }
} }
} }
@@ -1472,12 +1525,12 @@ void dmm_display(const char* text, const char* mode) {
// update shadow buf // update shadow buf
strncpy(app.last_disp_sent, text, HP_DISP_BUF_SIZE - 1); strncpy(app.last_disp_sent, text, HP_DISP_BUF_SIZE - 1);
app.last_disp_sent[HP_DISP_BUF_SIZE - 1] = '\0';
// "D[23]" + Text + "\n" // "D[23]" + Text + "\n"
snprintf(disp_cmd_buffer, sizeof(disp_cmd_buffer), "%s%s\n", mode, snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd), "%s%s\n", mode,
app.last_disp_sent); app.last_disp_sent);
// send it // send it
gpib_send(app.dmm_addr, disp_cmd_buffer); gpib_send(app.dmm_addr, scratch.disp.full_cmd);
} }
static inline void dmm_display_normal(void) { static inline void dmm_display_normal(void) {
gpib_send(app.dmm_addr, HP3478A_DISP_NORMAL); gpib_send(app.dmm_addr, HP3478A_DISP_NORMAL);
@@ -1507,33 +1560,34 @@ void restore_dmm_state(void) {
decode_dmm_state_bytes(app.saved_state_bytes, &state); decode_dmm_state_bytes(app.saved_state_bytes, &state);
// "D1 Fx Rx Nx Zx" + "T1 M20" // "D1 Fx Rx Nx Zx" + "T1 M20"
build_restoration_string(cmd_buffer, &state); build_restoration_string(scratch.cmd.line_buf, &state);
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_ONLY); strcat(scratch.cmd.line_buf, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_ONLY);
gpib_send(app.dmm_addr, cmd_buffer); gpib_send(app.dmm_addr, scratch.cmd.line_buf);
} }
void exit_to_passthrough(void) { void exit_to_passthrough(void) {
buzzer_set(0); // mute buzzer_set(0);
restore_dmm_state(); restore_dmm_state();
gpib_go_to_local(app.dmm_addr); gpib_go_to_local(app.dmm_addr);
app.current_mode = MODE_PASSTHROUGH; app.current_mode = MODE_PASSTHROUGH;
app.data.menu.layer = SUBMENU_NONE; app.data.menu.layer = SUBMENU_NONE;
app.menu_pos = 0; app.menu_pos = 0;
app.last_poll_time = millis(); app.beep_active = 0;
Delay_Ms(MENU_DEBOUNCE_MS); uint32_t now = millis();
app.last_poll_time = now;
app.ignore_input_start_ts = now;
} }
void enter_feature_mode(menu_item_t item) { void enter_feature_mode(menu_item_t item) {
// force display refresh // force display refresh
app.last_disp_sent[0] = '\0'; app.last_disp_sent[0] = '\0';
// clean buffer for cmds // clean buffer for cmds
cmd_buffer[0] = '\0'; scratch.cmd.line_buf[0] = '\0';
gpib_remote_enable(1); // assert REN gpib_remote_enable(1); // make sure REN is asserted
Delay_Ms(20);
// we will hold the decoded state here for REL/STATS // we will hold the decoded state here for REL/STATS
dmm_decoded_state_t saved_cfg; dmm_decoded_state_t saved_cfg;
@@ -1542,8 +1596,9 @@ void enter_feature_mode(menu_item_t item) {
case MENU_REL: case MENU_REL:
if (app.has_saved_state) { if (app.has_saved_state) {
decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg); decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg);
build_restoration_string(cmd_buffer, &saved_cfg); build_restoration_string(scratch.cmd.line_buf, &saved_cfg);
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA strcat(scratch.cmd.line_buf,
HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
HP3478A_CMD_SRQ_CLEAR); HP3478A_CMD_SRQ_CLEAR);
strcpy(app.data.rel.unit, saved_cfg.unit_str); strcpy(app.data.rel.unit, saved_cfg.unit_str);
} else { } else {
@@ -1551,7 +1606,7 @@ void enter_feature_mode(menu_item_t item) {
dmm_display("ERR NO STATE", HP3478A_DISP_TEXT_FAST); dmm_display("ERR NO STATE", HP3478A_DISP_TEXT_FAST);
} }
gpib_send(app.dmm_addr, cmd_buffer); gpib_send(app.dmm_addr, scratch.cmd.line_buf);
app.current_mode = MODE_FEAT_REL; app.current_mode = MODE_FEAT_REL;
app.data.rel.offset = 0.0; app.data.rel.offset = 0.0;
@@ -1579,8 +1634,20 @@ void enter_feature_mode(menu_item_t item) {
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR); HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
dmm_display("TEMP TYPE-K", HP3478A_DISP_TEXT_FAST); dmm_display("TEMP TYPE-K", HP3478A_DISP_TEXT_FAST);
} else if (app.temp_sensor == SENS_THERMISTOR) {
// Thermistor: 2-Wire Ohms (Usually standard for NTC)
// Range Auto (NTC varies wildy)
gpib_send(app.dmm_addr,
HP3478A_FUNC_OHMS_2WIRE HP3478A_RANGE_AUTO HP3478A_DIGITS_5_5
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
char buf[13];
snprintf(buf, sizeof(buf), "NTC %s",
NTC_DEFS[app.temp_ntc_preset].name);
dmm_display(buf, HP3478A_DISP_TEXT_FAST);
} else { } else {
// Resistor Setup (PT1000/Therm): // Resistor Setup (PT1000):
// F3=2W / F4=4W // F3=2W / F4=4W
// R4=30kOhm Range // R4=30kOhm Range
if (app.temp_wire_mode == WIRE_2W) { if (app.temp_wire_mode == WIRE_2W) {
@@ -1597,8 +1664,8 @@ void enter_feature_mode(menu_item_t item) {
if (app.temp_sensor == SENS_PT1000) if (app.temp_sensor == SENS_PT1000)
dmm_display("TEMP PT1000", HP3478A_DISP_TEXT_FAST); dmm_display("TEMP PT1000", HP3478A_DISP_TEXT_FAST);
else // else
dmm_display("TEMP THERM", HP3478A_DISP_TEXT_FAST); // dmm_display("TEMP THERM", HP3478A_DISP_TEXT_FAST);
} }
break; break;
@@ -1645,20 +1712,21 @@ void enter_feature_mode(menu_item_t item) {
case MENU_STATS: case MENU_STATS:
if (app.has_saved_state) { if (app.has_saved_state) {
decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg); decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg);
build_restoration_string(cmd_buffer, &saved_cfg); build_restoration_string(scratch.cmd.line_buf, &saved_cfg);
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA strcat(scratch.cmd.line_buf,
HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
HP3478A_CMD_SRQ_CLEAR); HP3478A_CMD_SRQ_CLEAR);
strcpy(app.data.stats.unit, saved_cfg.unit_str); strcpy(app.data.stats.unit, saved_cfg.unit_str);
} else { } else {
// fallback // fallback
strcpy(cmd_buffer, strcpy(scratch.cmd.line_buf,
HP3478A_FUNC_DC_VOLTS HP3478A_RANGE_AUTO HP3478A_DIGITS_5_5 HP3478A_FUNC_DC_VOLTS HP3478A_RANGE_AUTO HP3478A_DIGITS_5_5
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR); HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
strcpy(app.data.stats.unit, "VDC"); strcpy(app.data.stats.unit, "VDC");
} }
gpib_send(app.dmm_addr, cmd_buffer); gpib_send(app.dmm_addr, scratch.cmd.line_buf);
app.current_mode = MODE_FEAT_STATS; app.current_mode = MODE_FEAT_STATS;
@@ -1692,7 +1760,6 @@ void enter_menu_mode(void) {
dmm_display("M: REL", HP3478A_DISP_TEXT_FAST); dmm_display("M: REL", HP3478A_DISP_TEXT_FAST);
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR); gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
Delay_Ms(200);
} }
void handle_feature_logic(void) { void handle_feature_logic(void) {
@@ -1709,7 +1776,8 @@ void handle_feature_logic(void) {
if (!(stb & HP3478A_MASK_DATA_READY)) return; if (!(stb & HP3478A_MASK_DATA_READY)) return;
// read measurement // read measurement
int len = gpib_receive(app.dmm_addr, resp_buffer, sizeof(resp_buffer)); int len = gpib_receive(app.dmm_addr, scratch.io.raw_data,
sizeof(scratch.io.raw_data));
if (len < 0) { if (len < 0) {
// timeout or error // timeout or error
// printf("Read Timeout in Feature\n"); // printf("Read Timeout in Feature\n");
@@ -1719,7 +1787,7 @@ void handle_feature_logic(void) {
return; return;
} }
double val = parse_double(resp_buffer); double val = parse_double(scratch.io.raw_data);
// overload (HP 3478A sends +9.99990E+9 for OL) // overload (HP 3478A sends +9.99990E+9 for OL)
int is_overload = 0; int is_overload = 0;
if (val < DMM_OL_NEG_THRESHOLD || val > DMM_OL_THRESHOLD) is_overload = 1; if (val < DMM_OL_NEG_THRESHOLD || val > DMM_OL_THRESHOLD) is_overload = 1;
@@ -1738,7 +1806,7 @@ void handle_feature_logic(void) {
if (app.data.rel.stable_count >= REL_STABLE_SAMPLES) { if (app.data.rel.stable_count >= REL_STABLE_SAMPLES) {
app.data.rel.offset = val; app.data.rel.offset = val;
dmm_display("NULL SET", HP3478A_DISP_TEXT_FAST); dmm_display("NULL SET", HP3478A_DISP_TEXT_FAST);
tone(3000, 50); tone_nb(3000, 50);
app.data.rel.stable_count = 0; app.data.rel.stable_count = 0;
} else { } else {
dmm_display("LOCKING...", HP3478A_DISP_TEXT_FAST); dmm_display("LOCKING...", HP3478A_DISP_TEXT_FAST);
@@ -1754,9 +1822,9 @@ void handle_feature_logic(void) {
// prepend 'D' // prepend 'D'
snprintf(eff_unit, sizeof(eff_unit), "D%s", app.data.rel.unit); snprintf(eff_unit, sizeof(eff_unit), "D%s", app.data.rel.unit);
format_metric_value(disp_buffer, sizeof(disp_buffer), diff, eff_unit, format_metric_value(scratch.disp.full_cmd,
1); sizeof(scratch.disp.full_cmd), diff, eff_unit, 1);
dmm_display(disp_buffer, HP3478A_DISP_TEXT); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
} }
@@ -1766,20 +1834,21 @@ void handle_feature_logic(void) {
dmm_display("O.VLD", HP3478A_DISP_TEXT_FAST); dmm_display("O.VLD", HP3478A_DISP_TEXT_FAST);
} else { } else {
// P(mW) = (V^2 / 50) * 1000 = V^2 * 20 // P(mW) = (V^2 / 50) * 1000 = V^2 * 20
double p_mw = (val * val * 20.0f); double p_mw = (val * val * 20.0);
if (p_mw < 1e-9f) { if (p_mw < 1e-9) {
// Align -INF to look consistent // Align -INF to look consistent
// Display: "-INF DBM" // Display: "-INF DBM"
memset(disp_buffer, ' ', HP_DISP_LEN); memset(scratch.disp.full_cmd, ' ', HP_DISP_LEN);
disp_buffer[HP_DISP_LEN] = '\0'; scratch.disp.full_cmd[HP_DISP_LEN] = '\0';
memcpy(disp_buffer, "-INF", 4); memcpy(scratch.disp.full_cmd, "-INF", 4);
memcpy(&disp_buffer[8], " DBM", 4); memcpy(&scratch.disp.full_cmd[8], " DBM", 4);
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
} else { } else {
double dbm = 10.0 * log10(p_mw); double dbm = 10.0 * log10(p_mw);
format_metric_value(disp_buffer, sizeof(disp_buffer), dbm, "DBM", 00); format_metric_value(scratch.disp.full_cmd,
dmm_display(disp_buffer, HP3478A_DISP_TEXT); sizeof(scratch.disp.full_cmd), dbm, "DBM", 00);
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
} }
@@ -1791,7 +1860,7 @@ void handle_feature_logic(void) {
double temp_c = 0.0; double temp_c = 0.0;
const char* unit_str = " C"; const char* unit_str = " C";
// 1. TYPE K THERMOCOUPLE // TYPE K THERMOCOUPLE
if (app.temp_sensor == SENS_TYPE_K) { if (app.temp_sensor == SENS_TYPE_K) {
// 30mV range safety check (Floating input > 50mV) // 30mV range safety check (Floating input > 50mV)
if (fabs(val) > 0.050) { if (fabs(val) > 0.050) {
@@ -1817,9 +1886,9 @@ void handle_feature_logic(void) {
temp_c = t_amb + (val * TYPE_K_SCALE); temp_c = t_amb + (val * TYPE_K_SCALE);
} }
} }
// 2. PT1000 RTD (Callendar-Van Dusen) // PT1000 RTD (Callendar-Van Dusen)
else if (app.temp_sensor == SENS_PT1000) { else if (app.temp_sensor == SENS_PT1000) {
if (val < 10.0f) { if (val < 10.0) {
dmm_display("SHORT", HP3478A_DISP_TEXT_FAST); dmm_display("SHORT", HP3478A_DISP_TEXT_FAST);
return; return;
} else { } else {
@@ -1829,32 +1898,44 @@ void handle_feature_logic(void) {
double disc = (b * b) - (4 * a * c); double disc = (b * b) - (4 * a * c);
if (disc >= 0) if (disc >= 0)
temp_c = (-b + sqrtf(disc)) / (2 * a); temp_c = (-b + sqrt(disc)) / (2 * a);
else { else {
dmm_display("RANGE ERR", HP3478A_DISP_TEXT_FAST); dmm_display("RANGE ERR", HP3478A_DISP_TEXT_FAST);
return; return;
} }
} }
} }
// 3. THERMISTOR (Steinhart-Hart) // THERMISTOR (simplified Steinhart-Hart)
else { else {
if (val < 10.0f) { if (val < 10.0) {
dmm_display("SHORT", HP3478A_DISP_TEXT_FAST); dmm_display("SHORT", HP3478A_DISP_TEXT_FAST);
return; return;
} else { }
// log(0) ? :) if (val > 4000000.0) {
double r = (val < 1.0f) ? 1.0f : val; dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
double lr = log(r); return;
double lr3 = lr * lr * lr; }
double inv_t = THERM_A + (THERM_B * lr) + (THERM_C * lr3);
temp_c = (1.0f / inv_t) - 273.15f; double r_meas = val;
} // constants from our preset
double r0 = NTC_DEFS[app.temp_ntc_preset].r0;
double beta = NTC_DEFS[app.temp_ntc_preset].beta;
const double t0_k = 298.15; // 25.0C in Kelvin
// 1/T = 1/T0 + (1/B * ln(R/R0))
double ln_ratio = log(r_meas / r0);
double inv_t = (1.0 / t0_k) + ((1.0 / beta) * ln_ratio);
// convert Kelvin to Celsius
temp_c = (1.0 / inv_t) - 273.15;
unit_str = "C NTC";
} }
// Display: "24.5 C" (or "24.5 C (K)") // Display: "24.5 C" (or "24.5 C (K)")
format_metric_value(disp_buffer, sizeof(disp_buffer), temp_c, unit_str, format_metric_value(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
0); temp_c, unit_str, 0);
dmm_display(disp_buffer, HP3478A_DISP_TEXT); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
// CONT MODE // CONT MODE
@@ -1871,27 +1952,28 @@ void handle_feature_logic(void) {
if (is_overload) { if (is_overload) {
dmm_display("OPEN", HP3478A_DISP_TEXT_FAST); dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
} else { } else {
format_metric_value(disp_buffer, sizeof(disp_buffer), val, "OHM", 1); format_metric_value(scratch.disp.full_cmd,
dmm_display(disp_buffer, HP3478A_DISP_TEXT); sizeof(scratch.disp.full_cmd), val, "OHM", 1);
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
} }
else if (app.current_mode == MODE_FEAT_DIODE) { else if (app.current_mode == MODE_FEAT_DIODE) {
uint32_t now = millis(); uint32_t now = millis();
float voltage = is_overload ? 9.9f : (val / 1000.0f); double voltage = is_overload ? 9.9 : (val / 1000.0);
uint8_t is_valid_signal = uint8_t is_valid_signal =
(voltage > DIODE_TH_SHORT && voltage < DIODE_TH_OPEN); (voltage > DIODE_TH_SHORT && voltage < DIODE_TH_OPEN);
if (voltage < DIODE_TH_OPEN) { if (voltage < DIODE_TH_OPEN) {
format_metric_value(disp_buffer, sizeof(disp_buffer), voltage, "VDC", 1); format_metric_value(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
dmm_display(disp_buffer, HP3478A_DISP_TEXT); voltage, "VDC", 1);
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} else { } else {
dmm_display("OPEN", HP3478A_DISP_TEXT); dmm_display("OPEN", HP3478A_DISP_TEXT);
} }
diode_state_t next_state = app.data.diode.connected; diode_state_t next_state = app.data.diode.connected;
uint8_t request_buzzer = 0;
switch (app.data.diode.connected) { switch (app.data.diode.connected) {
case DIODE_STATE_OPEN: case DIODE_STATE_OPEN:
@@ -1910,15 +1992,8 @@ void handle_feature_logic(void) {
if (!is_valid_signal) { if (!is_valid_signal) {
next_state = DIODE_STATE_SHORT; // signal lost/glitch next_state = DIODE_STATE_SHORT; // signal lost/glitch
} else if ((now - app.data.diode.chirp_start) >= DIODE_STABLE_MS) { } else if ((now - app.data.diode.chirp_start) >= DIODE_STABLE_MS) {
next_state = DIODE_STATE_PLAYING; // has been stable long enough
app.data.diode.chirp_start = now; tone_nb(2500, DIODE_CHIRP_MS);
request_buzzer = 1;
}
break;
case DIODE_STATE_PLAYING:
request_buzzer = 1;
if ((now - app.data.diode.chirp_start) >= DIODE_CHIRP_MS) {
next_state = DIODE_STATE_DONE; next_state = DIODE_STATE_DONE;
} }
break; break;
@@ -1936,7 +2011,6 @@ void handle_feature_logic(void) {
} }
app.data.diode.connected = next_state; app.data.diode.connected = next_state;
buzzer_set(request_buzzer ? 2500 : 0);
} }
// XOHM MODE // XOHM MODE
@@ -1947,9 +2021,7 @@ void handle_feature_logic(void) {
if (val > 8.0e6 && val < 12.0e6) { if (val > 8.0e6 && val < 12.0e6) {
app.data.xohm.r1 = val; // Store R1 app.data.xohm.r1 = val; // Store R1
app.data.xohm.calibrated = 1; app.data.xohm.calibrated = 1;
tone(3000, 100); tone_nb(3000, 100);
dmm_display("READY", HP3478A_DISP_TEXT_FAST);
Delay_Ms(500);
} else { } else {
dmm_display("OPEN PROBES", HP3478A_DISP_TEXT_FAST); dmm_display("OPEN PROBES", HP3478A_DISP_TEXT_FAST);
} }
@@ -1958,15 +2030,16 @@ void handle_feature_logic(void) {
// R1 = xohm_ref (Internal) // R1 = xohm_ref (Internal)
// R2 = val (Measured Parallel) // R2 = val (Measured Parallel)
else { else {
if (is_overload || val >= (app.data.xohm.r1 - 1000.0f)) { if (is_overload || val >= (app.data.xohm.r1 - 1000.0)) {
dmm_display("OPEN", HP3478A_DISP_TEXT_FAST); dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
} else { } else {
double r1 = app.data.xohm.r1; double r1 = app.data.xohm.r1;
double r2 = val; double r2 = val;
double rx = (r1 * r2) / (r1 - r2); double rx = (r1 * r2) / (r1 - r2);
format_metric_value(disp_buffer, sizeof(disp_buffer), rx, "OHM", 1); format_metric_value(scratch.disp.full_cmd,
dmm_display(disp_buffer, HP3478A_DISP_TEXT); sizeof(scratch.disp.full_cmd), rx, "OHM", 1);
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
} else if (app.current_mode == MODE_FEAT_STATS) { } else if (app.current_mode == MODE_FEAT_STATS) {
@@ -2015,7 +2088,8 @@ void handle_feature_logic(void) {
// render // render
if (app.data.stats.view_mode == 0) { if (app.data.stats.view_mode == 0) {
// live mode // live mode
format_metric_value(disp_buffer, sizeof(disp_buffer), val_to_show, format_metric_value(scratch.disp.full_cmd,
sizeof(scratch.disp.full_cmd), val_to_show,
app.data.stats.unit, 1); app.data.stats.unit, 1);
} else { } else {
// stats mode: prefix (4) + number (8) // stats mode: prefix (4) + number (8)
@@ -2038,17 +2112,18 @@ void handle_feature_logic(void) {
double_to_str(num_buf, sizeof(num_buf), scaled, 4); double_to_str(num_buf, sizeof(num_buf), scaled, 4);
// combine: prefix + number + suffix // combine: prefix + number + suffix
snprintf(disp_buffer, sizeof(disp_buffer), "%s%s%c ", prefix_str, snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
num_buf, suffix ? suffix : ' '); "%s%s%c ", prefix_str, num_buf, suffix ? suffix : ' ');
} }
// send it // send it
dmm_display(disp_buffer, HP3478A_DISP_TEXT); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
} }
} }
} }
// gens the base string (e.g., "M: TEMP", "S: TYPE K") into disp_buffer // gens the base string (e.g., "M: TEMP", "S: TYPE K") into
// scratch.disp.full_cmd
void prepare_menu_base_string(void) { void prepare_menu_base_string(void) {
const char* prefix = ""; const char* prefix = "";
const char* name = "???"; const char* name = "???";
@@ -2062,28 +2137,29 @@ void prepare_menu_base_string(void) {
} else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) { } else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) {
prefix = "T: "; prefix = "T: ";
if (app.menu_pos < WIRE_MAX_ITEMS) name = WIRE_NAMES[app.menu_pos]; if (app.menu_pos < WIRE_MAX_ITEMS) name = WIRE_NAMES[app.menu_pos];
} else if (app.data.menu.layer == SUBMENU_TEMP_NTC) {
prefix = "N: ";
if (app.menu_pos < NTC_MAX_ITEMS) name = NTC_DEFS[app.menu_pos].name;
} }
snprintf(disp_buffer, sizeof(disp_buffer), "%s%s", prefix, name); snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd), "%s%s", prefix,
name);
} }
void handle_menu_navigation(void) { void handle_menu_navigation(void) {
uint32_t now = millis(); uint32_t now = millis();
uint32_t elapsed = now - app.data.menu.timer; uint32_t elapsed = now - app.data.menu.timer;
// nav: check SRQ (next item) if (elapsed > MENU_DEBOUNCE_MS) {
// only poll GPIB if physical line is asserted
if (gpib_check_srq()) { if (gpib_check_srq()) {
uint8_t stb = 0; uint8_t stb = 0;
gpib_serial_poll(app.dmm_addr, &stb); gpib_serial_poll(app.dmm_addr, &stb);
// only 4b (front panel button SRQ) // check if it was the front panel btn
if (stb & HP3478A_MASK_KEYBOARD_SRQ) { if (stb & HP3478A_MASK_KEYBOARD_SRQ) {
if (elapsed < MENU_DEBOUNCE_MS) { // reset timer
// we polled, so STB is clear on DMM. Just return. app.data.menu.timer = now;
return;
}
app.data.menu.timer = now; // reset the "hover" timer
app.menu_pos++; app.menu_pos++;
int max_items = 0; int max_items = 0;
@@ -2093,19 +2169,23 @@ void handle_menu_navigation(void) {
max_items = SENS_MAX_ITEMS; max_items = SENS_MAX_ITEMS;
else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) else if (app.data.menu.layer == SUBMENU_TEMP_WIRE)
max_items = WIRE_MAX_ITEMS; max_items = WIRE_MAX_ITEMS;
else if (app.data.menu.layer == SUBMENU_TEMP_NTC)
max_items = NTC_MAX_ITEMS;
if (app.menu_pos >= max_items) app.menu_pos = 0; if (app.menu_pos >= max_items) app.menu_pos = 0;
// update display immediately // update display
prepare_menu_base_string(); prepare_menu_base_string();
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
// re-arm SRQ // re-arm SRQ
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR); gpib_send(app.dmm_addr,
Delay_Ms(MENU_DEBOUNCE_MS); HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
return; return;
} }
} }
}
prepare_menu_base_string(); prepare_menu_base_string();
@@ -2115,9 +2195,9 @@ void handle_menu_navigation(void) {
int dots = dot_time / MENU_DOT_INTERVAL_MS; int dots = dot_time / MENU_DOT_INTERVAL_MS;
if (dots > 3) dots = 3; if (dots > 3) dots = 3;
for (int i = 0; i < dots; i++) strcat(disp_buffer, "."); for (int i = 0; i < dots; i++) strcat(scratch.disp.full_cmd, ".");
} }
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
if (elapsed > MENU_COMMIT_DELAY_MS) { if (elapsed > MENU_COMMIT_DELAY_MS) {
// L0: main menu // L0: main menu
@@ -2129,7 +2209,7 @@ void handle_menu_navigation(void) {
app.data.menu.timer = now; app.data.menu.timer = now;
prepare_menu_base_string(); prepare_menu_base_string();
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
return; return;
} }
// enter standard modes // enter standard modes
@@ -2144,6 +2224,12 @@ void handle_menu_navigation(void) {
if (app.temp_sensor == SENS_TYPE_K) { if (app.temp_sensor == SENS_TYPE_K) {
// Type K is voltage based so skip wire select // Type K is voltage based so skip wire select
enter_feature_mode(MENU_TEMP); enter_feature_mode(MENU_TEMP);
} else if (app.temp_sensor == SENS_THERMISTOR) {
app.data.menu.layer = SUBMENU_TEMP_NTC;
app.menu_pos = 0; // default to 10K
app.data.menu.timer = now;
prepare_menu_base_string();
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
} else { } else {
// wire select (for resistive) // wire select (for resistive)
app.data.menu.layer = SUBMENU_TEMP_WIRE; app.data.menu.layer = SUBMENU_TEMP_WIRE;
@@ -2151,16 +2237,20 @@ void handle_menu_navigation(void) {
app.data.menu.timer = now; app.data.menu.timer = now;
prepare_menu_base_string(); prepare_menu_base_string();
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
} }
return; return;
} }
// L2a: wire select
// L2: wire select
else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) { else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) {
app.temp_wire_mode = (wire_mode_t)app.menu_pos; app.temp_wire_mode = (wire_mode_t)app.menu_pos;
enter_feature_mode(MENU_TEMP); enter_feature_mode(MENU_TEMP);
return; return;
} // L2b: NTC select
else if (app.data.menu.layer == SUBMENU_TEMP_NTC) {
app.temp_ntc_preset = (ntc_preset_t)app.menu_pos;
enter_feature_mode(MENU_TEMP);
return;
} }
} }
} }
@@ -2168,6 +2258,13 @@ void handle_menu_navigation(void) {
void app_loop(void) { void app_loop(void) {
uint32_t now = millis(); uint32_t now = millis();
if (app.beep_active) {
if ((now - app.beep_start_ts) >= app.beep_duration) {
buzzer_set(0);
app.beep_active = 0;
}
}
// Passthrough // Passthrough
if (app.current_mode == MODE_PASSTHROUGH) { if (app.current_mode == MODE_PASSTHROUGH) {
int srq_asserted = gpib_check_srq(); int srq_asserted = gpib_check_srq();
@@ -2202,13 +2299,14 @@ void app_loop(void) {
app.dmm_online = 1; app.dmm_online = 1;
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR); gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
gpib_go_to_local(app.dmm_addr); gpib_go_to_local(app.dmm_addr);
tone(4000, 50); tone_nb(4000, 50);
app.poll_interval = POLL_INTERVAL_MS; // restore fast polling app.poll_interval = POLL_INTERVAL_MS; // restore fast polling
return; return;
} }
// valid and online, check buttons // valid and online, check buttons
if (stb & HP3478A_MASK_KEYBOARD_SRQ) { if (stb & HP3478A_MASK_KEYBOARD_SRQ &&
(now - app.ignore_input_start_ts) > MENU_LOCKOUT_MS) {
enter_menu_mode(); enter_menu_mode();
} }
@@ -2288,7 +2386,7 @@ static void cmd_help(void) {
} }
static void cmd_status(void) { static void cmd_status(void) {
snprintf(tmp_buffer, sizeof(tmp_buffer), snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf),
"Stat:\r\n" "Stat:\r\n"
" Target Addr: %d\r\n" " Target Addr: %d\r\n"
" Internal DMM: %d\r\n" " Internal DMM: %d\r\n"
@@ -2298,15 +2396,15 @@ static void cmd_status(void) {
" FW: " FW_VERSION "\r\n", " FW: " FW_VERSION "\r\n",
app.target_addr, app.dmm_addr, app.gpib_timeout_ms, app.target_addr, app.dmm_addr, app.gpib_timeout_ms,
app.auto_read ? "ON" : "OFF", app.current_mode); app.auto_read ? "ON" : "OFF", app.current_mode);
usb_send_text(tmp_buffer); usb_send_text(scratch.cmd.fmt_buf);
} }
static void process_command(void) { static void process_command(void) {
if (!get_start_command(cmd_buffer, sizeof(cmd_buffer))) { if (!get_start_command(scratch.cmd.line_buf, sizeof(scratch.cmd.line_buf))) {
return; return;
} }
char* p_cmd = skip_spaces(cmd_buffer); char* p_cmd = skip_spaces(scratch.cmd.line_buf);
int is_cpp_cmd = (strncmp(p_cmd, "++", 2) == 0); int is_cpp_cmd = (strncmp(p_cmd, "++", 2) == 0);
int is_query = (strchr(p_cmd, '?') != NULL); int is_query = (strchr(p_cmd, '?') != NULL);
@@ -2333,8 +2431,9 @@ static void process_command(void) {
usb_send_text("ERR: Invalid Addr\r\n"); usb_send_text("ERR: Invalid Addr\r\n");
} else { } else {
// if no arg provided, show current // if no arg provided, show current
snprintf(tmp_buffer, sizeof(tmp_buffer), "%d\r\n", app.target_addr); snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%d\r\n",
usb_send_text(tmp_buffer); app.target_addr);
usb_send_text(scratch.cmd.fmt_buf);
} }
break; break;
@@ -2381,9 +2480,9 @@ static void process_command(void) {
usb_send_text("ERR: Range 1-60000\r\n"); usb_send_text("ERR: Range 1-60000\r\n");
} }
} else { } else {
snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu\r\n", snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%lu\r\n",
app.gpib_timeout_ms); app.gpib_timeout_ms);
usb_send_text(tmp_buffer); usb_send_text(scratch.cmd.fmt_buf);
} }
break; break;
case CMD_TRG: case CMD_TRG:
@@ -2427,8 +2526,9 @@ static void process_command(void) {
uint8_t poll_addr = (*p_args) ? atoi(p_args) : app.target_addr; uint8_t poll_addr = (*p_args) ? atoi(p_args) : app.target_addr;
uint8_t stb; uint8_t stb;
if (gpib_serial_poll(poll_addr, &stb) == 0) { if (gpib_serial_poll(poll_addr, &stb) == 0) {
snprintf(tmp_buffer, sizeof(tmp_buffer), "%d\r\n", stb); snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%d\r\n",
usb_send_text(tmp_buffer); stb);
usb_send_text(scratch.cmd.fmt_buf);
} else } else
usb_send_text("ERR: Bus\r\n"); usb_send_text("ERR: Bus\r\n");
break; break;
@@ -2447,10 +2547,10 @@ static void process_command(void) {
while (p_args[i] != 0 && i < HP_DISP_LEN) { while (p_args[i] != 0 && i < HP_DISP_LEN) {
char c = p_args[i]; char c = p_args[i];
if (c >= 'a' && c <= 'z') c -= 32; if (c >= 'a' && c <= 'z') c -= 32;
disp_buffer[i++] = c; scratch.disp.full_cmd[i++] = c;
} }
disp_buffer[i] = 0; scratch.disp.full_cmd[i] = 0;
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST); dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
usb_send_text("OK\r\n"); usb_send_text("OK\r\n");
break; break;
} }
@@ -2538,9 +2638,10 @@ static void process_command(void) {
return; return;
do_read_operation: { do_read_operation: {
int len = gpib_receive(app.target_addr, resp_buffer, sizeof(resp_buffer)); int len = gpib_receive(app.target_addr, scratch.io.raw_data,
sizeof(scratch.io.raw_data));
if (len > 0) { if (len > 0) {
usb_send_text(resp_buffer); usb_send_text(scratch.io.raw_data);
} else if (is_cpp_cmd || is_query) { } else if (is_cpp_cmd || is_query) {
usb_send_text("ERR: Read Timeout\r\n"); usb_send_text("ERR: Read Timeout\r\n");
} }
@@ -2574,20 +2675,9 @@ int main() {
app.usb_online = 0; app.usb_online = 0;
app.usb_raw_prev = USB_HW_IS_ACTIVE(); app.usb_raw_prev = USB_HW_IS_ACTIVE();
app.usb_ts = millis(); app.usb_ts = millis();
app.last_poll_time = millis(); app.last_poll_time = 0;
app.ignore_input_start_ts = millis() - 2000;
while (1) { app.dmm_online = 0;
uint8_t status_byte;
if (gpib_serial_poll(app.dmm_addr, &status_byte) == 0) {
// printf("Device Found (Stb: 0x%02X)\n", status_byte);
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
gpib_go_to_local(app.dmm_addr);
break;
}
Delay_Ms(100);
}
app.dmm_online = 1;
while (1) { while (1) {
handle_usb_state(); handle_usb_state();