Skip to content

Commit 077315f

Browse files
committed
stackoverflow example
1 parent 8b513a2 commit 077315f

File tree

5 files changed

+323
-0
lines changed

5 files changed

+323
-0
lines changed
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/*
2+
This example shows how to retrieve data from Sqlite3 databases from SD Card
3+
through the Web Server and display in the form of HTML page.
4+
It also demonstrates query filtering by parameter passing and chunked encoding.
5+
Before running please copy 'so_users.db' to SD Card. Please see
6+
7+
https://github.com/siara-cc/stackoverflow_db
8+
9+
to find out how to obtain so_users.db
10+
11+
Please also increase stack size in cores/esp8266/cont.h
12+
to atleast 6144 (from 4096)
13+
14+
Please see https://github.com/siara-cc/esp_arduino_sqlite3_lib/
15+
for more inforemation.
16+
17+
Copyright (c) 2018, Siara Logics (cc)
18+
*/
19+
20+
/*
21+
* Copyright (c) 2015, Majenko Technologies
22+
* All rights reserved.
23+
*
24+
* Redistribution and use in source and binary forms, with or without modification,
25+
* are permitted provided that the following conditions are met:
26+
*
27+
* * Redistributions of source code must retain the above copyright notice, this
28+
* list of conditions and the following disclaimer.
29+
*
30+
* * Redistributions in binary form must reproduce the above copyright notice, this
31+
* list of conditions and the following disclaimer in the documentation and/or
32+
* other materials provided with the distribution.
33+
*
34+
* * Neither the name of Majenko Technologies nor the names of its
35+
* contributors may be used to endorse or promote products derived from
36+
* this software without specific prior written permission.
37+
*
38+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
39+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
40+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
42+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
45+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48+
*/
49+
50+
#include <ESP8266WiFi.h>
51+
#include <WiFiClient.h>
52+
#include <ESP8266WebServer.h>
53+
#include <ESP8266mDNS.h>
54+
#include <sqlite3.h>
55+
#include <vfs.h>
56+
#include <SPI.h>
57+
58+
const char *ssid = "Nokia1";
59+
const char *password = "nokiafour";
60+
61+
ESP8266WebServer server ( 80 );
62+
63+
const int led = 13;
64+
65+
void handleRoot() {
66+
digitalWrite(led, 1);
67+
String temp;
68+
int sec = millis() / 1000;
69+
int min = sec / 60;
70+
int hr = min / 60;
71+
temp = F("<html><head>\
72+
<title>ESP8266 Demo to Query database on Micro SD</title>\
73+
<style>\
74+
body { font-family: Arial, Helvetica, Sans-Serif; font-size: large; Color: #000088; }\
75+
</style>\
76+
</head>\
77+
<body>\
78+
<h1>Hello from ESP8266!</h1>\
79+
<h2>Query StackOverflow Users database on Micro SD Card</h2>\
80+
<p>StackOverflow publishes snapshot of its data periodically at archive.org \
81+
<a href='https://archive.org/download/stackexchange'>here</a> \
82+
and is <a href='https://ia800107.us.archive.org/27/items/stackexchange/license.txt'> \
83+
licensed under cc-by-sa 3.0</a>.</p>\
84+
<p><a href='https://github.com/siara-cc/stackoverflow_db'>This repository</a> \
85+
hosts StackOverflow User data imported into a convenient SQLite database\
86+
(size 1.94 GB and contains close to 10 million records). The date of snapshot is 3-Dec-2018.</p>\
87+
<p>This example shows how to retrieve data from this database copied to Micro SD Card \
88+
attached to ESP8266 through its Web Server and display in the form of HTML page.</p>\
89+
<h3>Query by User Id</h3>\
90+
<form name='params' method='GET' action='query_db'>\
91+
Enter id: <input type=text style='font-size: large' value='5072621' name='so_id'/> \
92+
<input type=hidden value='' name='so_disp_name'/> \
93+
<p>(To find your id, see search box after clicking your profile icon on https://stackoverflow.com)</p>\
94+
<input type=submit style='font-size: large' value='Query database by Id'/>\
95+
</form>\
96+
<hr>\
97+
<h3>Query by Display name</h3>\
98+
<form name='params' method='GET' action='query_db'>\
99+
<input type=hidden value='' name='so_id'/> \
100+
Enter Display name: <input type=text style='font-size: large' value='roadrunner' name='so_disp_name'/> \
101+
<br><br><input type=submit style='font-size: large' value='Query database by Display Name'/>\
102+
</form>\
103+
<hr>\
104+
<h3>Aggregate Query by Location</h3>\
105+
<form name='params' method='GET' action='query_db'>\
106+
<input type=hidden value='' name='so_id'/> \
107+
<input type=hidden value='' name='so_disp_name'/> \
108+
Enter Location (blank for all): \
109+
<input type=text style='font-size: large' value='' name='so_loc'/> \
110+
<br>Minimum count:\
111+
<input type=text style='font-size: large' value='10000' name='so_loc_count'/> \
112+
<br><br><input type=submit style='font-size: large' value='Aggregate Query by Location'/>\
113+
</form>\
114+
<hr>\
115+
</body>\
116+
</html>");
117+
server.send(200, "text/html", temp.c_str());
118+
digitalWrite(led, 0);
119+
}
120+
121+
void handleNotFound() {
122+
digitalWrite(led, 1);
123+
String message = F("File Not Found\n\n");
124+
message += F("URI: ");
125+
message += server.uri();
126+
message += F("\nMethod: ");
127+
message += (server.method() == HTTP_GET) ? "GET" : "POST";
128+
message += F("\nArguments: ");
129+
message += server.args();
130+
message += F("\n");
131+
for ( uint8_t i = 0; i < server.args(); i++ ) {
132+
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
133+
}
134+
server.send(404, "text/plain", message);
135+
digitalWrite(led, 0);
136+
}
137+
138+
sqlite3 *db1;
139+
int rc;
140+
sqlite3_stmt *res;
141+
int rec_count = 0;
142+
const char *tail;
143+
144+
int openDb(const char *filename, sqlite3 **db) {
145+
int rc = sqlite3_open(filename, db);
146+
if (rc) {
147+
Serial.print(F("Can't open database: "));
148+
Serial.println(sqlite3_errmsg(*db));
149+
return rc;
150+
} else {
151+
Serial.println(F("Opened database successfully"));
152+
}
153+
return rc;
154+
}
155+
156+
void setup(void) {
157+
pinMode(led, OUTPUT);
158+
digitalWrite(led, 0);
159+
Serial.begin(74880);
160+
system_update_cpu_freq(SYS_CPU_160MHZ);
161+
WiFi.mode(WIFI_STA);
162+
WiFi.begin(ssid, password);
163+
Serial.println(F("Hello"));
164+
165+
// Wait for connection
166+
while (WiFi.status() != WL_CONNECTED) {
167+
delay(500);
168+
Serial.print(F("."));
169+
}
170+
171+
Serial.println("");
172+
Serial.print(F("Connected to "));
173+
Serial.println(ssid);
174+
Serial.print(F("IP address: "));
175+
Serial.println(WiFi.localIP());
176+
177+
if (MDNS.begin("esp8266")) {
178+
Serial.println(F("MDNS responder started"));
179+
}
180+
181+
SPI.begin();
182+
vfs_mount("/SD0", SS);
183+
184+
sqlite3_initialize();
185+
186+
// Open database
187+
if (openDb("/SD0/so_users.db", &db1))
188+
return;
189+
190+
server.on("/", handleRoot);
191+
server.on("/query_db", []() {
192+
long start = micros();
193+
String sql = F("Select Count(*) From SO_Users Where ");
194+
if (server.arg("so_disp_name").length() > 0) {
195+
sql += F("DisplayName = '");
196+
sql += server.arg("so_disp_name");
197+
sql += "'";
198+
} else if (server.arg("so_id").length() > 0) {
199+
sql += F("Id = '");
200+
sql += server.arg("so_id");
201+
sql += F("'");
202+
} else {
203+
sql = "";
204+
}
205+
int step_res;
206+
if (sql.length() > 0) {
207+
rc = sqlite3_prepare_v2(db1, sql.c_str(), 1000, &res, &tail);
208+
if (rc != SQLITE_OK) {
209+
String resp = F("Failed to fetch data: ");
210+
resp += sqlite3_errmsg(db1);
211+
resp += F(".<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
212+
server.send ( 200, "text/html", resp.c_str());
213+
Serial.println(resp.c_str());
214+
return;
215+
}
216+
do {
217+
step_res = sqlite3_step(res);
218+
if (step_res == SQLITE_ROW) {
219+
rec_count = sqlite3_column_int(res, 0);
220+
if (rec_count > 5000) {
221+
String resp = F("Too many records: ");
222+
resp += rec_count;
223+
resp += F(". Please select different range");
224+
resp += F(".<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
225+
server.send ( 200, "text/html", resp.c_str());
226+
Serial.println(resp.c_str());
227+
sqlite3_finalize(res);
228+
return;
229+
}
230+
}
231+
ESP.wdtFeed();
232+
} while (step_res != SQLITE_DONE && step_res != SQLITE_ERROR);
233+
sqlite3_finalize(res);
234+
} else
235+
rec_count = -1;
236+
237+
sql = F("Select * From SO_Users Where ");
238+
if (server.arg("so_disp_name").length() > 0) {
239+
sql += F("DisplayName = '");
240+
sql += server.arg("so_disp_name");
241+
sql += F("'");
242+
} else if (server.arg("so_id").length() > 0) {
243+
sql += F("Id = '");
244+
sql += server.arg("so_id");
245+
sql += F("'");
246+
} else {
247+
sql = F("Select Location, Count(*) Count From SO_Users ");
248+
if (server.arg("so_loc").length() > 0) {
249+
sql += F("Where Location = '");
250+
sql += server.arg("so_loc");
251+
sql += F("' ");
252+
} else
253+
sql += F("Where Location > '' ");
254+
sql += F("Group by Location ");
255+
if (server.arg("so_loc_count").length() > 0) {
256+
sql += F("Having Count(*) >= ");
257+
sql += server.arg("so_loc_count");
258+
}
259+
}
260+
rc = sqlite3_prepare_v2(db1, sql.c_str(), 1000, &res, &tail);
261+
if (rc != SQLITE_OK) {
262+
String resp = F("Failed to fetch data: ");
263+
resp += sqlite3_errmsg(db1);
264+
resp += F("<br><br><a href='/'>back</a>");
265+
server.send ( 200, "text/html", resp.c_str());
266+
Serial.println(resp.c_str());
267+
return;
268+
}
269+
270+
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
271+
String resp = F("<!DOCTYPE html><html><head>\
272+
<title>StackOverflow Database query on ESP8266 through web server</title>\
273+
<style>\
274+
body { font-family: Arial, Helvetica, Sans-Serif; font-size: large; Color: #000088; }\
275+
</style><head><body><h1>Query StackOverflow Users db on Micro SD card attached to ESP8266 through its web server</h1><h3>");
276+
resp += sql;
277+
resp += F("</h3>");
278+
if (rec_count >= 0) {
279+
resp += F("<p>No. of records: ");
280+
resp += rec_count;
281+
resp += F("</p>");
282+
}
283+
resp += F("<table cellspacing='1' cellpadding='1' border='1'>");
284+
server.send(200, "text/html", resp.c_str());
285+
int cols = sqlite3_column_count(res);
286+
resp = F("<tr>");
287+
for (int i = 0; i < cols; i++) {
288+
resp += F("<td>");
289+
resp += (const char *) sqlite3_column_name(res, i);
290+
resp += F("</td>");
291+
}
292+
resp += F("</tr>");
293+
server.sendContent(resp);
294+
do {
295+
step_res = sqlite3_step(res);
296+
if (step_res == SQLITE_ROW) {
297+
resp = F("<tr>");
298+
for (int i = 0; i < cols; i++) {
299+
resp += F("<td>");
300+
resp += (const char *) sqlite3_column_text(res, i);
301+
resp += F("</td>");
302+
}
303+
resp += F("</tr>");
304+
server.sendContent(resp);
305+
rec_count++;
306+
}
307+
ESP.wdtFeed();
308+
} while (step_res != SQLITE_DONE && step_res != SQLITE_ERROR);
309+
resp = F("</table>");
310+
resp += F("<br>Time taken (seconds): ");
311+
resp += (micros()-start)/1000000;
312+
resp += F("<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
313+
server.sendContent(resp);
314+
sqlite3_finalize(res);
315+
});
316+
server.onNotFound(handleNotFound);
317+
server.begin();
318+
Serial.println(F("HTTP server started"));
319+
}
320+
321+
void loop ( void ) {
322+
server.handleClient();
323+
}

output_web_so.png

156 KB
Loading

output_web_so_id.png

112 KB
Loading

output_web_so_loc.png

176 KB
Loading

output_web_so_name.png

224 KB
Loading

0 commit comments

Comments
 (0)