irom app_action_t application_content(const string_t *src, string_t *dst) { const application_function_table_t *tableptr; if((config.status_trigger_io.io >= 0) && (config.status_trigger_io.pin >= 0)) io_write_pin((string_t *)0, config.status_trigger_io.io, config.status_trigger_io.pin, -1); if(parse_string(0, src, dst) != parse_ok) return(app_action_empty); for(tableptr = application_function_table; tableptr->function; tableptr++) if(string_match(dst, tableptr->command1) || string_match(dst, tableptr->command2)) break; if(tableptr->function) { string_clear(dst); return(tableptr->function(src, dst)); } string_cat(dst, ": command unknown\n"); return(app_action_error); }
iram void io_periodic(void) { const io_info_entry_t *info; io_data_entry_t *data; io_config_pin_entry_t *pin_config; io_data_pin_entry_t *pin_data; int io, pin, status_io, status_pin; io_flags_t flags = { .counter_triggered = 0 }; int value; status_io = config.status_trigger.io; status_pin = config.status_trigger.pin; for(io = 0; io < io_id_size; io++) { info = &io_info[io]; data = &io_data[io]; if(!data->detected) continue; if(info->periodic_fn) info->periodic_fn(io, info, data, &flags); for(pin = 0; pin < info->pins; pin++) { pin_config = &config.io_config[io][pin]; pin_data = &data->pin[pin]; switch(pin_config->mode) { case(io_pin_disabled): case(io_pin_input_digital): case(io_pin_counter): case(io_pin_output_digital): case(io_pin_input_analog): case(io_pin_i2c): case(io_pin_uart): case(io_pin_lcd): case(io_pin_error): { break; } case(io_pin_timer): { if((pin_data->direction != io_dir_none) && (pin_data->speed >= 10) && ((pin_data->speed -= 10) <= 0)) { switch(pin_data->direction) { case(io_dir_none): { break; } case(io_dir_up): { info->write_pin_fn((string_t *)0, info, pin_data, pin_config, pin, 1); pin_data->direction = io_dir_down; break; } case(io_dir_down): { info->write_pin_fn((string_t *)0, info, pin_data, pin_config, pin, 0); pin_data->direction = io_dir_up; break; } } if(pin_config->flags.repeat) pin_data->speed = pin_config->speed; else { pin_data->speed = 0; pin_data->direction = io_dir_none; } } break; } case(io_pin_trigger): { if((info->read_pin_fn((string_t *)0, info, pin_data, pin_config, pin, &value) == io_ok) && (value != 0)) { io_trigger_pin((string_t *)0, pin_config->shared.trigger.io.io, pin_config->shared.trigger.io.pin, pin_config->shared.trigger.trigger_mode); info->write_pin_fn((string_t *)0, info, pin_data, pin_config, pin, 0); } break; } case(io_pin_output_analog): { if((pin_config->shared.output_analog.upper_bound > pin_config->shared.output_analog.lower_bound) && (pin_config->speed > 0) && (pin_data->direction != io_dir_none)) io_trigger_pin_x((string_t *)0, info, pin_data, pin_config, pin, (pin_data->direction == io_dir_up) ? io_trigger_up : io_trigger_down); break; } } } } if((flags.counter_triggered) && (status_io >= 0) && (status_pin >= 0)) io_trigger_pin((string_t *)0, status_io, status_pin, io_trigger_on); } /* app commands */ irom app_action_t application_function_io_mode(const string_t *src, string_t *dst) { const io_info_entry_t *info; io_data_entry_t *data; io_config_pin_entry_t *pin_config; io_data_pin_entry_t *pin_data; io_pin_mode_t mode; io_pin_ll_mode_t llmode; int io, pin; if(parse_int(1, src, &io, 0) != parse_ok) { io_config_dump(dst, &config, -1, -1, false); return(app_action_normal); } if((io < 0) || (io >= io_id_size)) { string_format(dst, "invalid io %d\n", io); return(app_action_error); } info = &io_info[io]; data = &io_data[io]; if(!data->detected) { string_format(dst, "io %d not detected\n", io); return(app_action_error); } if(parse_int(2, src, &pin, 0) != parse_ok) { io_config_dump(dst, &config, io, -1, false); return(app_action_normal); } if((pin < 0) || (pin >= info->pins)) { string_cat(dst, "io pin out of range\n"); return(app_action_error); } pin_config = &config.io_config[io][pin]; pin_data = &data->pin[pin]; if(parse_string(3, src, dst) != parse_ok) { string_clear(dst); io_config_dump(dst, &config, io, pin, false); return(app_action_normal); } if((mode = io_mode_from_string(dst)) == io_pin_error) { string_copy(dst, "invalid mode\n"); return(app_action_error); } string_clear(dst); llmode = io_pin_ll_error; switch(mode) { case(io_pin_input_digital): { if(!info->caps.input_digital) { string_cat(dst, "digital input mode invalid for this io\n"); return(app_action_error); } llmode = io_pin_ll_input_digital; break; } case(io_pin_counter): { if(!info->caps.counter) { string_cat(dst, "counter mode invalid for this io\n"); return(app_action_error); } int debounce; if((parse_int(4, src, &debounce, 0) != parse_ok)) { string_cat(dst, "counter: <debounce ms>\n"); return(app_action_error); } pin_config->speed = debounce; llmode = io_pin_ll_counter; break; } case(io_pin_trigger): { int debounce, trigger_io, trigger_pin; io_trigger_t trigger_type; if(!info->caps.counter) { string_cat(dst, "trigger mode invalid for this io\n"); return(app_action_error); } if((parse_int(4, src, &debounce, 0) != parse_ok)) { string_cat(dst, "trigger: <debounce ms> <io> <pin> <action>\n"); return(app_action_error); } if((parse_int(5, src, &trigger_io, 0) != parse_ok)) { string_cat(dst, "trigger: <debounce ms> <io> <pin> <action>\n"); return(app_action_error); } if((parse_int(6, src, &trigger_pin, 0) != parse_ok)) { string_cat(dst, "trigger: <debounce ms> <io> <pin> <action>\n"); return(app_action_error); } if((parse_string(7, src, dst) != parse_ok)) { string_clear(dst); string_cat(dst, "trigger: <debounce ms> <io> <pin> <action>\n"); return(app_action_error); } trigger_type = string_to_trigger_type(dst); if(trigger_type == io_trigger_error) { string_clear(dst); string_cat(dst, "trigger: <io> <pin> <action>\n"); return(app_action_error); } string_clear(dst); pin_config->speed = debounce; pin_config->shared.trigger.io.io = trigger_io; pin_config->shared.trigger.io.pin = trigger_pin; pin_config->shared.trigger.trigger_mode = trigger_type; llmode = io_pin_ll_counter; break; } case(io_pin_output_digital): { if(!info->caps.output_digital) { string_cat(dst, "digital output mode invalid for this io\n"); return(app_action_error); } llmode = io_pin_ll_output_digital; break; } case(io_pin_timer): { io_direction_t direction; int speed; if(!info->caps.output_digital) { string_cat(dst, "timer mode invalid for this io\n"); return(app_action_error); } if(parse_string(4, src, dst) != parse_ok) { string_copy(dst, "timer: <direction>:up/down <speed>:ms\n"); return(app_action_error); } if(string_match(dst, "up")) direction = io_dir_up; else if(string_match(dst, "down")) direction = io_dir_down; else { string_cat(dst, ": timer direction invalid\n"); return(app_action_error); } string_clear(dst); if((parse_int(5, src, &speed, 0) != parse_ok)) { string_copy(dst, "timer: <direction>:up/down <speed>:ms\n"); return(app_action_error); } if(speed < 10) { string_cat(dst, "timer: speed too small: must be >= 10 ms\n"); return(app_action_error); } pin_config->direction = direction; pin_config->speed = speed; llmode = io_pin_ll_output_digital; break; } case(io_pin_input_analog): { if(!info->caps.input_analog) { string_cat(dst, "analog input mode invalid for this io\n"); return(app_action_error); } llmode = io_pin_ll_input_analog; break; } case(io_pin_output_analog): { int lower_bound = 0; int upper_bound = 0; int speed = 0; if(!info->caps.output_analog) { string_cat(dst, "analog output mode invalid for this io\n"); return(app_action_error); } parse_int(4, src, &lower_bound, 0); parse_int(5, src, &upper_bound, 0); parse_int(6, src, &speed, 0); if((lower_bound < 0) || (lower_bound > 65535)) { string_format(dst, "outputa: lower bound out of range: %d\n", lower_bound); return(app_action_error); } if(upper_bound == 0) upper_bound = lower_bound; if((upper_bound < 0) || (upper_bound > 65535)) { string_format(dst, "outputa: upper bound out of range: %d\n", upper_bound); return(app_action_error); } if(upper_bound < lower_bound) { string_cat(dst, "upper bound below lower bound\n"); return(app_action_error); } if((speed < 0) || (speed > 65535)) { string_format(dst, "outputa: speed out of range: %d\n", speed); return(app_action_error); } pin_config->shared.output_analog.lower_bound = lower_bound; pin_config->shared.output_analog.upper_bound = upper_bound; pin_config->speed = speed; llmode = io_pin_ll_output_analog; break; } case(io_pin_i2c): { io_i2c_t pin_mode; if(!info->caps.i2c) { string_cat(dst, "i2c mode invalid for this io\n"); return(app_action_error); } if(parse_string(4, src, dst) != parse_ok) { string_copy(dst, "i2c: <pin mode>=sda|scl\n"); return(app_action_error); } if((pin_mode = io_i2c_pin_from_string(dst)) == io_i2c_error) { string_copy(dst, "i2c: <pin mode>=sda|scl\n"); return(app_action_error); } string_clear(dst); pin_config->shared.i2c.pin_mode = pin_mode; llmode = io_pin_ll_i2c; break; } case(io_pin_uart): { if(!info->caps.uart) { string_cat(dst, "uart mode invalid for this io\n"); return(app_action_error); } llmode = io_pin_ll_uart; break; } case(io_pin_lcd): { io_lcd_mode_t pin_mode; if(parse_string(4, src, dst) != parse_ok) { string_copy(dst, "lcd: <pin use>=rs|rw|e|d0|d1|d2|d3|d4|d5|d6|d7|bl\n"); return(app_action_error); } if((pin_mode = io_lcd_mode_from_string(dst)) == io_lcd_error) { string_copy(dst, "lcd: <pin use>=rs|rw|e|d0|d1|d2|d3|d4|d5|d6|d7|bl\n"); return(app_action_error); } string_clear(dst); if(pin_mode == io_lcd_bl) // backlight { if(info->caps.output_analog) llmode = io_pin_ll_output_analog; else if(info->caps.output_digital) llmode = io_pin_ll_output_digital; else { string_cat(dst, "analog/digital output mode invalid for this io\n"); return(app_action_error); } } else { if(!info->caps.output_digital) { string_cat(dst, "digital output mode invalid for this io\n"); return(app_action_error); } llmode = io_pin_ll_output_digital; } pin_config->shared.lcd.pin_use = pin_mode; break; } case(io_pin_disabled): { llmode = io_pin_ll_disabled; break; } case(io_pin_error): { llmode = io_pin_ll_error; string_cat(dst, "unsupported io mode\n"); return(app_action_error); } } if((mode == io_pin_error) || (llmode == io_pin_ll_error)) { string_cat(dst, "error\n"); return(app_action_error); } pin_config->mode = mode; pin_config->llmode = llmode; if(info->init_pin_mode_fn && (info->init_pin_mode_fn(dst, info, pin_data, pin_config, pin) != io_ok)) { pin_config->mode = io_pin_disabled; pin_config->llmode = io_pin_ll_disabled; return(app_action_error); } io_config_dump(dst, &config, io, pin, false); return(app_action_normal); } irom app_action_t application_function_io_read(const string_t *src, string_t *dst) { const io_info_entry_t *info; io_config_pin_entry_t *pin_config; int io, pin, value; if(parse_int(1, src, &io, 0) != parse_ok) { string_cat(dst, "io-read: <io> <pin>\n"); return(app_action_error); } if((io < 0) || (io >= io_id_size)) { string_format(dst, "invalid io %d\n", io); return(app_action_error); } info = &io_info[io]; if(parse_int(2, src, &pin, 0) != parse_ok) { string_cat(dst, "get: <io> <pin>\n"); return(app_action_error); } if((pin < 0) || (pin >= info->pins)) { string_cat(dst, "io pin out of range\n"); return(app_action_error); } pin_config = &config.io_config[io][pin]; io_string_from_mode(dst, pin_config->mode); if(pin_config->mode == io_pin_i2c) { string_cat(dst, "/"); io_string_from_i2c_type(dst, pin_config->shared.i2c.pin_mode); } if(pin_config->mode == io_pin_lcd) { string_cat(dst, "/"); io_string_from_lcd_mode(dst, pin_config->shared.lcd.pin_use); } string_cat(dst, ": "); if(io_read_pin(dst, io, pin, &value) != io_ok) return(app_action_error); string_format(dst, "[%d]\n", value); return(app_action_normal); } irom app_action_t application_function_io_write(const string_t *src, string_t *dst) { const io_info_entry_t *info; io_config_pin_entry_t *pin_config; int io, pin, value; if(parse_int(1, src, &io, 0) != parse_ok) { string_cat(dst, "io-write <io> <pin> <value>\n"); return(app_action_error); } if((io < 0) || (io >= io_id_size)) { string_format(dst, "invalid io %d\n", io); return(app_action_error); } info = &io_info[io]; if(parse_int(2, src, &pin, 0) != parse_ok) { string_cat(dst, "io-write <io> <pin> <value>\n"); return(app_action_error); } if((pin < 0) || (pin >= info->pins)) { string_cat(dst, "invalid pin\n"); return(app_action_error); } pin_config = &config.io_config[io][pin]; value = 0; parse_int(3, src, &value, 0); io_string_from_mode(dst, pin_config->mode); if(pin_config->mode == io_pin_lcd) { string_cat(dst, "/"); io_string_from_lcd_mode(dst, pin_config->shared.lcd.pin_use); } string_cat(dst, ": "); if(io_write_pin(dst, io, pin, value) != io_ok) { string_cat(dst, "\n"); return(app_action_error); } if(io_read_pin(dst, io, pin, &value) != io_ok) { string_cat(dst, "\n"); return(app_action_error); } string_format(dst, "[%d]\n", value); return(app_action_normal); } irom app_action_t application_function_io_trigger(const string_t *src, string_t *dst) { const io_info_entry_t *info; int io, pin; io_trigger_t trigger_type; if(parse_int(1, src, &io, 0) != parse_ok) goto usage; if((io < 0) || (io >= io_id_size)) { string_format(dst, "invalid io %d\n", io); return(app_action_error); } info = &io_info[io]; if(parse_int(2, src, &pin, 0) != parse_ok) goto usage; if((pin < 0) || (pin >= info->pins)) { string_cat(dst, "invalid pin\n"); return(app_action_error); } if(parse_string(3, src, dst) != parse_ok) goto usage; if((trigger_type = string_to_trigger_type(dst)) == io_trigger_error) goto usage; string_clear(dst); string_cat(dst, "trigger "); trigger_type_to_string(trigger_type, dst); string_format(dst, " %u/%u: ", io, pin); if(io_trigger_pin(dst, io, pin, trigger_type) != io_ok) { string_cat(dst, "\n"); return(app_action_error); } string_cat(dst, "ok\n"); return(app_action_normal); usage: string_clear(dst); string_cat(dst, "io-trigger <io> <pin> <action> (action = off/on/down/up)\n"); return(app_action_error); } irom static app_action_t application_function_io_clear_set_flag(const string_t *src, string_t *dst, int value) { const io_info_entry_t *info; io_data_entry_t *data; io_data_pin_entry_t *pin_data; io_config_pin_entry_t *pin_config; int io, pin; io_pin_flag_t saved_flags; if(parse_int(1, src, &io, 0) != parse_ok) { string_cat(dst, "io-flag <io> <pin> <flag>\n"); return(app_action_error); } if((io < 0) || (io >= io_id_size)) { string_format(dst, "invalid io %d\n", io); return(app_action_error); } info = &io_info[io]; data = &io_data[io]; if(parse_int(2, src, &pin, 0) != parse_ok) { string_cat(dst, "io-flag <io> <pin> <flag>\n"); return(app_action_error); } if((pin < 0) || (pin >= info->pins)) { string_cat(dst, "invalid pin\n"); return(app_action_error); } pin_data = &data->pin[pin]; pin_config = &config.io_config[io][pin]; saved_flags = pin_config->flags; if((parse_string(3, src, dst) == parse_ok) && !pin_flag_from_string(dst, pin_config, value)) { string_copy(dst, "io-flag <io> <pin> <flag>\n"); return(app_action_error); } if(pin_config->flags.pullup && !info->caps.pullup) { pin_config->flags = saved_flags; string_copy(dst, "io does not support pullup\n"); return(app_action_error); } if(info->init_pin_mode_fn && (info->init_pin_mode_fn(dst, info, pin_data, pin_config, pin) != io_ok)) { pin_config->flags = saved_flags; string_copy(dst, "cannot enable this flag\n"); return(app_action_error); } string_clear(dst); string_format(dst, "flags for pin %d/%d:", io, pin); pin_string_from_flags(dst, pin_config); string_cat(dst, "\n"); return(app_action_normal); } irom app_action_t application_function_io_set_flag(const string_t *src, string_t *dst) { return(application_function_io_clear_set_flag(src, dst, 1)); } irom app_action_t application_function_io_clear_flag(const string_t *src, string_t *dst) { return(application_function_io_clear_set_flag(src, dst, 0)); } /* dump */ typedef enum { ds_id_io, ds_id_pin, ds_id_flags_1, ds_id_flags_2, ds_id_mode, ds_id_disabled, ds_id_input, ds_id_counter, ds_id_trigger_1, ds_id_trigger_2, ds_id_output, ds_id_timer, ds_id_analog_output, ds_id_i2c_sda, ds_id_i2c_scl, ds_id_uart, ds_id_lcd, ds_id_unknown, ds_id_not_detected, ds_id_info_1, ds_id_info_2, ds_id_header, ds_id_footer, ds_id_preline, ds_id_postline, ds_id_error, ds_id_length, ds_id_invalid = ds_id_length } dump_string_id_t; typedef const char string_array_t[ds_id_length][256]; typedef struct { const string_array_t plain; const string_array_t html; } dump_string_t; static const roflash dump_string_t dump_strings = { .plain = { "io[%d]: %s@%x", " pin: %2d", " flags:", ", ", "mode: ", "disabled", "input, state: %s", "counter, counter: %d, debounce: %d", "trigger, counter: %d, debounce: %d, io: %d, pin: %d, trigger type: ", "", "output, state: %s", "timer, config direction: %s, speed: %d ms, current direction: %s, delay: %d ms, state: %s", "analog output, min/static: %d, max: %d, current speed: %d, direction: %s, value: %d", "i2c/sda", "i2c/scl", "uart", "lcd", "unknown", " not found\n", ", info: ", "", "", "", "", "\n", "error", }, .html = { "<td>io[%d]</td><td>%s@%x</td>", "<td></td><td>%d</td>", "<td>", "</td>", "", "<td>disabled</td>", "<td>input</td><td>state: %s</td>", "<td>counter</td><td><td>counter: %d</td><td>debounce: %d</td>", "<td>trigger</td><td>counter: %d</td><td>debounce: %d</td><td>io: %d</td><td>pin: %d</td><td>trigger type: ", "</td>", "<td>output</td><td>state: %s</td>", "<td>timer</td><td>config direction: %s, speed: %d ms</td><<td>current direction %s, delay: %d ms, state: %s</td>", "<td>analog output</td><td>min/static: %d, max: %d, speed: %d, current direction: %s, value: %d", "<td>i2c</td><td>sda</td>", "<td>i2c</td><td>scl, speed: %d</td>", "<td>uart</td>", "<td>lcd</td>", "<td>unknown</td>", "<td>not found</td>", "<td>", "</td>", "<table border=\"1\"><tr><th>index</th><th>name</th><th>mode</th><th colspan=\"8\"></th></tr>", "</table>\n", "<tr>", "</trd>\n", "<td>error</td>", } };