Initial commit
This commit is contained in:
commit
d7d9a3567a
41
README.md
Normal file
41
README.md
Normal 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
47
example/gpio_funcs.cpp
Normal 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
13
example/gpio_funcs.h
Normal 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
236
example/main.cpp
Normal 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
15
include/shell.h
Normal 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
20
library.json
Normal 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
59
src/shell.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user