First commit
This commit is contained in:
		
						commit
						27b2cfb80b
					
				
							
								
								
									
										19
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | Copyright (c) 2024 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/ | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | # cmd | ||||||
|  | 
 | ||||||
|  | This is a simple Arduino Library I wrote because the existing Libraries did not do what I was looking for. I wanted a simple command line interface that worked similar to Linux and Cisco clis where you can edit the buffer and ask for help. Its fairly simple to use: | ||||||
|  | 
 | ||||||
|  | ## Example usage | ||||||
|  | See the standard example .ino file. | ||||||
|  | 
 | ||||||
|  | You can load the tty with: | ||||||
|  | ```bash | ||||||
|  | screen -L /dev/ttyUSB0 9600 | ||||||
|  | ``` | ||||||
							
								
								
									
										377
									
								
								cmd.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								cmd.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,377 @@ | |||||||
|  | #include "cmd.h" | ||||||
|  | 
 | ||||||
|  | // Initiator.
 | ||||||
|  | // Size is the number of commands, default callback is called if a command is
 | ||||||
|  | // not found.
 | ||||||
|  | Cmd::Cmd(size_t size, CmdFunction defaultCallback) { | ||||||
|  | 	m_size = size; | ||||||
|  | 	m_commands = (const char **)malloc(sizeof(const char *) * m_size); | ||||||
|  | 	m_functions = (CmdFunction *)malloc(sizeof(CmdFunction) * m_size); | ||||||
|  | 	m_defaultFunction = defaultCallback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return the set size of the command array.
 | ||||||
|  | size_t Cmd::GetSize() { return m_size; } | ||||||
|  | 
 | ||||||
|  | // Add a command to the function list. If list is too full, false is returned.
 | ||||||
|  | bool Cmd::AddCmd(const char *cmd, CmdFunction function) { | ||||||
|  | 	if (m_nextCmd >= m_size) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	m_commands[m_nextCmd] = cmd; | ||||||
|  | 	m_functions[m_nextCmd] = function; | ||||||
|  | 
 | ||||||
|  | 	m_nextCmd++; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get array of all commands.
 | ||||||
|  | const char **Cmd::GetCmds() { return m_commands; } | ||||||
|  | 
 | ||||||
|  | // Rather or not we echo back to serial characters received.
 | ||||||
|  | bool Cmd::GetEcho() { return m_echo; } | ||||||
|  | void Cmd::SetEcho(bool echo) { m_echo = echo; } | ||||||
|  | 
 | ||||||
|  | // The separator for command parsing.
 | ||||||
|  | const char *Cmd::GetSeparator() { return m_separator; } | ||||||
|  | void Cmd::SetSeparator(const char *separator) { m_separator = separator; } | ||||||
|  | 
 | ||||||
|  | // Indicates the command line.
 | ||||||
|  | const char *Cmd::GetLineIndicator() { return m_line_indicator; } | ||||||
|  | void Cmd::SetLineIndicator(const char *line_indicator) { | ||||||
|  | 	m_line_indicator = line_indicator; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Buffer size configuration.
 | ||||||
|  | size_t Cmd::GetBufferSize() { return m_buffer_size; } | ||||||
|  | void Cmd::SetBufferSize(size_t bufferSize) { m_buffer_size = bufferSize; } | ||||||
|  | 
 | ||||||
|  | // Return current buffer.
 | ||||||
|  | const char *Cmd::GetBuffer() { return m_buffer; } | ||||||
|  | 
 | ||||||
|  | // Resets the buffer and prints
 | ||||||
|  | void Cmd::StartNewBuffer() { | ||||||
|  | 	free(m_buffer); | ||||||
|  | 	m_buffer = NULL; | ||||||
|  | 	Serial.print(m_line_indicator); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Print the current buffer.
 | ||||||
|  | void Cmd::PrintBuffer() { | ||||||
|  | 	// If we're processing a command, do not print.
 | ||||||
|  | 	if (m_processing) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	// Set the last character in the buffer to null to terminate.
 | ||||||
|  | 	m_buffer[m_buffer_read] = '\0'; | ||||||
|  | 	// Print indicator and buffer.
 | ||||||
|  | 	Serial.print(m_line_indicator); | ||||||
|  | 	Serial.print(m_buffer); | ||||||
|  | 	// Move cursor to correct location.
 | ||||||
|  | 	for (unsigned int i = m_buffer_cursor; i < m_buffer_read; i++) { | ||||||
|  | 		Serial.write('\x08'); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse next token.
 | ||||||
|  | char *Cmd::Parse() { return strtok(NULL, m_separator); } | ||||||
|  | 
 | ||||||
|  | // Print help for the current command.
 | ||||||
|  | void Cmd::PrintHelp() { | ||||||
|  | 	// Copy buffer to token buffer.
 | ||||||
|  | 	m_bufferTok = (char *)malloc(m_buffer_size); | ||||||
|  | 	strcpy(m_bufferTok, m_buffer); | ||||||
|  | 
 | ||||||
|  | 	// Tokenize buffer based on separator and get first token being the command.
 | ||||||
|  | 	char *cmd = strtok(m_bufferTok, m_separator); | ||||||
|  | 
 | ||||||
|  | 	// Printing help means the command line is currently the line we're on.
 | ||||||
|  | 	Serial.println(); | ||||||
|  | 
 | ||||||
|  | 	// To prevent buffer printing during a command execution.
 | ||||||
|  | 	m_processing = true; | ||||||
|  | 	// Look for the command that matches.
 | ||||||
|  | 	bool foundCmd = false; | ||||||
|  | 	// Only scan for command if specified.
 | ||||||
|  | 	if (cmd != NULL) { | ||||||
|  | 		for (unsigned int i = 0; i < m_size && m_commands[i] != NULL; i++) { | ||||||
|  | 			if (strcasecmp_P(cmd, m_commands[i]) == 0) { | ||||||
|  | 				// If command matches, call its function and tell it we're asking for
 | ||||||
|  | 				// help.
 | ||||||
|  | 				m_functions[i](this, cmd, true); | ||||||
|  | 				foundCmd = true; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If command wasn't found, call the default callback and tell it we're asking
 | ||||||
|  | 	// for help.
 | ||||||
|  | 	if (!foundCmd) { | ||||||
|  | 		Serial.println("Calling default function"); | ||||||
|  | 		m_defaultFunction(this, cmd, true); | ||||||
|  | 	} | ||||||
|  | 	m_processing = false; | ||||||
|  | 
 | ||||||
|  | 	// Print the buffer now that help was provided.
 | ||||||
|  | 	Serial.println(); | ||||||
|  | 	PrintBuffer(); | ||||||
|  | 
 | ||||||
|  | 	// Free memory used by the token buffer.
 | ||||||
|  | 	free(m_bufferTok); | ||||||
|  | 	m_bufferTok = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse buffer for command.
 | ||||||
|  | void Cmd::ParseBuffer() { | ||||||
|  | 	// Copy buffer to token buffer.
 | ||||||
|  | 	m_bufferTok = (char *)malloc(m_buffer_size); | ||||||
|  | 	strcpy(m_bufferTok, m_buffer); | ||||||
|  | 
 | ||||||
|  | 	// Tokenize buffer based on separator and get first token being the command.
 | ||||||
|  | 	char *cmd = strtok(m_bufferTok, m_separator); | ||||||
|  | 
 | ||||||
|  | 	// To prevent buffer printing during a command execution.
 | ||||||
|  | 	m_processing = true; | ||||||
|  | 	// Look for the command that matches.
 | ||||||
|  | 	bool foundCmd = false; | ||||||
|  | 	// Only scan for command if specified.
 | ||||||
|  | 	if (cmd != NULL) { | ||||||
|  | 		for (unsigned int i = 0; i < m_size && m_commands[i] != NULL; i++) { | ||||||
|  | 			if (strcasecmp_P(cmd, m_commands[i]) == 0) { | ||||||
|  | 				// If command matches, call its function.
 | ||||||
|  | 				m_functions[i](this, cmd, false); | ||||||
|  | 				foundCmd = true; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If command wasn't found, call the default callback.
 | ||||||
|  | 	if (!foundCmd) { | ||||||
|  | 		m_defaultFunction(this, cmd, false); | ||||||
|  | 	} | ||||||
|  | 	m_processing = false; | ||||||
|  | 
 | ||||||
|  | 	// Free memory used by the token buffer.
 | ||||||
|  | 	free(m_bufferTok); | ||||||
|  | 	m_bufferTok = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Main command loop, call in the main loop of your program.
 | ||||||
|  | void Cmd::Loop() { | ||||||
|  | 	// If no serial data is available, we do not have anything to process.
 | ||||||
|  | 	if (!Serial.available()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If buffer is not allocated, let's reset.
 | ||||||
|  | 	if (m_buffer == NULL) { | ||||||
|  | 		m_buffer = (char *)malloc(m_buffer_size); | ||||||
|  | 		m_buffer_read = 0; | ||||||
|  | 		m_buffer_cursor = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Start read and read all available data in serial RX buffer.
 | ||||||
|  | 	bool receivedEndLine = false; | ||||||
|  | 	size_t availableData = Serial.available(); | ||||||
|  | 	for (size_t i = 1; i <= availableData; i++) { | ||||||
|  | 		char byteRead = Serial.read(); | ||||||
|  | 
 | ||||||
|  | 		// If we're currently reading an escape character, verify state.
 | ||||||
|  | 		if (m_buffer_reading_esc == 1) { | ||||||
|  | 			// If we get the bracket, we need to move on to the next step in reading
 | ||||||
|  | 			// an escape character. Otherwise, we are not reading an escape character.
 | ||||||
|  | 			if (byteRead == '[') { | ||||||
|  | 				m_buffer_reading_esc = 2; | ||||||
|  | 				continue; | ||||||
|  | 			} else { | ||||||
|  | 				m_buffer_reading_esc = 0; | ||||||
|  | 			} | ||||||
|  | 		} else if (m_buffer_reading_esc == 2) { | ||||||
|  | 			// Process escape character read.
 | ||||||
|  | 			switch (byteRead) { | ||||||
|  | 				case 'D':  // Move cursor left
 | ||||||
|  | 					m_buffer_reading_esc = 0; | ||||||
|  | 					if (m_buffer_cursor <= 0) { | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 					m_buffer_cursor--; | ||||||
|  | 					Serial.print("\x1b[D"); | ||||||
|  | 					break; | ||||||
|  | 				case 'C':  // Move cursor right
 | ||||||
|  | 					m_buffer_reading_esc = 0; | ||||||
|  | 					if (m_buffer_cursor >= m_buffer_read) { | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 					m_buffer_cursor++; | ||||||
|  | 					Serial.print("\x1b[C"); | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				default: | ||||||
|  | 					m_buffer_reading_esc = 0; | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Check if ascii byte.
 | ||||||
|  | 		bool is_ascii = byteRead >= 32 && byteRead <= 126; | ||||||
|  | 
 | ||||||
|  | 		// If we're to echo and its an ascii byte, send the byte we read back to the
 | ||||||
|  | 		// serial console.
 | ||||||
|  | 		if (m_echo && is_ascii) { | ||||||
|  | 			Serial.write(byteRead); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If escape character received, move into escape reading mode.
 | ||||||
|  | 		if (byteRead == '\x1b') { | ||||||
|  | 			m_buffer_reading_esc = 1; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If backspace or delete key.
 | ||||||
|  | 		if (byteRead == 8 || byteRead == 127) { | ||||||
|  | 			// Only perform character delete if buffer exists and cursor isn't at
 | ||||||
|  | 			// start.
 | ||||||
|  | 			if (m_buffer_read != 0 && m_buffer_cursor != 0) { | ||||||
|  | 				// If cursor isn't at start, we need to re-print the line minus the
 | ||||||
|  | 				// character deleted.
 | ||||||
|  | 				if (m_buffer_cursor != m_buffer_read) { | ||||||
|  | 					// Create new buffer for re-writing current buffer.
 | ||||||
|  | 					char *buf = (char *)malloc(m_buffer_size); | ||||||
|  | 					// Copy current buffer.
 | ||||||
|  | 					strcpy(buf, m_buffer); | ||||||
|  | 					// Clear the line from the curosr.
 | ||||||
|  | 					Serial.print("\x08\x1b[1P"); | ||||||
|  | 					// Print and re-write the buffer from the cursor location minus
 | ||||||
|  | 					// character deleted.
 | ||||||
|  | 					for (unsigned int i = m_buffer_cursor; i < m_buffer_read; i++) { | ||||||
|  | 						m_buffer[i - 1] = buf[i]; | ||||||
|  | 						Serial.write(buf[i]); | ||||||
|  | 					} | ||||||
|  | 					// Now that the buffer has been re-written, we can free the buffer.
 | ||||||
|  | 					free(buf); | ||||||
|  | 					// Move the cursor back to where it should be.
 | ||||||
|  | 					for (unsigned int i = m_buffer_cursor; i < m_buffer_read; i++) { | ||||||
|  | 						Serial.write('\x08'); | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
|  | 					// As we're not deleting from cursor location, we can just clear the
 | ||||||
|  | 					// last character via this escape.
 | ||||||
|  | 					Serial.print("\x08\x1b[K"); | ||||||
|  | 				} | ||||||
|  | 				// Wipe the last byte in the buffer in both cases.
 | ||||||
|  | 				m_buffer_read--; | ||||||
|  | 				m_buffer_cursor--; | ||||||
|  | 				m_buffer[m_buffer_read] = '\0'; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If begining of line requested.
 | ||||||
|  | 		if (byteRead == 1) { | ||||||
|  | 			// Move cursor to beginning of line.
 | ||||||
|  | 			while (m_buffer_cursor != 0) { | ||||||
|  | 				m_buffer_cursor--; | ||||||
|  | 				Serial.print("\x1b[D"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If end of line requested.
 | ||||||
|  | 		if (byteRead == 5) { | ||||||
|  | 			// Move cursor to end of line.
 | ||||||
|  | 			while (m_buffer_cursor < m_buffer_read) { | ||||||
|  | 				m_buffer_cursor++; | ||||||
|  | 				Serial.print("\x1b[C"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If cancel or exit received.
 | ||||||
|  | 		if (byteRead == 3 || byteRead == 4) { | ||||||
|  | 			Serial.println(); | ||||||
|  | 			// Clear buffer, start new line,
 | ||||||
|  | 			StartNewBuffer(); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Clear screen.
 | ||||||
|  | 		if (byteRead == 12) { | ||||||
|  | 			m_buffer[m_buffer_read] = '\0'; | ||||||
|  | 			Serial.print("\x1b[H\x1b[J"); | ||||||
|  | 			PrintBuffer(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If end of line.
 | ||||||
|  | 		if (byteRead == '\r') { | ||||||
|  | 			receivedEndLine = true; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If help requested, print it.
 | ||||||
|  | 		if (byteRead == '?') { | ||||||
|  | 			m_buffer[m_buffer_read] = '\0'; | ||||||
|  | 			PrintHelp(); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If not ascii, we only allow ascii on the cli.
 | ||||||
|  | 		if (!is_ascii) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If cursor is not at end, we need to write new byte where cursor is.
 | ||||||
|  | 		if (m_buffer_cursor != m_buffer_read) { | ||||||
|  | 			// Copy current buffer for re-write.
 | ||||||
|  | 			char *buf = (char *)malloc(m_buffer_size); | ||||||
|  | 			strcpy(buf, m_buffer); | ||||||
|  | 			// Set current cursor location byte to newly read byte.
 | ||||||
|  | 			m_buffer[m_buffer_cursor] = byteRead; | ||||||
|  | 			// From cursor location, re-write the buffer with old buffer data and
 | ||||||
|  | 			// print to the console.
 | ||||||
|  | 			for (unsigned int i = m_buffer_cursor; i < m_buffer_read; i++) { | ||||||
|  | 				m_buffer[i + 1] = buf[i]; | ||||||
|  | 				Serial.write(buf[i]); | ||||||
|  | 			} | ||||||
|  | 			// We're done with the buffer copy.
 | ||||||
|  | 			free(buf); | ||||||
|  | 			// Move cursor back to where it was.
 | ||||||
|  | 			for (unsigned int i = m_buffer_cursor; i < m_buffer_read; i++) { | ||||||
|  | 				Serial.write('\x08'); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// Otherwise new byte can go to end of buffer.
 | ||||||
|  | 			m_buffer[m_buffer_read] = byteRead; | ||||||
|  | 		} | ||||||
|  | 		// We read a new byte, increment the cursor location and read index.
 | ||||||
|  | 		m_buffer_read++; | ||||||
|  | 		m_buffer_cursor++; | ||||||
|  | 
 | ||||||
|  | 		// If we're going to overflow next read, we need to stop that.
 | ||||||
|  | 		if (m_buffer_read >= (m_buffer_size - 1)) { | ||||||
|  | 			Serial.println("Data too large."); | ||||||
|  | 
 | ||||||
|  | 			// Clear the buffer, and start new line.
 | ||||||
|  | 			StartNewBuffer(); | ||||||
|  | 
 | ||||||
|  | 			// Clear the serial buffer.
 | ||||||
|  | 			while (Serial.available()) { | ||||||
|  | 				Serial.read(); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If we received end of line in read, we need to parse the buffer.
 | ||||||
|  | 	if (receivedEndLine) { | ||||||
|  | 		// Print new line and null terminate the buffer.
 | ||||||
|  | 		Serial.println(); | ||||||
|  | 		m_buffer[m_buffer_read] = '\0'; | ||||||
|  | 
 | ||||||
|  | 		// Parse the buffer.
 | ||||||
|  | 		ParseBuffer(); | ||||||
|  | 
 | ||||||
|  | 		// Start new buffer.
 | ||||||
|  | 		StartNewBuffer(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								cmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								cmd.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | #ifndef _CMD_H_ | ||||||
|  | #define _CMD_H_ | ||||||
|  | 
 | ||||||
|  | #include <Arduino.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | class Cmd; | ||||||
|  | 
 | ||||||
|  | typedef void (*CmdFunction)(Cmd *thisCmd, char *command, bool printHelp); | ||||||
|  | 
 | ||||||
|  | class Cmd { | ||||||
|  |  protected: | ||||||
|  | 	const char **m_commands = NULL; | ||||||
|  | 	CmdFunction *m_functions = NULL; | ||||||
|  | 
 | ||||||
|  | 	CmdFunction m_defaultFunction; | ||||||
|  | 
 | ||||||
|  | 	size_t m_size = 0; | ||||||
|  | 	size_t m_nextCmd = 0; | ||||||
|  | 
 | ||||||
|  | 	bool m_echo = true; | ||||||
|  | 	bool m_processing = false; | ||||||
|  | 	const char *m_separator = " "; | ||||||
|  | 	const char *m_line_indicator = "$ "; | ||||||
|  | 	size_t m_buffer_size = 50; | ||||||
|  | 
 | ||||||
|  | 	char *m_buffer = NULL; | ||||||
|  | 	char *m_bufferTok = NULL; | ||||||
|  | 	size_t m_buffer_read = 0; | ||||||
|  | 	uint8_t m_buffer_reading_esc = 0; | ||||||
|  | 	size_t m_buffer_cursor = 0; | ||||||
|  | 
 | ||||||
|  | 	void PrintHelp(); | ||||||
|  | 	void ParseBuffer(); | ||||||
|  | 	void StartNewBuffer(); | ||||||
|  | 
 | ||||||
|  |  public: | ||||||
|  | 	Cmd(size_t size, CmdFunction defaultCallback); | ||||||
|  | 
 | ||||||
|  | 	size_t GetSize(); | ||||||
|  | 	bool AddCmd(const char *cmd, CmdFunction function); | ||||||
|  | 	const char **GetCmds(); | ||||||
|  | 
 | ||||||
|  | 	bool GetEcho(); | ||||||
|  | 	void SetEcho(bool echo); | ||||||
|  | 
 | ||||||
|  | 	const char *GetSeparator(); | ||||||
|  | 	void SetSeparator(const char *separator); | ||||||
|  | 
 | ||||||
|  | 	const char *GetLineIndicator(); | ||||||
|  | 	void SetLineIndicator(const char *line_indicator); | ||||||
|  | 
 | ||||||
|  | 	size_t GetBufferSize(); | ||||||
|  | 	void SetBufferSize(size_t bufferSize); | ||||||
|  | 
 | ||||||
|  | 	const char *GetBuffer(); | ||||||
|  | 	void PrintBuffer(); | ||||||
|  | 	char *Parse(); | ||||||
|  | 
 | ||||||
|  | 	void SendESC(const char *code); | ||||||
|  | 
 | ||||||
|  | 	void Loop(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif  // _CMD_H_
 | ||||||
							
								
								
									
										95
									
								
								examples/standard/standard.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								examples/standard/standard.ino
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | |||||||
|  | #include <cmd.h> | ||||||
|  | 
 | ||||||
|  | // Global command variable.
 | ||||||
|  | Cmd *cmd; | ||||||
|  | 
 | ||||||
|  | // Command entered was invalid, or help is being requested.
 | ||||||
|  | void cmd_unrecognized(Cmd *thisCmd, char *command, bool printHelp) { | ||||||
|  | 	// If help is being requested, print the available commands.
 | ||||||
|  | 	if (printHelp) { | ||||||
|  | 		// Get the command parameters.
 | ||||||
|  | 		size_t size = cmd->GetSize();	 | ||||||
|  | 		PGM_P *commands = cmd->GetCmds(); | ||||||
|  | 
 | ||||||
|  | 		// Print each command.
 | ||||||
|  | 		Serial.println("Available commands:\n"); | ||||||
|  | 		for (int i = 0; i < size && commands[i] != NULL; i++) { | ||||||
|  | 			char buf[100]; | ||||||
|  | 			sprintf_P(buf, commands[i]); | ||||||
|  | 			Serial.println(buf); | ||||||
|  | 		} | ||||||
|  | 		// Stop here.
 | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	// No help was requested, so the command provided likely doesn't exist.
 | ||||||
|  | 	Serial.print("Unrecognized command ["); | ||||||
|  | 	Serial.print(command); | ||||||
|  | 	Serial.println("]"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Simple echo ping command.
 | ||||||
|  | void cmd_pi(Cmd *thisCmd, char *command, bool printHelp) { | ||||||
|  | 	// If help was requested, print the help for this command.
 | ||||||
|  | 	if (printHelp) { | ||||||
|  | 		Serial.print(command); | ||||||
|  | 		Serial.println(" *"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	// Echo back the buffer.
 | ||||||
|  | 	Serial.println(cmd->GetBuffer()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Demo send command.
 | ||||||
|  | void cmd_send(Cmd *thisCmd, char *command, bool printHelp) { | ||||||
|  | 	// If help was requested, print the help.
 | ||||||
|  | 	if (printHelp) { | ||||||
|  | 		Serial.print(command); | ||||||
|  | 		Serial.println(" address code"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Parse the next available argument.
 | ||||||
|  | 	char *parsed = cmd->Parse(); | ||||||
|  | 	if (parsed == NULL) { | ||||||
|  | 		Serial.println("Invalid address"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	// Parse integer.
 | ||||||
|  | 	int address = atoi(parsed); | ||||||
|  | 
 | ||||||
|  | 	// Get the next argument.
 | ||||||
|  | 	parsed = cmd->Parse(); | ||||||
|  | 	if (parsed == NULL) { | ||||||
|  | 		Serial.println("Invalid code"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	// Parse char.
 | ||||||
|  | 	unsigned char code = atoi(parsed); | ||||||
|  | 
 | ||||||
|  | 	// Print parsed arguments.
 | ||||||
|  | 	Serial.print("Sending code: "); | ||||||
|  | 	Serial.print(code); | ||||||
|  | 	Serial.print(" to <"); | ||||||
|  | 	Serial.print(address); | ||||||
|  | 	Serial.println(">"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  | 	// Setup serial interface.
 | ||||||
|  | 	Serial.begin(9600); | ||||||
|  | 
 | ||||||
|  | 	// Initialize the command line with 2 commands.
 | ||||||
|  | 	cmd = new Cmd(2, cmd_unrecognized); | ||||||
|  | 	 | ||||||
|  | 	// Add commands.
 | ||||||
|  | 	cmd->AddCmd(PSTR("pi"), cmd_pi); | ||||||
|  | 	cmd->AddCmd(PSTR("send"), cmd_send); | ||||||
|  | 
 | ||||||
|  | 	// Print a line indicator to inform the user the cli is ready.
 | ||||||
|  | 	Serial.print(cmd->GetLineIndicator()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  | 	// Run the command line loop.
 | ||||||
|  | 	cmd->Loop(); | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								library.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | { | ||||||
|  |     "name": "cmd", | ||||||
|  |     "description": "A serial command line interface with buffer editing.", | ||||||
|  |     "keywords": "serial, cmd, communication, processing, command", | ||||||
|  |     "authors": | ||||||
|  |     { | ||||||
|  |       "name": "James Coleman", | ||||||
|  |       "email": "grmrgecko@gmail.com", | ||||||
|  |       "url": "https://github.com/GRMrGecko" | ||||||
|  |     }, | ||||||
|  |     "repository": | ||||||
|  |     { | ||||||
|  |       "type": "git", | ||||||
|  |       "url": "https://github.com/GRMrGecko/cmd.git" | ||||||
|  |     }, | ||||||
|  |     "version": "1.0.0", | ||||||
|  |     "frameworks": "arduino", | ||||||
|  |     "platforms": "*" | ||||||
|  |   } | ||||||
							
								
								
									
										9
									
								
								library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								library.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | name=cmd | ||||||
|  | version=1.0.0 | ||||||
|  | author=James Coleman <grmrgecko@gmail.com> | ||||||
|  | maintainer=James Coleman <grmrgecko@gmail.com> | ||||||
|  | sentence=A serial command line interface with buffer editing. | ||||||
|  | paragraph=An easy to use command line interface, simply add commands and their callbacks. | ||||||
|  | category=Data Processing | ||||||
|  | url=https://github.com/GRMrGecko/cmd | ||||||
|  | architectures=* | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user