Skip to content

Commit

Permalink
config: brightness contrast saturation
Browse files Browse the repository at this point in the history
  • Loading branch information
yoursunny committed Jan 7, 2025
1 parent 12ca367 commit 5c42f60
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 23 deletions.
68 changes: 48 additions & 20 deletions examples/AsyncCam/handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ static const char FRONTPAGE[] = R"EOT(
<h1>esp32cam AsyncCam example</h1>
<form id="update"><p>
<select name="resolution" required>%resolution%</select>
%brightness%
%contrast%
%saturation%
%hmirror%
%vflip%
<input type="submit" value="update">
Expand All @@ -28,18 +31,20 @@ async function fetchText(uri, init) {
return (await response.text()).trim().replaceAll("\r\n", "\n");
}
const $update = document.querySelector("#update");
const $display = document.querySelector("#display");
document.querySelector("#update").addEventListener("submit", async (evt) => {
$update.addEventListener("submit", async (evt) => {
evt.preventDefault();
try {
await fetchText("/update.cgi", {
method: "POST",
body: new URLSearchParams(new FormData(evt.target)),
body: new URLSearchParams(new FormData($update)),
});
} catch (err) {
$display.textContent = err.toString();
}
});
$update.reset();
for (const $ctrl of document.querySelectorAll("#controls button")) {
$ctrl.addEventListener("click", (evt) => {
Expand All @@ -62,61 +67,84 @@ for (const $ctrl of document.querySelectorAll("#controls button")) {
)EOT";

static String
rewriteFrontpage(const esp32cam::Settings& settings, const String& var) {
rewriteFrontpage(const esp32cam::Settings& s, const String& var) {
StreamString b;

if (var == "resolution") {
for (const auto& r : esp32cam::Camera.listResolutions()) {
b.printf("<option value=\"%d\"%s>", r.as<int>(),
r > initialResolution ? " disabled"
: r == settings.resolution ? " selected"
: "");
r > initialResolution ? " disabled"
: r == s.resolution ? " selected"
: "");
b.print(r);
b.print("</option>");
}
}

#define SETTING_INT(MEM, MIN, MAX) \
else if (var == #MEM) { \
b.printf("<label>" #MEM "=<input type=\"number\" name=\"" #MEM \
"\" value=\"%d\" min=\"%d\" max=\"%d\"></label>", \
s.MEM, MIN, MAX); \
}

#define SETTING_BOOL(MEM) \
else if (var == #MEM) { \
b.printf("<label><input type=\"checkbox\" name=\"" #MEM "\" value=\"1\"%s>" #MEM "</label>", \
settings.MEM ? " checked" : ""); \
s.MEM ? " checked" : ""); \
}

SETTING_INT(brightness, -2, 2)
SETTING_INT(contrast, -2, 2)
SETTING_INT(saturation, -2, 2)
SETTING_BOOL(hmirror)
SETTING_BOOL(vflip)

#undef SETTING_INT
#undef SETTING_BOOL

return b;
}

static void
handleFrontpage(AsyncWebServerRequest* request) {
handleFrontpage(AsyncWebServerRequest* req) {
auto settings = esp32cam::Camera.status();
request->send(200, "text/html", reinterpret_cast<const uint8_t*>(FRONTPAGE), sizeof(FRONTPAGE),
[=](const String& var) { return rewriteFrontpage(settings, var); });
req->send(200, "text/html", reinterpret_cast<const uint8_t*>(FRONTPAGE), sizeof(FRONTPAGE),
[=](const String& var) { return rewriteFrontpage(settings, var); });
}

static void
handleUpdate(AsyncWebServerRequest* request) {
bool ok = esp32cam::Camera.update([=](esp32cam::Settings& settings) {
settings.resolution =
esp32cam::Resolution(static_cast<int>(request->arg("resolution").toInt()));
settings.hmirror = request->arg("hmirror").toInt() != 0;
settings.vflip = request->arg("vflip").toInt() != 0;
handleUpdate(AsyncWebServerRequest* req) {
bool ok = esp32cam::Camera.update([=](esp32cam::Settings& s) {
#define UPDATE(MEM) \
do { \
if constexpr (std::is_same_v<decltype(s.MEM), bool>) { \
s.MEM = req->hasArg(#MEM); \
} else { \
s.MEM = static_cast<decltype(s.MEM)>(req->arg(#MEM).toInt()); \
} \
} while (false)
s.resolution = esp32cam::Resolution(static_cast<int>(req->arg("resolution").toInt()));
UPDATE(brightness);
UPDATE(contrast);
UPDATE(saturation);
UPDATE(hmirror);
UPDATE(vflip);

#undef UPDATE
});

if (!ok) {
request->send(500, "text/plain", "update settings error\n");
req->send(500, "text/plain", "update settings error\n");
return;
}
request->send(204);
req->send(204);
}

void
addRequestHandlers() {
server.on("/robots.txt", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/plain", "User-Agent: *\nDisallow: /\n");
server.on("/robots.txt", HTTP_GET, [](AsyncWebServerRequest* req) {
req->send(200, "text/plain", "User-Agent: *\nDisallow: /\n");
});

server.on("/", HTTP_GET, handleFrontpage);
Expand Down
23 changes: 21 additions & 2 deletions src/esp32cam/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ CameraClass::status() const {
}

result.resolution = Resolution(sensor->status.framesize);
result.brightness = sensor->status.brightness;
result.contrast = sensor->status.contrast;
result.saturation = sensor->status.saturation;
result.hmirror = sensor->status.hmirror != 0;
result.vflip = sensor->status.vflip != 0;
return result;
Expand All @@ -113,10 +116,19 @@ CameraClass::update(const Settings& settings, int sleepFor) {
return false;
}

#define CHECK_RANGE(MEM, MIN, MAX) \
do { \
if (!(settings.MEM >= MIN && settings.MEM <= MAX)) { \
ESP32CAM_LOG("update " #MEM " %d out of range [%d,%d]", static_cast<int>(settings.MEM), MIN, \
MAX); \
return false; \
} \
} while (false)

#define UPDATE(STATUS_MEM, SETTING_MEM, SETTER_TYP) \
do { \
auto prev = sensor->status.STATUS_MEM; \
if (prev != static_cast<decltype(sensor->status.STATUS_MEM)>(settings.SETTING_MEM)) { \
if (prev != static_cast<decltype(prev)>(settings.SETTING_MEM)) { \
int res = sensor->set_##STATUS_MEM(sensor, static_cast<SETTER_TYP>(settings.SETTING_MEM)); \
ESP32CAM_LOG("update " #STATUS_MEM " %d => %d %s", static_cast<int>(prev), \
static_cast<int>(settings.SETTING_MEM), res == 0 ? "success" : "failure"); \
Expand All @@ -125,13 +137,20 @@ CameraClass::update(const Settings& settings, int sleepFor) {
} \
} \
} while (false)

#define UPDATE1(MEM) UPDATE(MEM, MEM, int)

CHECK_RANGE(brightness, -2, 2);
CHECK_RANGE(contrast, -2, 2);
CHECK_RANGE(saturation, -2, 2);

UPDATE(framesize, resolution.as<framesize_t>(), framesize_t);
UPDATE1(brightness);
UPDATE1(contrast);
UPDATE1(saturation);
UPDATE1(hmirror);
UPDATE1(vflip);

#undef CHECK_RANGE
#undef UPDATE
#undef UPDATE1

Expand Down
11 changes: 10 additions & 1 deletion src/esp32cam/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ convertJpegQuality(int quality);

} // namespace detail

/** @brief Camera configuration. */
/** @brief Camera initialization configuration. */
class Config {
public:
Config();
Expand Down Expand Up @@ -57,6 +57,15 @@ struct Settings {
/** @brief Picture resolution. */
Resolution resolution;

/** @brief Image brightness, between -2 and +2. */
int8_t brightness;

/** @brief Image contrast, between -2 and +2. */
int8_t contrast;

/** @brief Image saturation, between -2 and +2. */
int8_t saturation;

/** @brief Horizontal flip. */
bool hmirror = false;

Expand Down

0 comments on commit 5c42f60

Please sign in to comment.