Skip to content

Commit fddff95

Browse files
committed
add sqlite console example
1 parent 5988b3d commit fddff95

File tree

3 files changed

+318
-2
lines changed

3 files changed

+318
-2
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ If you do not have the ESP32 sdk for Arduino, please see https://github.com/espr
8888

8989
## Dependencies / pre-requisites
9090

91-
No dependencies except for the Arduino SDK. The Sqlite3 code is included with the library.
91+
No dependencies except for the Arduino and ESP32 core SDK. The Sqlite3 code is included with the library.
9292

9393
## Limitations on ESP32
9494

@@ -97,7 +97,6 @@ No dependencies except for the Arduino SDK. The Sqlite3 code is included with th
9797
## Limitations of this library
9898

9999
* Locking is not implemented. So it cannot be reliably used in a multi-threaded / multi-core code set, except for read-only operations.
100-
* As of now many features of Sqlite3 have been omitted, except for basic table and index operations. These are expected to be made available shortly.
101100

102101
## Limitations of Flash memory
103102

@@ -122,5 +121,9 @@ Output of SD Card database query through WebServer example:
122121
![](output_web_1.png?raw=true)
123122
![](output_web_2.png?raw=true)
124123

124+
SQLite console:
125+
126+
![](console_screenshot.png?raw=true)
127+
125128
## Issues
126129
Please contact the author (Arundale Ramanathan) at arun@siara.cc if you find any problem (or create issue here).

console_screenshot.png

64 KB
Loading
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/*
2+
Open and execute SQL statements throught this console.
3+
Output is in tab-delimited format.
4+
Connections for SD Card in SPI Mode:
5+
* SD Card | ESP32
6+
* DAT2 -
7+
* DAT3 SS (D5)
8+
* CMD MOSI (D23)
9+
* VSS GND
10+
* VDD 3.3V
11+
* CLK SCK (D18)
12+
* DAT0 MISO (D19)
13+
* DAT1 -
14+
Connections for SD Card in SD_MMC Mode:
15+
* SD Card | ESP32
16+
* DAT2 (1) D12
17+
* DAT3 (2) D13
18+
* CMD (3) D15
19+
* VDD (4) 3.3V
20+
* CLK (5) D14
21+
* VSS (6) GND
22+
* DAT0 (7) D2
23+
* DAT1 (8) D4
24+
*/
25+
26+
#include <stdio.h>
27+
#include <stdlib.h>
28+
#include <string.h>
29+
#include <sqlite3.h>
30+
#include <SPI.h>
31+
#include <FS.h>
32+
#include "SPIFFS.h"
33+
#include "SD_MMC.h"
34+
#include "SD.h"
35+
36+
/* You only need to format SPIFFS the first time you run a
37+
test or else use the SPIFFS plugin to create a partition
38+
https://github.com/me-no-dev/arduino-esp32fs-plugin */
39+
#define FORMAT_SPIFFS_IF_FAILED true
40+
#define MAX_FILE_NAME_LEN 100
41+
#define MAX_STR_LEN 500
42+
43+
char db_file_name[MAX_FILE_NAME_LEN] = "\0";
44+
sqlite3 *db = NULL;
45+
int rc;
46+
47+
bool first_time = false;
48+
static int callback(void *data, int argc, char **argv, char **azColName) {
49+
int i;
50+
if (first_time) {
51+
Serial.println((const char *) data);
52+
for (i = 0; i<argc; i++) {
53+
if (i)
54+
Serial.print((char) '\t');
55+
Serial.printf("%s", azColName[i]);
56+
}
57+
Serial.printf("\n");
58+
first_time = false;
59+
}
60+
for (i = 0; i<argc; i++) {
61+
if (i)
62+
Serial.print((char) '\t');
63+
Serial.printf("%s", argv[i] ? argv[i] : "NULL");
64+
}
65+
Serial.printf("\n");
66+
return 0;
67+
}
68+
69+
int db_open() {
70+
if (db != NULL)
71+
sqlite3_close(db);
72+
int rc = sqlite3_open(db_file_name, &db);
73+
if (rc) {
74+
Serial.print(F("Can't open database: "));
75+
Serial.println(sqlite3_errmsg(db));
76+
return rc;
77+
} else
78+
Serial.println(F("Opened database successfully"));
79+
return rc;
80+
}
81+
82+
char *zErrMsg = 0;
83+
const char* data = "Output:";
84+
int db_exec(const char *sql) {
85+
if (db == NULL) {
86+
Serial.println("No database open");
87+
return 0;
88+
}
89+
first_time = true;
90+
long start = micros();
91+
int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
92+
if (rc != SQLITE_OK) {
93+
Serial.print(F("SQL error: "));
94+
Serial.println(zErrMsg);
95+
sqlite3_free(zErrMsg);
96+
} else
97+
Serial.println(F("Operation done successfully"));
98+
Serial.print(F("Time taken:"));
99+
Serial.print(micros()-start);
100+
Serial.println(F(" us"));
101+
return rc;
102+
}
103+
104+
int input_string(char *str, int max_len) {
105+
max_len--;
106+
int ctr = 0;
107+
str[ctr] = 0;
108+
while (str[ctr] != '\n') {
109+
if (Serial.available()) {
110+
str[ctr] = Serial.read();
111+
if (str[ctr] >= ' ' && str[ctr] <= '~')
112+
ctr++;
113+
if (ctr >= max_len)
114+
break;
115+
}
116+
}
117+
str[ctr] = 0;
118+
Serial.println(str);
119+
}
120+
121+
int input_num() {
122+
char in[20];
123+
int ctr = 0;
124+
in[ctr] = 0;
125+
while (in[ctr] != '\n') {
126+
if (Serial.available()) {
127+
in[ctr] = Serial.read();
128+
if (in[ctr] >= '0' && in[ctr] <= '9')
129+
ctr++;
130+
if (ctr >= sizeof(in))
131+
break;
132+
}
133+
}
134+
in[ctr] = 0;
135+
int ret = atoi(in);
136+
Serial.println(ret);
137+
return ret;
138+
}
139+
140+
void listDir(fs::FS &fs, const char * dirname) {
141+
Serial.print(F("Listing directory: "));
142+
Serial.println(dirname);
143+
File root = fs.open(dirname);
144+
if (!root){
145+
Serial.println(F("Failed to open directory"));
146+
return;
147+
}
148+
if (!root.isDirectory()){
149+
Serial.println("Not a directory");
150+
return;
151+
}
152+
File file = root.openNextFile();
153+
while (file) {
154+
if (file.isDirectory()) {
155+
Serial.print(" Dir : ");
156+
Serial.println(file.name());
157+
} else {
158+
Serial.print(" File: ");
159+
Serial.print(file.name());
160+
Serial.print(" Size: ");
161+
Serial.println(file.size());
162+
}
163+
file = root.openNextFile();
164+
}
165+
}
166+
167+
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
168+
Serial.printf("Renaming file %s to %s\n", path1, path2);
169+
if (fs.rename(path1, path2)) {
170+
Serial.println(F("File renamed"));
171+
} else {
172+
Serial.println(F("Rename failed"));
173+
}
174+
}
175+
176+
void deleteFile(fs::FS &fs, const char *path) {
177+
Serial.printf("Deleting file: %s\n", path);
178+
if (fs.remove(path)) {
179+
Serial.println(F("File deleted"));
180+
} else {
181+
Serial.println(F("Delete failed"));
182+
}
183+
}
184+
185+
enum {CHOICE_OPEN_DB = 1, CHOICE_EXEC_SQL, CHOICE_EXEC_MULTI_SQL, CHOICE_CLOSE_DB,
186+
CHOICE_LIST_FOLDER, CHOICE_RENAME_FILE, CHOICE_DELETE_FILE};
187+
int askChoice() {
188+
Serial.println();
189+
Serial.println(F("Welcome to SQLite console!!"));
190+
Serial.println(F("---------------------------"));
191+
Serial.println();
192+
Serial.print(F("Database file: "));
193+
Serial.println(db_file_name);
194+
Serial.println();
195+
Serial.println(F("1. Open database"));
196+
Serial.println(F("2. Execute SQL"));
197+
Serial.println(F("3. Execute Multiple SQL"));
198+
Serial.println(F("4. Close database"));
199+
Serial.println(F("5. List folder contents"));
200+
Serial.println(F("6. Rename file"));
201+
Serial.println(F("7. Delete file"));
202+
Serial.println();
203+
Serial.print(F("Enter choice: "));
204+
return input_num();
205+
}
206+
207+
void displayPrompt(const char *title) {
208+
Serial.println(F("(prefix /spiffs/ or /sd/ or /sdcard/ for"));
209+
Serial.println(F(" SPIFFS or SD_SPI or SD_MMC respectively)"));
210+
Serial.print(F("Enter "));
211+
Serial.println(title);
212+
}
213+
214+
const char *prefixSPIFFS = "/spiffs/";
215+
const char *prefixSD_SPI = "/sd/";
216+
const char *prefixSD_MMC = "/sdcard/";
217+
fs::FS *ascertainFS(const char *str, int *prefix_len) {
218+
if (strstr(str, prefixSPIFFS) == str) {
219+
*prefix_len = strlen(prefixSPIFFS) - 1;
220+
return &SPIFFS;
221+
}
222+
if (strstr(str, prefixSD_SPI) == str) {
223+
*prefix_len = strlen(prefixSD_SPI) - 1;
224+
return &SD;
225+
}
226+
if (strstr(str, prefixSD_MMC) == str) {
227+
*prefix_len = strlen(prefixSD_MMC) - 1;
228+
return &SD_MMC;
229+
}
230+
return NULL;
231+
}
232+
233+
void setup() {
234+
Serial.begin(115200);
235+
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
236+
Serial.println(F("Failed to mount file Serial"));
237+
return;
238+
}
239+
SPI.begin();
240+
SD_MMC.begin();
241+
SD.begin();
242+
sqlite3_initialize();
243+
}
244+
245+
char str[MAX_STR_LEN];
246+
void loop() {
247+
248+
int choice = askChoice();
249+
switch (choice) {
250+
case CHOICE_OPEN_DB:
251+
displayPrompt("file name: ");
252+
input_string(str, MAX_FILE_NAME_LEN);
253+
if (str[0] != 0) {
254+
strncpy(db_file_name, str, MAX_FILE_NAME_LEN);
255+
db_open();
256+
}
257+
break;
258+
case CHOICE_EXEC_SQL:
259+
Serial.print(F("Enter SQL (max "));
260+
Serial.print(MAX_STR_LEN);
261+
Serial.println(F(" characters):"));
262+
input_string(str, MAX_STR_LEN);
263+
if (str[0] != 0)
264+
db_exec(str);
265+
break;
266+
case CHOICE_EXEC_MULTI_SQL:
267+
Serial.println(F("(Copy paste may not always work due to limited serial buffer)"));
268+
Serial.println(F("Keep entering SQL, empty to stop:"));
269+
do {
270+
input_string(str, MAX_STR_LEN);
271+
if (str[0] != 0)
272+
db_exec(str);
273+
} while (str[0] != 0);
274+
break;
275+
case CHOICE_CLOSE_DB:
276+
if (db_file_name[0] != 0) {
277+
db_file_name[0] = 0;
278+
sqlite3_close(db);
279+
}
280+
break;
281+
case CHOICE_LIST_FOLDER:
282+
case CHOICE_RENAME_FILE:
283+
case CHOICE_DELETE_FILE:
284+
fs::FS *fs;
285+
displayPrompt("path: ");
286+
input_string(str, MAX_STR_LEN);
287+
if (str[0] != 0) {
288+
int fs_prefix_len = 0;
289+
fs = ascertainFS(str, &fs_prefix_len);
290+
if (fs != NULL) {
291+
switch (choice) {
292+
case CHOICE_LIST_FOLDER:
293+
listDir(*fs, str + fs_prefix_len);
294+
break;
295+
case CHOICE_RENAME_FILE:
296+
char str1[MAX_FILE_NAME_LEN];
297+
displayPrompt("path to rename as: ");
298+
input_string(str1, MAX_STR_LEN);
299+
if (str1[0] != 0)
300+
renameFile(*fs, str + fs_prefix_len, str1 + fs_prefix_len);
301+
break;
302+
case CHOICE_DELETE_FILE:
303+
deleteFile(*fs, str + fs_prefix_len);
304+
break;
305+
}
306+
}
307+
}
308+
break;
309+
default:
310+
Serial.println(F("Invalid choice. Try again."));
311+
}
312+
313+
}

0 commit comments

Comments
 (0)