Initial commit

This commit is contained in:
Chris 2025-04-03 20:55:28 +02:00
commit d7d9a3567a
7 changed files with 431 additions and 0 deletions

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# SerialShell for embedded devices
## Installing
Add to your platformio.ini:
```text
lib_deps = https://dev.noccylabs.info/noccy/pio-serialshell.git#0.1.0 \
your_other_deps...
```
## Example usage
```c
void cmdHandler(char* buf, int len) {
// Tokenize the string on spaces
char* tok = strtok(buf, " ");
// If tokenization fails, return
if (tok == NULL) return;
// Check if the "help" command was entered
if (strncmp(tok, "help\0", 5) == 0) {
// ...
}
}
void setup() {
// Setup serial
Serial.begin(115200);
// Initialize the shell, set the prompt, and assign the callback
shell_init();
shell_prompt("cmd> ");
shell_callback(cmdHandler);
}
void loop() {
// Process shell stuff
shell_loop();
}
```

47
example/gpio_funcs.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "gpio_funcs.h"
void gpioStatusEsp8266() {
volatile u32_t *gpio_out = (volatile u32_t*)0x60000300;
volatile u32_t *gpio_ena = (volatile u32_t*)0x6000030C;
volatile u32_t *gpio_in = (volatile u32_t*)0x60000318;
for (int i = 0; i < NUM_DIGITAL_PINS; i++) {
int bit = 1 << i;
Serial.printf(" D%d dir=%s in=%s strap=%s out=%s\n",
i,
(*gpio_ena & bit) ? "OUTPUT" : "INPUT",
(*gpio_in & bit) ? "HIGH" : "LOW",
(*gpio_in & (bit << 16)) ? "HIGH" : "LOW",
(*gpio_out & bit) ? "HIGH" : "LOW"
);
}
}
void gpioScanArduino() {
for (int i = 0; i < NUM_DIGITAL_PINS; i++) {
int mode = getPinMode(i);
Serial.printf(" D%d dir=%s read=%s\n", i, (mode==OUTPUT) ? "OUTPUT" : (mode==INPUT_PULLUP ? "INPUT_PULLUP" : "INPUT"), digitalRead(i) ? "HIGH" : "LOW");
}
}
void gpioScan() {
#ifdef ESP8266
gpioStatusEsp8266();
#else
gpioScanArduino();
#endif
}
int getPinMode(int pin) {
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile auto *reg = portModeRegister(port);
volatile auto *out = portOutputRegister(port);
volatile auto *in = portInputRegister(port);
// Serial.printf("d=%d mask=%02x mode=%04x|%d out=%04x|%d in=%04x|%d\n", pin, bit, *reg, (*reg&bit), *out, (*out&bit), *in, (*in&bit));
if (*reg & bit) return (OUTPUT);
return ((*out & bit) ? INPUT_PULLUP : INPUT);
}

13
example/gpio_funcs.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <Arduino.h>
#include "ESP8266WiFi.h"
#include <shell.h>
#include <Wire.h>
#include <inttypes.h>
#include <Esp.h>
void gpioStatusEsp8266();
void gpioScanArduino();
void gpioScan();
int getPinMode(int pin);

236
example/main.cpp Normal file
View File

@ -0,0 +1,236 @@
#include <Arduino.h>
#include "ESP8266WiFi.h"
#include <shell.h>
#include <Wire.h>
#include <inttypes.h>
#include <Esp.h>
#include "gpio_funcs.h"
void scanWifi();
void scanI2c();
void cmdHandler(char* buf, int len) {
char* tok = strtok(buf, " ");
if (tok == NULL) return;
if (strncmp(tok, "help\0", 5) == 0) {
Serial.println("echo Toggle hard echo");
Serial.println("status Show device status");
Serial.println("set Update a setting value");
Serial.println("get Get a setting value");
Serial.println("list List all settings");
Serial.println("undo Reload NVRAM and discard changes");
Serial.println("commit Commit settings to NVRAM");
Serial.println("scan Scan wifi,i2c");
Serial.println("gpio Manipulate GPIO");
Serial.println("reset Reset (restart) the device");
return;
}
if (strncmp(tok, "status\0", 7) == 0) {
// char* status = strtok(NULL, " ");
Serial.printf("Hardware.: %s (%s) %s\n", BOARD, BOARD_VARIANT, BOARD_MCU);
Serial.printf("Serial...: %08x\n", system_get_chip_id());
Serial.printf("WiFi.....: %s (ssid:%s,ip:%s)\n", "connected", "HOtSpOt", "0.0.0.0");
Serial.printf("MQTT.....: %s (server:%s,user:%s)\n", "disconnected", "10.1.4.2", "d1");
return;
}
/*
echo on
echo off
echo "message"
*/
if (strncmp(tok, "echo\0", 5) == 0) {
char* echostate = strtok(NULL, " ");
if (echostate == NULL) {
Serial.println("E: Expected on or off");
} else {
if (strncmp(echostate, "on\0", 3) == 0) {
Serial.println("Echo on");
shell_echo(true);
} else if (strncmp(echostate, "off\0", 4) == 0) {
Serial.println("Echo off");
shell_echo(false);
} else {
}
}
return;
}
/*
config
config open GROUP
config set KEY VALUE
config get KEY
config commit
*/
if (strncmp(tok, "set\0", 4) == 0) {
char* arg1 = strtok(NULL, " ");
if (arg1 == NULL) {
Serial.println("E: Missing setting name");
} else {
char* arg2 = strtok(NULL, "\0");
if (arg2 == NULL) {
Serial.println("E: Missing setting value");
} else {
Serial.printf("set: %s = %s\n", arg1, arg2);
}
}
return;
}
/*
scan show usage
scan wifi
scan i2c
*/
if (strncmp(tok, "scan\0", 5) == 0) {
char* scantype = strtok(NULL, " ");
if (scantype == NULL) {
Serial.println("E: Missing scan type");
} else {
if (strncmp(scantype,"wifi\0",5) == 0) {
scanWifi();
} else if (strncmp(scantype,"i2c\0",4) == 0) {
scanI2c();
} else {
Serial.println("E: Invalid scan type");
}
return;
}
return;
}
/*
gpio show usage
gpio scan show all GPIO
gpio N show GPIO N
gpio N {in|out} set GPIO N direction
gpio N {high|low} set GPIO N state
*/
if (strncmp(tok, "gpio\0", 5) == 0) {
char* scantype = strtok(NULL, " ");
if (scantype == NULL) {
// Serial.println("E: Missing gpio operation");
gpioScan();
return;
} else {
if (strncmp(scantype,"set\0",4) == 0) {
char* gpiosetpin = strtok(NULL, " ");
if (NULL == gpiosetpin) {
Serial.println("E: missing pin");
return;
}
int dpin = atoi(gpiosetpin);
char* gpioset = strtok(NULL, " ");
if (NULL == gpioset) {
Serial.println("E: missing new state");
return;
}
if (strncmp(gpioset, "high\0", 5) == 0) {
digitalWrite(dpin, HIGH);
Serial.printf(" D%d => high\n", dpin);
} else if (strncmp(gpioset, "low\0", 4) == 0) {
digitalWrite(dpin, LOW);
Serial.printf(" D%d => low\n", dpin);
}
return;
} else if (strncmp(scantype,"get\0",4) == 0) {
char* gpiogetpin = strtok(NULL, " ");
if (NULL == gpiogetpin) {
Serial.println("E: missing pin");
return;
}
int dpin = atoi(gpiogetpin);
int getval = digitalRead(dpin);
Serial.printf(" D%d: %s\n", dpin, getval?"HIGH":"LOW");
} else if (strncmp(scantype,"mode\0",5) == 0) {
char* gpiomodepin = strtok(NULL, " ");
if (NULL == gpiomodepin) {
Serial.println("E: missing pin");
return;
}
int dpin = atoi(gpiomodepin);
char* gpiomode = strtok(NULL, " ");
if (NULL == gpiomode) {
Serial.println("E: missing mode");
return;
}
if (strncmp(gpiomode, "in\0", 3) == 0) {
pinMode(dpin, INPUT);
Serial.printf(" D%d -> input\n", dpin);
} else if (strncmp(gpiomode, "out\0", 4) == 0) {
pinMode(dpin, OUTPUT);
Serial.printf(" D%d -> output\n", dpin);
} else {
Serial.printf("E: invalid mode");
}
} else {
Serial.println("E: Invalid gpio operation");
}
return;
}
return;
}
/*
i2c
i2c scan same as 'scan i2c'
i2c write ADDR DATA..
i2c read ADDR LEN read LEN bytes
*/
Serial.println("E: Invalid command");
}
void scanI2c() {
byte error, address;
int nDevices;
Serial.println("Starting I2C scan...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.printf(" Found device at 0x%02x\n", address);
nDevices++;
} else if (error==4) {
Serial.printf(" Unknown error at 0x%02x\n", address);
}
}
Serial.printf("Scan complete (%d items)\n", nDevices);
}
void scanWifi() {
Serial.println("Starting WiFi scan...");
int numNetworks = WiFi.scanNetworks();
for (int i = 0; i < numNetworks; i++) {
Serial.printf(" %s rssi=%d bssid=%s ch=%d enc=%d\n", WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.BSSIDstr(i).c_str(), WiFi.channel(i), WiFi.encryptionType(i));
}
Serial.printf("Scan complete (%d items)\n", numNetworks);
}
void setup() {
Serial.begin(115200);
shell_init();
shell_prompt("cmd> ");
shell_callback(cmdHandler);
}
void loop() {
shell_loop();
}

15
include/shell.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
// SerialShell header
void shell_init();
void shell_echo(bool echo);
void shell_prompt(char* prompt);
void shell_callback(void(*cb)(char*, int));
void shell_loop();
const int IBUF_MAX = 255;

20
library.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "SerialShell",
"version": "1.0.0",
"description": "",
"keywords": "serial, shell, command, interactive",
"repository": {
"type": "git",
"url": "https://dev.noccylabs.info/noccy/pio-serialshell.git"
},
"authors": [
{
"name": "Christopher Vagnetoft",
"email": "labs@noccy.com",
"maintainer": true
}
],
"license": "GPL-2.0-or-later",
"frameworks": "*",
"platforms": "*"
}

59
src/shell.cpp Normal file
View File

@ -0,0 +1,59 @@
#include <Arduino.h>
#include "shell.h"
bool m_echo = true;
char m_ibuf[256] = { 0 };
int m_ibuf_pos = 0;
char m_prompt[10] = { 0 };
void (*m_callback)(char*, int);
void shell_init() {
strcat(m_prompt, "#> ");
}
void shell_echo(bool echo) {
m_echo = echo;
}
void shell_prompt(char* prompt) {
strncpy(m_prompt, prompt, 10);
}
void shell_callback(void(*cb)(char*, int)) {
m_callback = cb;
}
void draw_prompt() {
Serial.print(m_prompt);
}
void shell_loop() {
int c;
while (Serial.available()) {
c = Serial.read();
if (c == 13 || c == 4) {
// if (m_echo)
Serial.write(10);
if (m_callback != nullptr && m_ibuf_pos>0) {
m_callback(m_ibuf, m_ibuf_pos);
}
draw_prompt();
m_ibuf_pos = 0;
memset(m_ibuf, 0, IBUF_MAX);
} else if (c == 8 || c == 127) {
if (m_ibuf_pos > 0) {
m_ibuf[--m_ibuf_pos] = 0;
if (m_echo)
Serial.print("\x08 \x08");
}
} else if (c >= 32 && c<127) {
if (m_ibuf_pos < IBUF_MAX) {
m_ibuf[m_ibuf_pos++] = c & 0xFF;
if (m_echo)
Serial.write(c);
}
}
}
}