including tftp based shell and sockets from different project

This commit is contained in:
2024-12-15 12:09:32 +01:00
parent bd923e4fdc
commit 1cf86768c0
26 changed files with 2917 additions and 10 deletions
+4
View File
@@ -0,0 +1,4 @@
idf_component_register(
SRC_DIRS "extern"
INCLUDE_DIRS "include" "extern"
)
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
#ifndef EMBEDDED_CLI_H
#define EMBEDDED_CLI_H
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
// cstdint is available only since C++11, so use C header
#include <stdint.h>
// used for proper alignment of cli buffer
#if UINTPTR_MAX == 0xFFFF
#define CLI_UINT uint16_t
#elif UINTPTR_MAX == 0xFFFFFFFF
#define CLI_UINT uint32_t
#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu
#define CLI_UINT uint64_t
#else
#error unsupported pointer size
#endif
#define CLI_UINT_SIZE (sizeof(CLI_UINT))
// convert size in bytes to size in terms of CLI_UINTs (rounded up
// if bytes is not divisible by size of single CLI_UINT)
#define BYTES_TO_CLI_UINTS(bytes) \
(((bytes) + CLI_UINT_SIZE - 1)/CLI_UINT_SIZE)
typedef struct CliCommand CliCommand;
typedef struct CliCommandBinding CliCommandBinding;
typedef struct EmbeddedCli EmbeddedCli;
typedef struct EmbeddedCliConfig EmbeddedCliConfig;
struct CliCommand {
/**
* Name of the command.
* In command "set led 1 1" "set" is name
*/
const char *name;
/**
* String of arguments of the command.
* In command "set led 1 1" "led 1 1" is string of arguments
* Is ended with double 0x00 char
* Use tokenize functions to easily get individual tokens
*/
char *args;
};
/**
* Struct to describe binding of command to function and
*/
struct CliCommandBinding {
/**
* Name of command to bind. Should not be NULL.
*/
const char *name;
/**
* Help string that will be displayed when "help <cmd>" is executed.
* Can have multiple lines separated with "\r\n"
* Can be NULL if no help is provided.
*/
const char *help;
/**
* Flag to perform tokenization before calling binding function.
*/
bool tokenizeArgs;
/**
* Pointer to any specific app context that is required for this binding.
* It will be provided in binding callback.
*/
void *context;
/**
* Binding function for when command is received.
* If null, default callback (onCommand) will be called.
* @param cli - pointer to cli that is calling this binding
* @param args - string of args (if tokenizeArgs is false) or tokens otherwise
* @param context
*/
void (*binding)(EmbeddedCli *cli, char *args, void *context);
};
struct EmbeddedCli {
/**
* Should write char to connection
* @param cli - pointer to cli that executed this function
* @param c - actual character to write
*/
void (*writeChar)(EmbeddedCli *cli, char c);
/**
* Called when command is received and command not found in list of
* command bindings (or binding function is null).
* @param cli - pointer to cli that executed this function
* @param command - pointer to received command
*/
void (*onCommand)(EmbeddedCli *cli, CliCommand *command);
/**
* Can be used for any application context
*/
void *appContext;
/**
* Pointer to actual implementation, do not use.
*/
void *_impl;
};
/**
* Configuration to create CLI
*/
struct EmbeddedCliConfig {
/**
* Invitation string. Is printed at the beginning of each line with user
* input
*/
const char *invitation;
/**
* Size of buffer that is used to store characters until they're processed
*/
uint16_t rxBufferSize;
/**
* Size of buffer that is used to store current input that is not yet
* sended as command (return not pressed yet)
*/
uint16_t cmdBufferSize;
/**
* Size of buffer that is used to store previously entered commands
* Only unique commands are stored in buffer. If buffer is smaller than
* entered command (including arguments), command is discarded from history
*/
uint16_t historyBufferSize;
/**
* Maximum amount of bindings that can be added via addBinding function.
* Cli increases takes extra bindings for internal commands:
* - help
*/
uint16_t maxBindingCount;
/**
* Buffer to use for cli and all internal structures. If NULL, memory will
* be allocated dynamically. Otherwise this buffer is used and no
* allocations are made
*/
CLI_UINT *cliBuffer;
/**
* Size of buffer for cli and internal structures (in bytes).
*/
uint16_t cliBufferSize;
/**
* Whether autocompletion should be enabled.
* If false, autocompletion is disabled but you still can use 'tab' to
* complete current command manually.
*/
bool enableAutoComplete;
};
/**
* Returns pointer to default configuration for cli creation. It is safe to
* modify it and then send to embeddedCliNew().
* Returned structure is always the same so do not free and try to use it
* immediately.
* Default values:
* <ul>
* <li>rxBufferSize = 64</li>
* <li>cmdBufferSize = 64</li>
* <li>historyBufferSize = 128</li>
* <li>cliBuffer = NULL (use dynamic allocation)</li>
* <li>cliBufferSize = 0</li>
* <li>maxBindingCount = 8</li>
* <li>enableAutoComplete = true</li>
* </ul>
* @return configuration for cli creation
*/
EmbeddedCliConfig *embeddedCliDefaultConfig(void);
/**
* Returns how many space in config buffer is required for cli creation
* If you provide buffer with less space, embeddedCliNew will return NULL
* This amount will always be divisible by CLI_UINT_SIZE so allocated buffer
* and internal structures can be properly aligned
* @param config
* @return
*/
uint16_t embeddedCliRequiredSize(EmbeddedCliConfig *config);
/**
* Create new CLI.
* Memory is allocated dynamically if cliBuffer in config is NULL.
* After CLI is created, override function pointers to start using it
* @param config - config for cli creation
* @return pointer to created CLI
*/
EmbeddedCli *embeddedCliNew(EmbeddedCliConfig *config);
/**
* Same as calling embeddedCliNew with default config.
* @return
*/
EmbeddedCli *embeddedCliNewDefault(void);
/**
* Receive character and put it to internal buffer
* Actual processing is done inside embeddedCliProcess
* You can call this function from something like interrupt service routine,
* just make sure that you call it only from single place. Otherwise input
* might get corrupted
* @param cli
* @param c - received char
*/
void embeddedCliReceiveChar(EmbeddedCli *cli, char c);
/**
* Process rx/tx buffers. Command callbacks are called from here
* @param cli
*/
void embeddedCliProcess(EmbeddedCli *cli);
/**
* Add specified binding to list of bindings. If list is already full, binding
* is not added and false is returned
* @param cli
* @param binding
* @return true if binding was added, false otherwise
*/
bool embeddedCliAddBinding(EmbeddedCli *cli, CliCommandBinding binding);
/**
* Print specified string and account for currently entered but not submitted
* command.
* Current command is deleted, provided string is printed (with new line) after
* that current command is printed again, so user can continue typing it.
* @param cli
* @param string
*/
void embeddedCliPrint(EmbeddedCli *cli, const char *string);
/**
* Free allocated for cli memory
* @param cli
*/
void embeddedCliFree(EmbeddedCli *cli);
/**
* Perform tokenization of arguments string. Original string is modified and
* should not be used directly (only inside other token functions).
* Individual tokens are separated by single 0x00 char, double 0x00 is put at
* the end of token list. After calling this function, you can use other
* token functions to get individual tokens and token count.
*
* Important: Call this function only once. Otherwise information will be lost if
* more than one token existed
* @param args - string to tokenize (must have extra writable char after 0x00)
* @return
*/
void embeddedCliTokenizeArgs(char *args);
/**
* Return specific token from tokenized string
* @param tokenizedStr
* @param pos (counted from 1)
* @return token
*/
const char *embeddedCliGetToken(const char *tokenizedStr, uint16_t pos);
/**
* Same as embeddedCliGetToken but works on non-const buffer
* @param tokenizedStr
* @param pos (counted from 1)
* @return token
*/
char *embeddedCliGetTokenVariable(char *tokenizedStr, uint16_t pos);
/**
* Find token in provided tokens string and return its position (counted from 1)
* If no such token is found - 0 is returned.
* @param tokenizedStr
* @param token - token to find
* @return position (increased by 1) or zero if no such token found
*/
uint16_t embeddedCliFindToken(const char *tokenizedStr, const char *token);
/**
* Return number of tokens in tokenized string
* @param tokenizedStr
* @return number of tokens
*/
uint16_t embeddedCliGetTokenCount(const char *tokenizedStr);
#ifdef __cplusplus
}
#endif
#endif //EMBEDDED_CLI_H
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Sviatoslav Kokurin (funbiscuit)
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.
+7
View File
@@ -0,0 +1,7 @@
# Embedded CLI
Implementation is taken from `funbiscuit` @ [github](https://github.com/funbiscuit/embedded-cli)
- Only source and header where integrated.
- Full project is included in `external/embedded-cli`
+161
View File
@@ -0,0 +1,161 @@
/**
* @file cliWrapper.hpp
* @author Gustice
* @brief Wrapping Command-Line-Interfaces with custom endpoints
* @details Command-Line-Interface is based on the implementation of
* https://github.com/funbiscuit/embedded-cli
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include "embedded_cli.h"
#include <esp_log.h>
#include <functional>
#include <memory>
#include "utils.hpp"
/* Module configuration */
static constexpr int CLI_RX_BUFFER_SIZE = 64;
static constexpr int CLI_CMD_BUFFER_SIZE = 64; // to also accept URLs for Updates
static constexpr int CLI_HISTORY_SIZE = 16;
static constexpr int CLI_BINDING_COUNT = 8;
static constexpr std::string_view CliWelcomeString{
"** Smart-DMX-Bridge **\n"
" send 'help' for help-page\n"
" NOTE: Syslog-messages are sent to client of this session\n"};
/**
* @brief Wrapper for c-implementation of CLI
*/
class Cli {
public:
/// @brief Type for callback arguments
using CArgs = std::vector<std::string> &;
/// @brief Type for bind callback
using BindCb = std::function<void(CArgs)>;
/// @brief Type for command callback
using CommandCb = std::function<void(std::string, CArgs)>;
/// @brief Parameter structure for command-binding
struct Binding {
std::string_view name;
std::string_view help_output;
BindCb callback;
};
/// @brief Constructor
/// @param port Reference to serial port
/// @param cmd command callback
Cli(CharStream &port, CommandCb cmd)
: _port(port), _commandCb(cmd) {
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
// NOTE: Note setting cliBuffer allows Cli to allocate needed buffer in correct size
// config->cliBuffer = cliBuffer;
// config->cliBufferSize = CLI_BUFFER_SIZE;
config->rxBufferSize = CLI_RX_BUFFER_SIZE;
config->cmdBufferSize = CLI_CMD_BUFFER_SIZE;
config->historyBufferSize = CLI_HISTORY_SIZE;
config->maxBindingCount = CLI_BINDING_COUNT;
cli = embeddedCliNew(config);
if (cli == NULL) {
ESP_LOGE("CLI", "Cli was not created, requires %d bytes. Check sizes!",
embeddedCliRequiredSize(config));
return;
}
cli->onCommand = genericCallback;
cli->writeChar = writeToken;
// appContext is a user-end variable and is bound to current instance
cli->appContext = this;
}
/// @brief Destructor
~Cli() {
embeddedCliFree(cli);
}
/// @brief Create command binding
bool addBinding(Binding bind) {
BindCb *cb = new BindCb{bind.callback};
CliCommandBinding cBinding{.name = bind.name.begin(),
.help = bind.help_output.begin(),
.tokenizeArgs = true,
// NOTE: this hard reference only works because we only provide
// an add-function without remove
.context = (void *)(cb),
.binding = boundCallback};
return embeddedCliAddBinding(cli, cBinding);
}
/// @brief Static method to generate c-compatible function-pointer for bound-command-callback
/// @details - receives and wraps arguments in string,
/// - determines object according to appContext
/// - calls appropriate method of appropriate object
static void boundCallback(EmbeddedCli *cli, char *args, void *context) {
if (context == nullptr) {
return;
ESP_LOGE("CLI", "No context in callback available");
}
ESP_LOGD("CLI", "Received command:");
BindCb &cb = *((BindCb *)context);
uint len = embeddedCliGetTokenCount(args);
std::vector<std::string> sArgs;
while (len-- > 0) {
const char *const arg = embeddedCliGetToken(args, 1);
ESP_LOGD("CLI", " arg: %s", arg);
sArgs.emplace_back(arg);
// shift position to end of this element to save time
args += sArgs.back().size() + 1; // needs also to be shifted by null-delimiter
}
cb(sArgs);
}
/// @brief Static method to generate c-compatible function-pointer for command-callback
/// @details - receives and wraps arguments in string,
/// - determines object according to appContext
/// - calls command-callback of appropriate object
static void genericCallback(EmbeddedCli *cli, CliCommand *command) {
std::string name(command->name);
ESP_LOGI("CLI", "Received generic command: %s", name.c_str());
embeddedCliTokenizeArgs(command->args);
std::vector<std::string> sArgs;
for (int i = 1; i <= embeddedCliGetTokenCount(command->args); ++i) {
sArgs.emplace_back(embeddedCliGetToken(command->args, i));
ESP_LOGI("CLI", " arg %d : %s", i, sArgs.back().c_str());
}
auto &commandCb = static_cast<Cli *>(cli->appContext)->_commandCb;
commandCb(name, sArgs);
}
/// @brief Process buffer and trigger commands
void process() {
auto r = _port.read();
for (auto &&c : r) {
embeddedCliReceiveChar(cli, c);
}
embeddedCliProcess(cli);
}
/// @brief publicly exposed write method
void write(std::string output) {
_port.write(output);
}
private:
/// @brief Reference to hardware port
CharStream &_port;
/// @brief Callback for generic command
CommandCb _commandCb;
EmbeddedCli *cli = nullptr;
/// @brief Write callback
static void writeToken(EmbeddedCli *embeddedCli, char c) {
static_cast<Cli *>(embeddedCli->appContext)->_port.write(c);
}
};