Skip to content

Commit 5c42f60

Browse files
committed
config: brightness contrast saturation
1 parent 12ca367 commit 5c42f60

File tree

3 files changed

+79
-23
lines changed

3 files changed

+79
-23
lines changed

examples/AsyncCam/handlers.cpp

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ static const char FRONTPAGE[] = R"EOT(
88
<h1>esp32cam AsyncCam example</h1>
99
<form id="update"><p>
1010
<select name="resolution" required>%resolution%</select>
11+
%brightness%
12+
%contrast%
13+
%saturation%
1114
%hmirror%
1215
%vflip%
1316
<input type="submit" value="update">
@@ -28,18 +31,20 @@ async function fetchText(uri, init) {
2831
return (await response.text()).trim().replaceAll("\r\n", "\n");
2932
}
3033
34+
const $update = document.querySelector("#update");
3135
const $display = document.querySelector("#display");
32-
document.querySelector("#update").addEventListener("submit", async (evt) => {
36+
$update.addEventListener("submit", async (evt) => {
3337
evt.preventDefault();
3438
try {
3539
await fetchText("/update.cgi", {
3640
method: "POST",
37-
body: new URLSearchParams(new FormData(evt.target)),
41+
body: new URLSearchParams(new FormData($update)),
3842
});
3943
} catch (err) {
4044
$display.textContent = err.toString();
4145
}
4246
});
47+
$update.reset();
4348
4449
for (const $ctrl of document.querySelectorAll("#controls button")) {
4550
$ctrl.addEventListener("click", (evt) => {
@@ -62,61 +67,84 @@ for (const $ctrl of document.querySelectorAll("#controls button")) {
6267
)EOT";
6368

6469
static String
65-
rewriteFrontpage(const esp32cam::Settings& settings, const String& var) {
70+
rewriteFrontpage(const esp32cam::Settings& s, const String& var) {
6671
StreamString b;
6772

6873
if (var == "resolution") {
6974
for (const auto& r : esp32cam::Camera.listResolutions()) {
7075
b.printf("<option value=\"%d\"%s>", r.as<int>(),
71-
r > initialResolution ? " disabled"
72-
: r == settings.resolution ? " selected"
73-
: "");
76+
r > initialResolution ? " disabled"
77+
: r == s.resolution ? " selected"
78+
: "");
7479
b.print(r);
7580
b.print("</option>");
7681
}
7782
}
7883

84+
#define SETTING_INT(MEM, MIN, MAX) \
85+
else if (var == #MEM) { \
86+
b.printf("<label>" #MEM "=<input type=\"number\" name=\"" #MEM \
87+
"\" value=\"%d\" min=\"%d\" max=\"%d\"></label>", \
88+
s.MEM, MIN, MAX); \
89+
}
90+
7991
#define SETTING_BOOL(MEM) \
8092
else if (var == #MEM) { \
8193
b.printf("<label><input type=\"checkbox\" name=\"" #MEM "\" value=\"1\"%s>" #MEM "</label>", \
82-
settings.MEM ? " checked" : ""); \
94+
s.MEM ? " checked" : ""); \
8395
}
8496

97+
SETTING_INT(brightness, -2, 2)
98+
SETTING_INT(contrast, -2, 2)
99+
SETTING_INT(saturation, -2, 2)
85100
SETTING_BOOL(hmirror)
86101
SETTING_BOOL(vflip)
87102

103+
#undef SETTING_INT
88104
#undef SETTING_BOOL
89105

90106
return b;
91107
}
92108

93109
static void
94-
handleFrontpage(AsyncWebServerRequest* request) {
110+
handleFrontpage(AsyncWebServerRequest* req) {
95111
auto settings = esp32cam::Camera.status();
96-
request->send(200, "text/html", reinterpret_cast<const uint8_t*>(FRONTPAGE), sizeof(FRONTPAGE),
97-
[=](const String& var) { return rewriteFrontpage(settings, var); });
112+
req->send(200, "text/html", reinterpret_cast<const uint8_t*>(FRONTPAGE), sizeof(FRONTPAGE),
113+
[=](const String& var) { return rewriteFrontpage(settings, var); });
98114
}
99115

100116
static void
101-
handleUpdate(AsyncWebServerRequest* request) {
102-
bool ok = esp32cam::Camera.update([=](esp32cam::Settings& settings) {
103-
settings.resolution =
104-
esp32cam::Resolution(static_cast<int>(request->arg("resolution").toInt()));
105-
settings.hmirror = request->arg("hmirror").toInt() != 0;
106-
settings.vflip = request->arg("vflip").toInt() != 0;
117+
handleUpdate(AsyncWebServerRequest* req) {
118+
bool ok = esp32cam::Camera.update([=](esp32cam::Settings& s) {
119+
#define UPDATE(MEM) \
120+
do { \
121+
if constexpr (std::is_same_v<decltype(s.MEM), bool>) { \
122+
s.MEM = req->hasArg(#MEM); \
123+
} else { \
124+
s.MEM = static_cast<decltype(s.MEM)>(req->arg(#MEM).toInt()); \
125+
} \
126+
} while (false)
127+
s.resolution = esp32cam::Resolution(static_cast<int>(req->arg("resolution").toInt()));
128+
UPDATE(brightness);
129+
UPDATE(contrast);
130+
UPDATE(saturation);
131+
UPDATE(hmirror);
132+
UPDATE(vflip);
133+
134+
#undef UPDATE
107135
});
108136

109137
if (!ok) {
110-
request->send(500, "text/plain", "update settings error\n");
138+
req->send(500, "text/plain", "update settings error\n");
111139
return;
112140
}
113-
request->send(204);
141+
req->send(204);
114142
}
115143

116144
void
117145
addRequestHandlers() {
118-
server.on("/robots.txt", HTTP_GET, [](AsyncWebServerRequest* request) {
119-
request->send(200, "text/plain", "User-Agent: *\nDisallow: /\n");
146+
server.on("/robots.txt", HTTP_GET, [](AsyncWebServerRequest* req) {
147+
req->send(200, "text/plain", "User-Agent: *\nDisallow: /\n");
120148
});
121149

122150
server.on("/", HTTP_GET, handleFrontpage);

src/esp32cam/config.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ CameraClass::status() const {
101101
}
102102

103103
result.resolution = Resolution(sensor->status.framesize);
104+
result.brightness = sensor->status.brightness;
105+
result.contrast = sensor->status.contrast;
106+
result.saturation = sensor->status.saturation;
104107
result.hmirror = sensor->status.hmirror != 0;
105108
result.vflip = sensor->status.vflip != 0;
106109
return result;
@@ -113,10 +116,19 @@ CameraClass::update(const Settings& settings, int sleepFor) {
113116
return false;
114117
}
115118

119+
#define CHECK_RANGE(MEM, MIN, MAX) \
120+
do { \
121+
if (!(settings.MEM >= MIN && settings.MEM <= MAX)) { \
122+
ESP32CAM_LOG("update " #MEM " %d out of range [%d,%d]", static_cast<int>(settings.MEM), MIN, \
123+
MAX); \
124+
return false; \
125+
} \
126+
} while (false)
127+
116128
#define UPDATE(STATUS_MEM, SETTING_MEM, SETTER_TYP) \
117129
do { \
118130
auto prev = sensor->status.STATUS_MEM; \
119-
if (prev != static_cast<decltype(sensor->status.STATUS_MEM)>(settings.SETTING_MEM)) { \
131+
if (prev != static_cast<decltype(prev)>(settings.SETTING_MEM)) { \
120132
int res = sensor->set_##STATUS_MEM(sensor, static_cast<SETTER_TYP>(settings.SETTING_MEM)); \
121133
ESP32CAM_LOG("update " #STATUS_MEM " %d => %d %s", static_cast<int>(prev), \
122134
static_cast<int>(settings.SETTING_MEM), res == 0 ? "success" : "failure"); \
@@ -125,13 +137,20 @@ CameraClass::update(const Settings& settings, int sleepFor) {
125137
} \
126138
} \
127139
} while (false)
128-
129140
#define UPDATE1(MEM) UPDATE(MEM, MEM, int)
130141

142+
CHECK_RANGE(brightness, -2, 2);
143+
CHECK_RANGE(contrast, -2, 2);
144+
CHECK_RANGE(saturation, -2, 2);
145+
131146
UPDATE(framesize, resolution.as<framesize_t>(), framesize_t);
147+
UPDATE1(brightness);
148+
UPDATE1(contrast);
149+
UPDATE1(saturation);
132150
UPDATE1(hmirror);
133151
UPDATE1(vflip);
134152

153+
#undef CHECK_RANGE
135154
#undef UPDATE
136155
#undef UPDATE1
137156

src/esp32cam/config.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ convertJpegQuality(int quality);
1313

1414
} // namespace detail
1515

16-
/** @brief Camera configuration. */
16+
/** @brief Camera initialization configuration. */
1717
class Config {
1818
public:
1919
Config();
@@ -57,6 +57,15 @@ struct Settings {
5757
/** @brief Picture resolution. */
5858
Resolution resolution;
5959

60+
/** @brief Image brightness, between -2 and +2. */
61+
int8_t brightness;
62+
63+
/** @brief Image contrast, between -2 and +2. */
64+
int8_t contrast;
65+
66+
/** @brief Image saturation, between -2 and +2. */
67+
int8_t saturation;
68+
6069
/** @brief Horizontal flip. */
6170
bool hmirror = false;
6271

0 commit comments

Comments
 (0)