signal-generator/main/cmd.c

183 lines
4.8 KiB
C

#include "cmd.h"
#include "waveform.h"
#include "debug.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#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 <hz> [-c <ch>] sine wave\n"
" square -f <hz> [-d <duty%%>] [-c <ch>] square wave\n"
" saw -f <hz> [-c <ch>] triangle wave\n"
" off [-c <ch>] silence\n"
"\n"
" -f <hz> frequency (float ok)\n"
" -d <0-100> duty cycle %% (square only, default 50)\n"
" -c <ch> 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 <hz> 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);
}