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);
}
Exemplo n.º 2
0
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>",
	}
};