#include "cmd.h" #include "waveform.h" #include "debug.h" #include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #define TAG "cmd" static const char *wave_name(wave_type_t t) { switch (t) { case WAVE_SINE: return "sine"; case WAVE_SQUARE: return "square"; case WAVE_SAW: return "saw"; default: return "off"; } } static void print_help(void) { printf("\n" "Signal Generator Commands:\n" " sine -f [-c ] sine wave\n" " square -f [-d ] [-c ] square wave\n" " saw -f [-c ] triangle wave\n" " off [-c ] silence\n" "\n" " -f frequency (float ok)\n" " -d <0-100> duty cycle %% (square only, default 50)\n" " -c 0/l = left, 1/r = right, omit = both\n" "\n"); } typedef struct { wave_type_t type; float freq; float duty; int ch; /* 0=left, 1=right, -1=both */ bool valid; } parsed_cmd_t; static void str_lower(char *s) { for (; *s; s++) *s = tolower((unsigned char)*s); } static parsed_cmd_t parse_line(char *line) { parsed_cmd_t cmd = { .type = WAVE_NONE, .freq = 440.0f, .duty = 0.5f, .ch = -1, .valid = false, }; /* strip newline */ char *nl = strchr(line, '\n'); if (nl) *nl = '\0'; nl = strchr(line, '\r'); if (nl) *nl = '\0'; if (line[0] == '\0') return cmd; str_lower(line); char *tok = strtok(line, " \t"); if (!tok) return cmd; if (strcmp(tok, "sine") == 0) cmd.type = WAVE_SINE; else if (strcmp(tok, "square") == 0) cmd.type = WAVE_SQUARE; else if (strcmp(tok, "saw") == 0) cmd.type = WAVE_SAW; else if (strcmp(tok, "off") == 0) cmd.type = WAVE_NONE; else return cmd; bool need_freq = (cmd.type != WAVE_NONE); bool got_freq = false; while ((tok = strtok(NULL, " \t")) != NULL) { if (strcmp(tok, "-f") == 0) { tok = strtok(NULL, " \t"); if (!tok) return cmd; cmd.freq = strtof(tok, NULL); if (cmd.freq <= 0.0f || cmd.freq > 24000.0f) { printf("frequency out of range (0 < f <= 24000)\n"); return cmd; } got_freq = true; } else if (strcmp(tok, "-d") == 0) { tok = strtok(NULL, " \t"); if (!tok) return cmd; float d = strtof(tok, NULL); if (d < 0.0f || d > 100.0f) { printf("duty must be 0-100\n"); return cmd; } cmd.duty = d / 100.0f; } else if (strcmp(tok, "-c") == 0) { tok = strtok(NULL, " \t"); if (!tok) return cmd; if (strcmp(tok, "0") == 0 || strcmp(tok, "l") == 0) cmd.ch = 0; else if (strcmp(tok, "1") == 0 || strcmp(tok, "r") == 0) cmd.ch = 1; else { printf("channel: 0/l (left) or 1/r (right)\n"); return cmd; } } } if (need_freq && !got_freq) { printf("-f required\n"); return cmd; } cmd.valid = true; return cmd; } static void apply_cmd(const parsed_cmd_t *cmd) { const char *ch_str; if (cmd->ch == 0) ch_str = "L"; else if (cmd->ch == 1) ch_str = "R"; else ch_str = "L+R"; if (cmd->ch < 0) { waveform_set(0, cmd->type, cmd->freq, cmd->duty); waveform_set(1, cmd->type, cmd->freq, cmd->duty); } else { waveform_set(cmd->ch, cmd->type, cmd->freq, cmd->duty); } if (cmd->type == WAVE_NONE) { ESP_LOGI(TAG, "%s: off", ch_str); } else if (cmd->type == WAVE_SQUARE) { ESP_LOGI(TAG, "%s: %s %.1f Hz duty %.0f%%", ch_str, wave_name(cmd->type), cmd->freq, cmd->duty * 100.0f); } else { ESP_LOGI(TAG, "%s: %s %.1f Hz", ch_str, wave_name(cmd->type), cmd->freq); } } static void cmd_task(void *arg) { char line[128]; DBG("cmd_task started"); print_help(); printf("> "); fflush(stdout); for (;;) { if (fgets(line, sizeof(line), stdin) == NULL) { vTaskDelay(pdMS_TO_TICKS(100)); continue; } parsed_cmd_t cmd = parse_line(line); if (cmd.valid) { apply_cmd(&cmd); } else if (line[0] != '\0' && line[0] != '\n' && line[0] != '\r') { print_help(); } printf("> "); fflush(stdout); } } void cmd_start_task(void) { xTaskCreatePinnedToCore(cmd_task, "cmd", 4096, NULL, 5, NULL, 0); }