Skip to content

Commit 0cfaf72

Browse files
committed
Configuration over web added instead of using hardcoded variables
1 parent 314dfd2 commit 0cfaf72

File tree

8 files changed

+322
-69
lines changed

8 files changed

+322
-69
lines changed

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
<p align="center">
44
<img src="photos/onwifi.png" alt="E-paper device showing C++ quiz" width="400"/>
5+
<img src="photos/config.png" alt="E-paper device showing C++ quiz" width="400"/>
6+
<img src="photos/savedconfig.png" alt="E-paper device showing C++ quiz" width="400"/>
57
</p>
68

79
## 🚀 Features
@@ -15,14 +17,13 @@
1517

1618
## 📦 Hardware
1719

18-
- ESP32
1920
- Waveshare ESP32 Epaper Driver Board
2021
- Waveshare 7.5" E-Paper Display
2122

2223

2324
## 🧠 How It Works
2425

25-
- ESP32 boots and connects to Wi-Fi.
26+
- ESP32 boots and connects to Wi-Fi or creating AP to grab configurations at 192.168.4.1
2627
- It sends an HTTP request to a Google Apps Script endpoint.
2728
- The Apps Script reads a unshown question from Google Sheets and returns it as plain text.
2829
- ESP32 displays the question on the e-paper screen.
@@ -32,7 +33,7 @@
3233

3334
## 🎉 To Build Yourself
3435

35-
- ### Software
36+
- ### Software on Cloud
3637

3738
- Upload an Excel file ([questions.xlsx](cppquiz_questions.xlsx)) to Google Drive
3839
- Open it with Google Sheets
@@ -48,16 +49,23 @@
4849
```
4950

5051

51-
- ### Hardware
52+
- ### Sofware on ESP32
53+
54+
That one is optional, because of once wifi connection is failed, ESP32 will create Access Point.
5255

5356
Update `config.h` ([config.h](include/config.h))on ESP32 firmware:
5457

5558
```cpp
56-
const std::string WIFI_SSID = "YOUR_WIFI_SSID";
57-
const std::string WIFI_PWD = "YOUR_WIFI_PWD"
58-
const std::string GS_DEPLOYMENT_ID = "YOUR_GS_DEPLOYMENT_ID"
59+
const std::string wifi_ssid = "YOUR_WIFI_SSID";
60+
const std::string wifi_pwd = "YOUR_WIFI_PWD"
61+
const std::string gs_deployment_id = "YOUR_GS_DEPLOYMENT_ID"
5962
```
6063

64+
65+
- ### 3D Case
66+
67+
You can find some many different model to print from online 3d websites.
68+
6169
## 📄 Why Use an Excel File?
6270

6371
The questions displayed on this device are sourced from [cppquiz.org](https://cppquiz.org/), which provides a comprehensive collection of C++ questions. However, the site does not offer an API to fetch individual questions by ID.

include/config.h

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,33 @@
55

66
#define WS_BOARD
77

8-
const uint8_t PIN_EPD_BUSY = 25;
9-
const uint8_t PIN_EPD_CS = 15;
10-
const uint8_t PIN_EPD_RST = 26;
11-
const uint8_t PIN_EPD_DC = 27;
12-
13-
// WiFi credentials
14-
const std::string WIFI_SSID = "YOUR_WIFI_SSID";
15-
const std::string WIFI_PWD = "YOUR_WIFI_PWD";
8+
constexpr uint8_t PIN_EPD_BUSY = 25;
9+
constexpr uint8_t PIN_EPD_CS = 15;
10+
constexpr uint8_t PIN_EPD_RST = 26;
11+
constexpr uint8_t PIN_EPD_DC = 27;
12+
13+
// WiFi credentials defined as RTC_DATA_ATTR, with this trick, these credentials will be available after wakes up from deepsleep.
14+
// It is up to you, if you keep these guys as dummy, ESP32 will create a server to allow you enter credentials
15+
// after failed wifi connection. If you update, dont forget to update gs_deployment_id as well.
16+
// Don't change from char to String or std::string
17+
RTC_DATA_ATTR char wifi_ssid[32] = "YOUR_WIFI_SSID";
18+
RTC_DATA_ATTR char wifi_pwd[64] = "YOUR_WIFI_PWD";
1619

1720
// Google Apps Script Deployment ID
18-
const std::string GS_DEPLOYMENT_ID = "YOUR_GS_DEPLOYMENT_ID";
21+
RTC_DATA_ATTR char gs_deployment_id[128] = "YOUR_GS_DEPLOYMENT_ID";
22+
1923
// Base URL for accessing the Google Apps Script
20-
const std::string BASE_URL = "https://script.google.com/macros/s/" + GS_DEPLOYMENT_ID + "/exec?read=";
24+
const std::string BASE_URL = "https://script.google.com/macros/s/" + std::string{gs_deployment_id} + "/exec?read=";
2125

2226
// Query parameter for requesting a question
2327
const std::string QUESTION_TAG = "question";
2428
// Final URL used to fetch a question
2529
const std::string QUESTION_URL = BASE_URL + QUESTION_TAG;
2630

2731
// Sleep duration in us
28-
constexpr uint64_t SLEEP_DURATION_US = 60ULL * 1000'000 * 60;
32+
constexpr uint64_t SLEEP_DURATION_US = 60ULL * 1000'000 * 60;
2933

34+
// CONFIGURATION_TIMEOUT in ms
35+
constexpr uint32_t CONFIGURATION_TIMEOUT = 30UL * 1000 * 60;
3036

3137
#endif

include/display_utils.h

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
#include "fonts/UbuntuMono_R.h"
1010
#include "config.h"
1111

12-
// Macro to disallow copying and assignment for the Display class
12+
// Macro to disallow copying and assignment for the DisplayQuiz class
1313
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
1414
TypeName(const TypeName&) = delete; \
1515
void operator=(const TypeName&) = delete;
1616

17-
// Generic Display class template for rendering content to an e-ink display
17+
// Generic DisplayQuiz class template for rendering content to an e-ink display
1818
template <typename DisplayType>
19-
class Display
19+
class DisplayQuiz
2020
{
2121
public:
2222

2323
// Constructor initializes display and SPI if on WS_BOARD
24-
Display()
24+
DisplayQuiz()
2525
: m_display(DisplayType(PIN_EPD_CS, PIN_EPD_DC, PIN_EPD_RST, PIN_EPD_BUSY))
2626
#ifdef WS_BOARD
2727
, m_hspi(HSPI)
@@ -37,25 +37,24 @@ class Display
3737
m_display.epd2.selectSPI(m_hspi, SPISettings(4000000, MSBFIRST, SPI_MODE0));
3838
#endif
3939
m_display.init(115200);
40-
m_display.setRotation(1);
40+
m_display.setRotation(3);
4141
m_display.setTextSize(1);
4242
m_display.setTextColor(GxEPD_BLACK);
4343
m_display.fillScreen(GxEPD_WHITE);
4444
}
4545

4646
// Renders the full display including header, question, WiFi icon, and content
47-
void render(const String& ret, const bool connection_status)
47+
void render(const std::string& ret, const bool connection_status)
4848
{
49-
printHeader(); // Display the title
50-
printQuestion(); // Display the prompt question
49+
printHeader();
5150

5251
// Display WiFi icon depending on connection status
5352
displayBitmap(connection_status ? wifi_on_48x48 : wifi_off_48x48);
5453

55-
// Display the question content
54+
// Print the question content
5655
m_display.setFont(&UbuntuMono_R_12pt7b);
5756
m_display.setCursor(0, 150);
58-
m_display.print(ret);
57+
m_display.print(ret.c_str());
5958

6059
m_display.display();
6160
m_display.powerOff();
@@ -80,11 +79,7 @@ class Display
8079
uint16_t x = ((m_display.width() - tbw) / 2) - tbx;
8180
m_display.setCursor(x, tbh);
8281
m_display.print(Header);
83-
}
8482

85-
// Displays the static question prompt
86-
void printQuestion()
87-
{
8883
m_display.setFont(&FreeSans_12pt7b);
8984
const char Question[] = "According to the C++23 standard, what is the output of this program?";
9085
m_display.setCursor(0, 75);
@@ -98,7 +93,7 @@ class Display
9893
#endif
9994
EinkDisplay m_display; // E-ink display driver
10095

101-
DISALLOW_COPY_AND_ASSIGN(Display)
96+
DISALLOW_COPY_AND_ASSIGN(DisplayQuiz)
10297
};
10398

10499
#endif

include/html.h

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#ifndef HTML_H
2+
#define HTML_H
3+
4+
#include <Arduino.h>
5+
6+
const char afterConfigurationSaved[] PROGMEM = R"rawliteral(
7+
<!DOCTYPE html>
8+
<html lang="en">
9+
<head>
10+
<meta charset="UTF-8" />
11+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
12+
<title>Contrib Config Saved</title>
13+
</head>
14+
<body
15+
style="
16+
font-family: 'Courier New', Courier, monospace;
17+
background-color: #1e1e2e;
18+
color: #ffffff;
19+
margin: 0;
20+
display: flex;
21+
justify-content: center;
22+
align-items: center;
23+
height: 100vh;
24+
text-align: center;
25+
"
26+
>
27+
<div
28+
style="
29+
background-color: #2e2e3e;
30+
padding: 2rem;
31+
margin: 2rem;
32+
margin-top: 0;
33+
border-radius: 8px;
34+
width: 90%;
35+
max-width: 350px;
36+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
37+
"
38+
>
39+
<h1 style="color: #fff; margin-top: 3px; margin-bottom: 5px">
40+
Config Saved
41+
</h1>
42+
<p>The device will now restart with the new configuration</p>
43+
</div>
44+
</body>
45+
</html>
46+
)rawliteral";
47+
48+
49+
const char configurationPage[] PROGMEM = R"rawliteral(
50+
<!DOCTYPE html>
51+
<html lang="en">
52+
<head>
53+
<meta charset="UTF-8" />
54+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
55+
<title>WiFi Config</title>
56+
<style>
57+
body {
58+
margin: 0;
59+
padding: 0;
60+
font-family: monospace;
61+
background: linear-gradient(135deg, #1e1e2e, #2a2a3d);
62+
color: #ffffff;
63+
display: flex;
64+
justify-content: center;
65+
align-items: center;
66+
height: 100vh;
67+
}
68+
69+
.container {
70+
background-color: #2e2e3e;
71+
padding: 2rem;
72+
border-radius: 12px;
73+
width: 90%;
74+
max-width: 420px;
75+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
76+
animation: fadeIn 0.6s ease-in-out;
77+
}
78+
79+
@keyframes fadeIn {
80+
from {
81+
opacity: 0;
82+
transform: translateY(20px);
83+
}
84+
to {
85+
opacity: 1;
86+
transform: translateY(0);
87+
}
88+
}
89+
90+
pre {
91+
font-size: 0.6rem;
92+
text-align: center;
93+
color: #ccc;
94+
margin-bottom: 1rem;
95+
line-height: 1rem;
96+
white-space: pre-line;
97+
}
98+
99+
h2 {
100+
text-align: center;
101+
margin-bottom: 1.5rem;
102+
font-size: 1.2rem;
103+
color: #ffcc70;
104+
}
105+
106+
label {
107+
font-size: 0.7rem;
108+
color: #bbb;
109+
margin-top: 20px;
110+
display: block;
111+
text-transform: uppercase;
112+
letter-spacing: 0.05em;
113+
}
114+
115+
input {
116+
width: 100%;
117+
padding: 0.75rem;
118+
margin-top: 5px;
119+
border: none;
120+
border-radius: 6px;
121+
background-color: #3e3e4e;
122+
color: #fff;
123+
font-size: 0.95rem;
124+
transition: box-shadow 0.3s;
125+
}
126+
127+
input:focus {
128+
outline: none;
129+
box-shadow: 0 0 5px 2px #368db2;
130+
}
131+
132+
input::placeholder {
133+
color: #aaa;
134+
}
135+
136+
button {
137+
width: 100%;
138+
padding: 0.9rem;
139+
margin-top: 30px;
140+
font-size: 1rem;
141+
border: none;
142+
border-radius: 6px;
143+
background-color: #4caf50;
144+
color: white;
145+
cursor: pointer;
146+
transition: all 0.3s ease;
147+
}
148+
149+
button:hover {
150+
background-color: #45a045;
151+
transform: translateY(-2px);
152+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
153+
}
154+
</style>
155+
</head>
156+
<body>
157+
<div class="container">
158+
<pre>
159+
C P P Q u i z O n E i n k
160+
</pre>
161+
<h2>WiFi Configuration</h2>
162+
<form action="/submit" method="POST">
163+
<label for="wifi-ssid">WiFi SSID</label>
164+
<input type="text" name="wifi-ssid" placeholder="Enter SSID" required />
165+
166+
<label for="wifi-password">WiFi Password</label>
167+
<input type="password" name="wifi-password" placeholder="Enter Password" required />
168+
169+
<label for="gs-id">Google Sheet Id</label>
170+
<input type="text" name="gs-id" placeholder="Enter Google Sheet Id" required />
171+
172+
<button type="submit">Save Configuration Settings</button>
173+
</form>
174+
</div>
175+
</body>
176+
</html>
177+
)rawliteral";
178+
179+
#endif

0 commit comments

Comments
 (0)