📷 Adds camera service
This commit is contained in:
@@ -155,3 +155,17 @@ export type MQTTSettings = {
|
|||||||
keep_alive: number;
|
keep_alive: number;
|
||||||
clean_session: boolean;
|
clean_session: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CameraSettings = {
|
||||||
|
framesize: number;
|
||||||
|
quality: number;
|
||||||
|
brightness: number;
|
||||||
|
contrast: number;
|
||||||
|
saturation: number;
|
||||||
|
sharpness: number;
|
||||||
|
denoise: number;
|
||||||
|
special_effect: number;
|
||||||
|
wb_mode: number;
|
||||||
|
vflip: boolean;
|
||||||
|
hmirror: boolean;
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
import Users from '~icons/mdi/users';
|
import Users from '~icons/mdi/users';
|
||||||
import Settings from '~icons/mdi/settings';
|
import Settings from '~icons/mdi/settings';
|
||||||
import MdiController from '~icons/mdi/controller';
|
import MdiController from '~icons/mdi/controller';
|
||||||
|
import Devices from '~icons/mdi/devices'
|
||||||
|
import Camera from '~icons/mdi/camera-outline';
|
||||||
import Health from '~icons/mdi/stethoscope';
|
import Health from '~icons/mdi/stethoscope';
|
||||||
import Folder from '~icons/mdi/folder-outline';
|
import Folder from '~icons/mdi/folder-outline';
|
||||||
import Update from '~icons/mdi/reload';
|
import Update from '~icons/mdi/reload';
|
||||||
@@ -51,6 +53,19 @@
|
|||||||
href: '/controller',
|
href: '/controller',
|
||||||
feature: true,
|
feature: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Peripherals',
|
||||||
|
icon: Devices,
|
||||||
|
feature: true,
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
title: 'Camera',
|
||||||
|
icon: Camera,
|
||||||
|
href: '/peripherals/camera',
|
||||||
|
feature: $page.data.features.camera,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Connections',
|
title: 'Connections',
|
||||||
icon: Remote,
|
icon: Remote,
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
export const load = (async () => {
|
||||||
|
goto('/');
|
||||||
|
return;
|
||||||
|
}) satisfies PageLoad;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Camera from './Camera.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mx-0 my-1 flex flex-col space-y-4 sm:mx-8 sm:my-8">
|
||||||
|
<Camera />
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
|
export const load = (async () => {
|
||||||
|
return {
|
||||||
|
title: 'Camera'
|
||||||
|
};
|
||||||
|
}) satisfies PageLoad;
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { user } from '$lib/stores/user';
|
||||||
|
import SettingsCard from "$lib/components/SettingsCard.svelte";
|
||||||
|
import Camera from '~icons/mdi/camera-outline'
|
||||||
|
import VideoCamera from '~icons/mdi/videocam-outline'
|
||||||
|
import Reload from '~icons/mdi/reload'
|
||||||
|
import Record from '~icons/mdi/radio-button-unchecked'
|
||||||
|
import Recording from '~icons/mdi/radio-button-checked'
|
||||||
|
|
||||||
|
import Spinner from '$lib/components/Spinner.svelte';
|
||||||
|
import CameraSetting from './CameraSetting.svelte';
|
||||||
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
|
||||||
|
const ws_token = `?access_token=${$user.bearer_token}`
|
||||||
|
|
||||||
|
let stillId = 0
|
||||||
|
|
||||||
|
let recording = false
|
||||||
|
|
||||||
|
let videoMode = false
|
||||||
|
|
||||||
|
const takeStill = () => stillId += 1
|
||||||
|
|
||||||
|
const toggleMode = () => {
|
||||||
|
videoMode = !videoMode
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer:number
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
timer = setInterval(takeStill, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(timer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<SettingsCard collapsible={false}>
|
||||||
|
<Camera slot="icon" class="lex-shrink-0 mr-2 h-6 w-6 self-end" />
|
||||||
|
<span slot="title">Camera</span>
|
||||||
|
<img src={"/api/camera/still"+ ws_token + "key=" + stillId} alt="Live-stream" class="w-full rounded-lg shadow-lg" />
|
||||||
|
<CameraSetting />
|
||||||
|
<div>
|
||||||
|
<!-- <div class="relative">
|
||||||
|
<div class="-top-12 absolute flex justify-center w-full">
|
||||||
|
<label class="swap">
|
||||||
|
<input type="checkbox" bind:value={recording} />
|
||||||
|
<div class="swap-on"><Recording class="h-10 w-10" /></div>
|
||||||
|
<div class="swap-off"><Record class="h-10 w-10" /></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class="flex justify-center gap-4 p-2 rounded-xl bg-opacity-50 bg-slate-600 mt-2">
|
||||||
|
<button class="btn-outline" class:btn-primary={!videoMode} on:click={toggleMode}><Camera class="h-5 w-5" /></button>
|
||||||
|
<button class="btn-outline" class:btn-primary={ videoMode} on:click={toggleMode}><VideoCamera class="h-5 w-5" /></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <button class="btn btn-primary inline-flex items-center" on:click={takeStill}>
|
||||||
|
<Reload class="mr-2 h-5 w-5" />
|
||||||
|
<span>Reload</span>
|
||||||
|
</button> -->
|
||||||
|
</SettingsCard>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { api } from '$lib/api';
|
||||||
|
import Spinner from '$lib/components/Spinner.svelte';
|
||||||
|
import type { CameraSettings } from '$lib/models';
|
||||||
|
let settings:CameraSettings
|
||||||
|
|
||||||
|
const getCameraSettings = async () => {
|
||||||
|
const result = await api.get<CameraSettings>('/api/camera/settings')
|
||||||
|
if (result.isErr()){
|
||||||
|
console.error("An error occurred", result.inner);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
settings = result.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCameraSettings = async () => {
|
||||||
|
const result = await api.post<CameraSettings>('/api/camera/settings', settings)
|
||||||
|
if (result.isErr()){
|
||||||
|
console.error("An error occurred", result.inner);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
settings = result.inner
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await getCameraSettings()}
|
||||||
|
<Spinner />
|
||||||
|
{:then _}
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<button class="btn btn-primary" type="button" on:click={updateCameraSettings}>Update camera settings</button>
|
||||||
|
|
||||||
|
<label for="brightness">
|
||||||
|
Brightness {settings.brightness}
|
||||||
|
<input type="range" min="-2" max="2" class="range range-xs" bind:value={settings.brightness}/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="contrast">
|
||||||
|
Contrast {settings.contrast}
|
||||||
|
<input type="range" min="-2" max="2" class="range range-xs" bind:value={settings.contrast}/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="framesize">
|
||||||
|
FrameSize {settings.framesize}
|
||||||
|
<input type="range" min="0" max="10" class="range range-xs" bind:value={settings.framesize}/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="cursor-pointer flex items-center justify-between">
|
||||||
|
Vertical flip
|
||||||
|
<input type="checkbox" class="toggle" bind:checked={settings.vflip} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="cursor-pointer flex items-center justify-between">
|
||||||
|
Horizontal flip
|
||||||
|
<input type="checkbox" class="toggle" bind:checked={settings.hmirror} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="special_effect" class="flex items-center">
|
||||||
|
<span class="basis-1/2">Special Effect</span>
|
||||||
|
<select class="select select-bordered select-sm w-full max-w-xs" bind:value={settings.special_effect}>
|
||||||
|
<option value={0}>No effect</option>
|
||||||
|
<option value={1}>Negative</option>
|
||||||
|
<option value={2}>Grayscale</option>
|
||||||
|
<option value={3}>Red tint</option>
|
||||||
|
<option value={4}>Green tint</option>
|
||||||
|
<option value={5}>Blue tint</option>
|
||||||
|
<option value={6}>Sepia</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include <CameraService.h>
|
||||||
|
|
||||||
|
static const char *_STREAM_CONTENT_TYPE =
|
||||||
|
"multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||||
|
static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||||
|
static const char *_STREAM_PART =
|
||||||
|
"Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
|
||||||
|
|
||||||
|
SemaphoreHandle_t cameraMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
camera_fb_t *safe_camera_fb_get() {
|
||||||
|
camera_fb_t *fb = NULL;
|
||||||
|
if (xSemaphoreTake(cameraMutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
fb = esp_camera_fb_get();
|
||||||
|
xSemaphoreGive(cameraMutex);
|
||||||
|
}
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraService::CameraService(PsychicHttpServer *server,
|
||||||
|
TaskManager *taskManager,
|
||||||
|
SecurityManager *securityManager)
|
||||||
|
: _server(server),
|
||||||
|
_taskManager(taskManager),
|
||||||
|
_securityManager(securityManager),
|
||||||
|
_videoStream(_STREAM_CONTENT_TYPE) {}
|
||||||
|
void CameraService::begin() {
|
||||||
|
InitializeCamera();
|
||||||
|
_server->on(
|
||||||
|
STILL_SERVICE_PATH, HTTP_GET,
|
||||||
|
_securityManager->wrapRequest(
|
||||||
|
std::bind(&CameraService::cameraStill, this, std::placeholders::_1),
|
||||||
|
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
|
|
||||||
|
ESP_LOGV("CameraService", "Registered GET endpoint: %s", STILL_SERVICE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t CameraService::InitializeCamera() {
|
||||||
|
camera_config_t camera_config;
|
||||||
|
camera_config.ledc_channel = LEDC_CHANNEL_0;
|
||||||
|
camera_config.ledc_timer = LEDC_TIMER_0;
|
||||||
|
camera_config.pin_d0 = Y2_GPIO_NUM;
|
||||||
|
camera_config.pin_d1 = Y3_GPIO_NUM;
|
||||||
|
camera_config.pin_d2 = Y4_GPIO_NUM;
|
||||||
|
camera_config.pin_d3 = Y5_GPIO_NUM;
|
||||||
|
camera_config.pin_d4 = Y6_GPIO_NUM;
|
||||||
|
camera_config.pin_d5 = Y7_GPIO_NUM;
|
||||||
|
camera_config.pin_d6 = Y8_GPIO_NUM;
|
||||||
|
camera_config.pin_d7 = Y9_GPIO_NUM;
|
||||||
|
camera_config.pin_xclk = XCLK_GPIO_NUM;
|
||||||
|
camera_config.pin_pclk = PCLK_GPIO_NUM;
|
||||||
|
camera_config.pin_vsync = VSYNC_GPIO_NUM;
|
||||||
|
camera_config.pin_href = HREF_GPIO_NUM;
|
||||||
|
camera_config.pin_sccb_sda = SIOD_GPIO_NUM;
|
||||||
|
camera_config.pin_sccb_scl = SIOC_GPIO_NUM;
|
||||||
|
camera_config.pin_pwdn = PWDN_GPIO_NUM;
|
||||||
|
camera_config.pin_reset = RESET_GPIO_NUM;
|
||||||
|
camera_config.xclk_freq_hz = 20000000;
|
||||||
|
camera_config.pixel_format = PIXFORMAT_JPEG;
|
||||||
|
|
||||||
|
if (psramFound()) {
|
||||||
|
camera_config.frame_size = FRAMESIZE_SVGA;
|
||||||
|
camera_config.jpeg_quality = 10;
|
||||||
|
camera_config.fb_count = 2;
|
||||||
|
} else {
|
||||||
|
camera_config.frame_size = FRAMESIZE_SVGA;
|
||||||
|
camera_config.jpeg_quality = 12;
|
||||||
|
camera_config.fb_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_i("Initializing camera");
|
||||||
|
esp_err_t err = esp_camera_init(&camera_config);
|
||||||
|
if (err != ESP_OK) log_e("Camera probe failed with error 0x%x", err);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t CameraService::cameraStill(PsychicRequest *request) {
|
||||||
|
camera_fb_t *fb = safe_camera_fb_get();
|
||||||
|
if (!fb) {
|
||||||
|
Serial.println("Camera capture failed");
|
||||||
|
request->reply(500, "text/plain", "Camera capture failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
PsychicStreamResponse response = PsychicStreamResponse(request, "image/jpeg", "capture.jpg");
|
||||||
|
response.beginSend();
|
||||||
|
response.write(fb->buf, fb->len);
|
||||||
|
esp_camera_fb_return(fb);
|
||||||
|
return response.endSend();
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef CameraService_h
|
||||||
|
#define CameraService_h
|
||||||
|
|
||||||
|
#define CAMERA_MODEL_AI_THINKER
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <CameraPins.h>
|
||||||
|
#include <PsychicHttp.h>
|
||||||
|
#include <SecurityManager.h>
|
||||||
|
#include <TaskManager.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <esp_camera.h>
|
||||||
|
|
||||||
|
#define STREAM_SERVICE_PATH "/api/camera/stream"
|
||||||
|
#define STILL_SERVICE_PATH "/api/camera/still"
|
||||||
|
|
||||||
|
#define PART_BOUNDARY "frame"
|
||||||
|
|
||||||
|
camera_fb_t *safe_camera_fb_get();
|
||||||
|
|
||||||
|
class CameraService
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CameraService(PsychicHttpServer *server, TaskManager *taskManager, SecurityManager *securityManager);
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PsychicHttpServer *_server;
|
||||||
|
TaskManager *_taskManager;
|
||||||
|
SecurityManager *_securityManager;
|
||||||
|
PsychicStream _videoStream;
|
||||||
|
esp_err_t cameraStill(PsychicRequest *request);
|
||||||
|
esp_err_t InitializeCamera();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // end CameraService_h
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
#ifndef CameraSettingsService_h
|
||||||
|
#define CameraSettingsService_h
|
||||||
|
|
||||||
|
#include <EventEndpoint.h>
|
||||||
|
#include <FSPersistence.h>
|
||||||
|
#include <HttpEndpoint.h>
|
||||||
|
#include <JsonUtils.h>
|
||||||
|
#include <PsychicHttp.h>
|
||||||
|
#include <SecurityManager.h>
|
||||||
|
#include <SettingValue.h>
|
||||||
|
#include <StatefulService.h>
|
||||||
|
#include <esp_camera.h>
|
||||||
|
|
||||||
|
#define CAMERA_SETTINGS_FILE "/config/cameraSettings.json"
|
||||||
|
#define EVENT_CAMERA_SETTINGS "CameraSettings"
|
||||||
|
#define CAMERA_SETTINGS_PATH "/api/camera/settings"
|
||||||
|
|
||||||
|
class CameraSettings {
|
||||||
|
public:
|
||||||
|
pixformat_t pixformat;
|
||||||
|
framesize_t framesize; // 0 - 10
|
||||||
|
uint8_t quality; // 0 - 63
|
||||||
|
int8_t brightness; //-2 - 2
|
||||||
|
int8_t contrast; //-2 - 2
|
||||||
|
int8_t saturation; //-2 - 2
|
||||||
|
int8_t sharpness; //-2 - 2
|
||||||
|
uint8_t denoise;
|
||||||
|
gainceiling_t gainceiling;
|
||||||
|
uint8_t whitebal;
|
||||||
|
uint8_t special_effect; // 0 - 6
|
||||||
|
uint8_t wb_mode; // 0 - 4
|
||||||
|
uint8_t awb;
|
||||||
|
uint8_t exposure_ctrl;
|
||||||
|
uint8_t awb_gain;
|
||||||
|
uint8_t gain_ctrl;
|
||||||
|
uint8_t aec;
|
||||||
|
uint8_t aec2;
|
||||||
|
int8_t ae_level; //-2 - 2
|
||||||
|
uint16_t aec_value; // 0 - 1200
|
||||||
|
uint8_t agc;
|
||||||
|
uint8_t agc_gain; // 0 - 30
|
||||||
|
uint8_t bpc;
|
||||||
|
uint8_t wpc;
|
||||||
|
uint8_t raw_gma;
|
||||||
|
uint8_t lenc;
|
||||||
|
uint8_t hmirror;
|
||||||
|
uint8_t vflip;
|
||||||
|
uint8_t dcw;
|
||||||
|
uint8_t colorbar;
|
||||||
|
|
||||||
|
static void read(CameraSettings &settings, JsonObject &root) {
|
||||||
|
root["pixformat"] = settings.pixformat;
|
||||||
|
root["framesize"] = settings.framesize;
|
||||||
|
root["quality"] = settings.quality;
|
||||||
|
root["brightness"] = settings.brightness;
|
||||||
|
root["contrast"] = settings.contrast;
|
||||||
|
root["saturation"] = settings.saturation;
|
||||||
|
root["sharpness"] = settings.sharpness;
|
||||||
|
root["denoise"] = settings.denoise;
|
||||||
|
root["special_effect"] = settings.special_effect;
|
||||||
|
root["wb_mode"] = settings.wb_mode;
|
||||||
|
root["exposure_ctrl"] = settings.exposure_ctrl;
|
||||||
|
root["gain_ctrl"] = settings.gain_ctrl;
|
||||||
|
root["awb"] = settings.awb;
|
||||||
|
root["awb_gain"] = settings.awb_gain;
|
||||||
|
root["aec"] = settings.aec;
|
||||||
|
root["aec2"] = settings.aec2;
|
||||||
|
root["ae_level"] = settings.ae_level;
|
||||||
|
root["aec_value"] = settings.aec_value;
|
||||||
|
root["agc"] = settings.agc;
|
||||||
|
root["agc_gain"] = settings.agc_gain;
|
||||||
|
root["gainceiling"] = settings.gainceiling;
|
||||||
|
root["bpc"] = settings.bpc;
|
||||||
|
root["wpc"] = settings.wpc;
|
||||||
|
root["raw_gma"] = settings.raw_gma;
|
||||||
|
root["lenc"] = settings.lenc;
|
||||||
|
root["hmirror"] = settings.hmirror;
|
||||||
|
root["vflip"] = settings.vflip;
|
||||||
|
root["dcw"] = settings.dcw;
|
||||||
|
root["colorbar"] = settings.colorbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StateUpdateResult update(JsonObject &root,
|
||||||
|
CameraSettings &settings) {
|
||||||
|
settings.pixformat = root["pixformat"];
|
||||||
|
settings.framesize = root["framesize"];
|
||||||
|
settings.brightness = root["brightness"];
|
||||||
|
settings.contrast = root["contrast"];
|
||||||
|
settings.quality = root["quality"];
|
||||||
|
settings.contrast = root["contrast"];
|
||||||
|
settings.saturation = root["saturation"];
|
||||||
|
settings.sharpness = root["sharpness"];
|
||||||
|
settings.denoise = root["denoise"];
|
||||||
|
settings.exposure_ctrl = root["exposure_ctrl"];
|
||||||
|
settings.gain_ctrl = root["gain_ctrl"];
|
||||||
|
settings.special_effect = root["special_effect"];
|
||||||
|
settings.wb_mode = root["wb_mode"];
|
||||||
|
settings.awb = root["awb"];
|
||||||
|
settings.awb_gain = root["awb_gain"];
|
||||||
|
settings.aec = root["aec"];
|
||||||
|
settings.aec2 = root["aec2"];
|
||||||
|
settings.ae_level = root["ae_level"];
|
||||||
|
settings.aec_value = root["aec_value"];
|
||||||
|
settings.agc = root["agc"];
|
||||||
|
settings.agc_gain = root["agc_gain"];
|
||||||
|
settings.gainceiling = root["gainceiling"];
|
||||||
|
settings.bpc = root["bpc"];
|
||||||
|
settings.wpc = root["wpc"];
|
||||||
|
settings.raw_gma = root["raw_gma"];
|
||||||
|
settings.lenc = root["lenc"];
|
||||||
|
settings.hmirror = root["hmirror"];
|
||||||
|
settings.vflip = root["vflip"];
|
||||||
|
settings.dcw = root["dcw"];
|
||||||
|
settings.colorbar = root["colorbar"];
|
||||||
|
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CameraSettingsService : public StatefulService<CameraSettings> {
|
||||||
|
public:
|
||||||
|
CameraSettingsService(PsychicHttpServer *server, FS *fs,
|
||||||
|
SecurityManager *securityManager, EventSocket *socket)
|
||||||
|
: _server(server),
|
||||||
|
_securityManager(securityManager),
|
||||||
|
_httpEndpoint(CameraSettings::read, CameraSettings::update, this,
|
||||||
|
server, CAMERA_SETTINGS_PATH, securityManager,
|
||||||
|
AuthenticationPredicates::IS_ADMIN),
|
||||||
|
_eventEndpoint(CameraSettings::read, CameraSettings::update, this,
|
||||||
|
socket, EVENT_CAMERA_SETTINGS),
|
||||||
|
_fsPersistence(CameraSettings::read, CameraSettings::update, this, fs,
|
||||||
|
CAMERA_SETTINGS_FILE) {
|
||||||
|
addUpdateHandler([&](const String &originId) { updateCamera(); },
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin() {
|
||||||
|
_httpEndpoint.begin();
|
||||||
|
_eventEndpoint.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCamera() {
|
||||||
|
ESP_LOGI("CameraSettings", "Updating camera settings");
|
||||||
|
sensor_t *s = esp_camera_sensor_get();
|
||||||
|
s->set_pixformat(s, _state.pixformat);
|
||||||
|
s->set_framesize(s, _state.framesize);
|
||||||
|
s->set_brightness(s, _state.brightness);
|
||||||
|
s->set_contrast(s, _state.contrast);
|
||||||
|
s->set_saturation(s, _state.saturation);
|
||||||
|
s->set_sharpness(s, _state.sharpness);
|
||||||
|
s->set_denoise(s, _state.denoise);
|
||||||
|
s->set_gainceiling(s, _state.gainceiling);
|
||||||
|
s->set_quality(s, _state.quality);
|
||||||
|
s->set_colorbar(s, _state.colorbar);
|
||||||
|
s->set_whitebal(s, _state.whitebal);
|
||||||
|
s->set_awb_gain(s, _state.awb_gain);
|
||||||
|
s->set_wb_mode(s, _state.wb_mode);
|
||||||
|
s->set_exposure_ctrl(s, _state.exposure_ctrl);
|
||||||
|
s->set_aec2(s, _state.aec2);
|
||||||
|
s->set_ae_level(s, _state.ae_level);
|
||||||
|
s->set_aec_value(s, _state.aec_value);
|
||||||
|
s->set_gain_ctrl(s, _state.gain_ctrl);
|
||||||
|
s->set_agc_gain(s, _state.agc_gain);
|
||||||
|
s->set_bpc(s, _state.bpc);
|
||||||
|
s->set_wpc(s, _state.wpc);
|
||||||
|
s->set_special_effect(s, _state.special_effect);
|
||||||
|
s->set_raw_gma(s, _state.raw_gma);
|
||||||
|
s->set_lenc(s, _state.lenc);
|
||||||
|
s->set_hmirror(s, _state.hmirror);
|
||||||
|
s->set_vflip(s, _state.vflip);
|
||||||
|
s->set_dcw(s, _state.dcw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PsychicHttpServer *_server;
|
||||||
|
SecurityManager *_securityManager;
|
||||||
|
HttpEndpoint<CameraSettings> _httpEndpoint;
|
||||||
|
EventEndpoint<CameraSettings> _eventEndpoint;
|
||||||
|
FSPersistence<CameraSettings> _fsPersistence;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // end CameraSettingsService_h
|
||||||
@@ -15,15 +15,20 @@
|
|||||||
|
|
||||||
#include <ESP32SvelteKit.h>
|
#include <ESP32SvelteKit.h>
|
||||||
|
|
||||||
ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEndpoints)
|
ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server,
|
||||||
: _server(server), _numberEndpoints(numberEndpoints), _taskManager(),
|
unsigned int numberEndpoints)
|
||||||
_featureService(server), _securitySettingsService(server, &ESPFS),
|
: _server(server),
|
||||||
|
_numberEndpoints(numberEndpoints),
|
||||||
|
_taskManager(),
|
||||||
|
_featureService(server),
|
||||||
|
_securitySettingsService(server, &ESPFS),
|
||||||
_wifiSettingsService(server, &ESPFS, &_securitySettingsService, &_socket),
|
_wifiSettingsService(server, &ESPFS, &_securitySettingsService, &_socket),
|
||||||
_wifiScanner(server, &_securitySettingsService),
|
_wifiScanner(server, &_securitySettingsService),
|
||||||
_wifiStatus(server, &_securitySettingsService),
|
_wifiStatus(server, &_securitySettingsService),
|
||||||
_apSettingsService(server, &ESPFS, &_securitySettingsService),
|
_apSettingsService(server, &ESPFS, &_securitySettingsService),
|
||||||
_apStatus(server, &_securitySettingsService, &_apSettingsService),
|
_apStatus(server, &_securitySettingsService, &_apSettingsService),
|
||||||
_socket(server, &_securitySettingsService, AuthenticationPredicates::IS_AUTHENTICATED),
|
_socket(server, &_securitySettingsService,
|
||||||
|
AuthenticationPredicates::IS_AUTHENTICATED),
|
||||||
#if FT_ENABLED(FT_NTP)
|
#if FT_ENABLED(FT_NTP)
|
||||||
_ntpSettingsService(server, &ESPFS, &_securitySettingsService),
|
_ntpSettingsService(server, &ESPFS, &_securitySettingsService),
|
||||||
_ntpStatus(server, &_securitySettingsService),
|
_ntpStatus(server, &_securitySettingsService),
|
||||||
@@ -32,7 +37,8 @@ ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEnd
|
|||||||
_uploadFirmwareService(server, &_securitySettingsService),
|
_uploadFirmwareService(server, &_securitySettingsService),
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
|
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
|
||||||
_downloadFirmwareService(server, &_securitySettingsService, &_socket, &_taskManager),
|
_downloadFirmwareService(server, &_securitySettingsService, &_socket,
|
||||||
|
&_taskManager),
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_MQTT)
|
#if FT_ENABLED(FT_MQTT)
|
||||||
_mqttSettingsService(server, &ESPFS, &_securitySettingsService),
|
_mqttSettingsService(server, &ESPFS, &_securitySettingsService),
|
||||||
@@ -49,153 +55,169 @@ ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEnd
|
|||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_ANALYTICS)
|
#if FT_ENABLED(FT_ANALYTICS)
|
||||||
_analyticsService(&_socket, &_taskManager),
|
_analyticsService(&_socket, &_taskManager),
|
||||||
|
#endif
|
||||||
|
#if FT_ENABLED(FT_CAMERA)
|
||||||
|
_cameraService(server, &_taskManager, &_securitySettingsService),
|
||||||
|
_cameraSettingsService(server, &ESPFS, &_securitySettingsService, &_socket),
|
||||||
#endif
|
#endif
|
||||||
_restartService(server, &_securitySettingsService),
|
_restartService(server, &_securitySettingsService),
|
||||||
_factoryResetService(server, &ESPFS, &_securitySettingsService),
|
_factoryResetService(server, &ESPFS, &_securitySettingsService),
|
||||||
_systemStatus(server, &_securitySettingsService),
|
_systemStatus(server, &_securitySettingsService),
|
||||||
_fileExplorer(server, &_securitySettingsService),
|
_fileExplorer(server, &_securitySettingsService),
|
||||||
_motionService(_server, &_socket, &_securitySettingsService, &_taskManager) {
|
_motionService(_server, &_socket, &_securitySettingsService,
|
||||||
|
&_taskManager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32SvelteKit::begin() {
|
void ESP32SvelteKit::begin() {
|
||||||
ESP_LOGV("ESP32SvelteKit", "Loading settings from files system");
|
ESP_LOGV("ESP32SvelteKit", "Loading settings from files system");
|
||||||
ESP_LOGI("Running Firmware Version: %s", APP_VERSION);
|
ESP_LOGI("Running Firmware Version: %s", APP_VERSION);
|
||||||
ESPFS.begin(true);
|
ESPFS.begin(true);
|
||||||
|
|
||||||
_wifiSettingsService.initWiFi();
|
_wifiSettingsService.initWiFi();
|
||||||
|
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|
||||||
setupMDNS();
|
setupMDNS();
|
||||||
|
|
||||||
startServices();
|
startServices();
|
||||||
|
|
||||||
ESP_LOGV("ESP32SvelteKit", "Starting loop task");
|
ESP_LOGV("ESP32SvelteKit", "Starting loop task");
|
||||||
_taskManager.createTask(this->_loopImpl, "Spot main", 4096, this,
|
_taskManager.createTask(this->_loopImpl, "Spot main", 4096, this,
|
||||||
(tskIDLE_PRIORITY + 1), NULL,
|
(tskIDLE_PRIORITY + 1), NULL,
|
||||||
ESP32SVELTEKIT_RUNNING_CORE);
|
ESP32SVELTEKIT_RUNNING_CORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32SvelteKit::setupServer() {
|
void ESP32SvelteKit::setupServer() {
|
||||||
_server->config.max_uri_handlers = _numberEndpoints;
|
_server->config.max_uri_handlers = _numberEndpoints;
|
||||||
_server->listen(80);
|
_server->listen(80);
|
||||||
|
|
||||||
#ifdef EMBED_WWW
|
#ifdef EMBED_WWW
|
||||||
ESP_LOGV("ESP32SvelteKit","Registering routes from PROGMEM static resources");
|
ESP_LOGV("ESP32SvelteKit",
|
||||||
WWWData::registerRoutes([&](const String &uri, const String &contentType, const uint8_t *content, size_t len) {
|
"Registering routes from PROGMEM static resources");
|
||||||
PsychicHttpRequestCallback requestHandler = [contentType, content, len](PsychicRequest *request) {
|
WWWData::registerRoutes([&](const String &uri, const String &contentType,
|
||||||
PsychicResponse response(request);
|
const uint8_t *content, size_t len) {
|
||||||
response.setCode(200);
|
PsychicHttpRequestCallback requestHandler =
|
||||||
response.setContentType(contentType.c_str());
|
[contentType, content, len](PsychicRequest *request) {
|
||||||
response.addHeader("Content-Encoding", "gzip");
|
PsychicResponse response(request);
|
||||||
response.addHeader("Cache-Control", "public, immutable, max-age=31536000");
|
response.setCode(200);
|
||||||
response.setContent(content, len);
|
response.setContentType(contentType.c_str());
|
||||||
return response.send();
|
response.addHeader("Content-Encoding", "gzip");
|
||||||
};
|
response.addHeader("Cache-Control",
|
||||||
PsychicWebHandler *handler = new PsychicWebHandler();
|
"public, immutable, max-age=31536000");
|
||||||
handler->onRequest(requestHandler);
|
response.setContent(content, len);
|
||||||
_server->on(uri.c_str(), HTTP_GET, handler);
|
return response.send();
|
||||||
|
};
|
||||||
|
PsychicWebHandler *handler = new PsychicWebHandler();
|
||||||
|
handler->onRequest(requestHandler);
|
||||||
|
_server->on(uri.c_str(), HTTP_GET, handler);
|
||||||
|
|
||||||
// Set default end-point for all non matching requests
|
// Set default end-point for all non matching requests
|
||||||
// this is easier than using webServer.onNotFound()
|
// this is easier than using webServer.onNotFound()
|
||||||
if (uri.equals("/index.html")) {
|
if (uri.equals("/index.html")) {
|
||||||
_server->defaultEndpoint->setHandler(handler);
|
_server->defaultEndpoint->setHandler(handler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
// Serve static resources from /www/
|
// Serve static resources from /www/
|
||||||
ESP_LOGV("ESP32SvelteKit",
|
ESP_LOGV("ESP32SvelteKit",
|
||||||
"Registering routes from FS /www/ static resources");
|
"Registering routes from FS /www/ static resources");
|
||||||
_server->serveStatic("/_app/", ESPFS, "/www/_app/");
|
_server->serveStatic("/_app/", ESPFS, "/www/_app/");
|
||||||
_server->serveStatic("/favicon.png", ESPFS, "/www/favicon.png");
|
_server->serveStatic("/favicon.png", ESPFS, "/www/favicon.png");
|
||||||
// Serving all other get requests with "/www/index.htm"
|
// Serving all other get requests with "/www/index.htm"
|
||||||
_server->onNotFound([](PsychicRequest *request) {
|
_server->onNotFound([](PsychicRequest *request) {
|
||||||
if (request->method() == HTTP_GET) {
|
if (request->method() == HTTP_GET) {
|
||||||
PsychicFileResponse response(request, ESPFS, "/www/index.html",
|
PsychicFileResponse response(request, ESPFS, "/www/index.html",
|
||||||
"text/html");
|
"text/html");
|
||||||
return response.send();
|
return response.send();
|
||||||
// String url = "http://" + request->host() + "/index.html";
|
// String url = "http://" + request->host() + "/index.html";
|
||||||
// request->redirect(url.c_str());
|
// request->redirect(url.c_str());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
#ifdef SERVE_CONFIG_FILES
|
#ifdef SERVE_CONFIG_FILES
|
||||||
_server->serveStatic("/config/", ESPFS, "/config/");
|
_server->serveStatic("/config/", ESPFS, "/config/");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ENABLE_CORS)
|
#if defined(ENABLE_CORS)
|
||||||
ESP_LOGV("ESP32SvelteKit", "Enabling CORS headers");
|
ESP_LOGV("ESP32SvelteKit", "Enabling CORS headers");
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN);
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin",
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
CORS_ORIGIN);
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers",
|
||||||
|
"Accept, Content-Type, Authorization");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials",
|
||||||
|
"true");
|
||||||
#endif
|
#endif
|
||||||
DefaultHeaders::Instance().addHeader("Server", _appName);
|
DefaultHeaders::Instance().addHeader("Server", _appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32SvelteKit::setupMDNS() {
|
void ESP32SvelteKit::setupMDNS() {
|
||||||
ESP_LOGV("ESP32SvelteKit", "Starting MDNS");
|
ESP_LOGV("ESP32SvelteKit", "Starting MDNS");
|
||||||
MDNS.begin(_wifiSettingsService.getHostname().c_str());
|
MDNS.begin(_wifiSettingsService.getHostname().c_str());
|
||||||
MDNS.setInstanceName(_appName);
|
MDNS.setInstanceName(_appName);
|
||||||
MDNS.addService("http", "tcp", 80);
|
MDNS.addService("http", "tcp", 80);
|
||||||
MDNS.addService("ws", "tcp", 80);
|
MDNS.addService("ws", "tcp", 80);
|
||||||
MDNS.addServiceTxt("http", "tcp", "Firmware Version", APP_VERSION);
|
MDNS.addServiceTxt("http", "tcp", "Firmware Version", APP_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32SvelteKit::startServices() {
|
void ESP32SvelteKit::startServices() {
|
||||||
_apStatus.begin();
|
_apStatus.begin();
|
||||||
_socket.begin();
|
_socket.begin();
|
||||||
_apSettingsService.begin();
|
_apSettingsService.begin();
|
||||||
_factoryResetService.begin();
|
_factoryResetService.begin();
|
||||||
_featureService.begin();
|
_featureService.begin();
|
||||||
_restartService.begin();
|
_restartService.begin();
|
||||||
_systemStatus.begin();
|
_systemStatus.begin();
|
||||||
_wifiSettingsService.begin();
|
_wifiSettingsService.begin();
|
||||||
_wifiScanner.begin();
|
_wifiScanner.begin();
|
||||||
_wifiStatus.begin();
|
_wifiStatus.begin();
|
||||||
|
|
||||||
#if FT_ENABLED(FT_UPLOAD_FIRMWARE)
|
#if FT_ENABLED(FT_UPLOAD_FIRMWARE)
|
||||||
_uploadFirmwareService.begin();
|
_uploadFirmwareService.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
|
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
|
||||||
_downloadFirmwareService.begin();
|
_downloadFirmwareService.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_NTP)
|
#if FT_ENABLED(FT_NTP)
|
||||||
_ntpSettingsService.begin();
|
_ntpSettingsService.begin();
|
||||||
_ntpStatus.begin();
|
_ntpStatus.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_MQTT)
|
#if FT_ENABLED(FT_MQTT)
|
||||||
_mqttSettingsService.begin();
|
_mqttSettingsService.begin();
|
||||||
_mqttStatus.begin();
|
_mqttStatus.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_SECURITY)
|
#if FT_ENABLED(FT_SECURITY)
|
||||||
_authenticationService.begin();
|
_authenticationService.begin();
|
||||||
_securitySettingsService.begin();
|
_securitySettingsService.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_ANALYTICS)
|
#if FT_ENABLED(FT_ANALYTICS)
|
||||||
_analyticsService.begin();
|
_analyticsService.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_SLEEP)
|
#if FT_ENABLED(FT_SLEEP)
|
||||||
_sleepService.begin();
|
_sleepService.begin();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_BATTERY)
|
#if FT_ENABLED(FT_BATTERY)
|
||||||
_batteryService.begin();
|
_batteryService.begin();
|
||||||
|
#endif
|
||||||
|
_taskManager.begin();
|
||||||
|
_fileExplorer.begin();
|
||||||
|
_motionService.begin();
|
||||||
|
#if FT_ENABLED(FT_CAMERA)
|
||||||
|
_cameraService.begin();
|
||||||
|
_cameraSettingsService.begin();
|
||||||
#endif
|
#endif
|
||||||
_taskManager.begin();
|
|
||||||
_fileExplorer.begin();
|
|
||||||
_motionService.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ESP32SvelteKit::_loop() {
|
void IRAM_ATTR ESP32SvelteKit::_loop() {
|
||||||
while (1) {
|
while (1) {
|
||||||
_wifiSettingsService.loop();
|
_wifiSettingsService.loop();
|
||||||
_apSettingsService.loop();
|
_apSettingsService.loop();
|
||||||
#if FT_ENABLED(FT_MQTT)
|
#if FT_ENABLED(FT_MQTT)
|
||||||
_mqttSettingsService.loop();
|
_mqttSettingsService.loop();
|
||||||
#endif
|
#endif
|
||||||
#if FT_ENABLED(FT_ANALYTICS)
|
#if FT_ENABLED(FT_ANALYTICS)
|
||||||
_analyticsService.loop();
|
_analyticsService.loop();
|
||||||
#endif
|
#endif
|
||||||
_motionService.loop();
|
_motionService.loop();
|
||||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include <MqttStatus.h>
|
#include <MqttStatus.h>
|
||||||
#include <MotionService.h>
|
#include <MotionService.h>
|
||||||
#include <NTPSettingsService.h>
|
#include <NTPSettingsService.h>
|
||||||
|
#include <CameraService.h>
|
||||||
|
#include <CameraSettingsService.h>
|
||||||
#include <NTPStatus.h>
|
#include <NTPStatus.h>
|
||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
#include <RestartService.h>
|
#include <RestartService.h>
|
||||||
@@ -164,6 +166,16 @@ public:
|
|||||||
{
|
{
|
||||||
return &_motionService;
|
return &_motionService;
|
||||||
}
|
}
|
||||||
|
#if FT_ENABLED(FT_CAMERA)
|
||||||
|
CameraService *getCameraService()
|
||||||
|
{
|
||||||
|
return &_cameraService;
|
||||||
|
}
|
||||||
|
CameraSettingsService *getCameraSettingsService()
|
||||||
|
{
|
||||||
|
return &_cameraSettingsService;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void factoryReset()
|
void factoryReset()
|
||||||
{
|
{
|
||||||
@@ -223,6 +235,10 @@ private:
|
|||||||
TaskManager _taskManager;
|
TaskManager _taskManager;
|
||||||
FileExplorer _fileExplorer;
|
FileExplorer _fileExplorer;
|
||||||
MotionService _motionService;
|
MotionService _motionService;
|
||||||
|
#if FT_ENABLED(FT_CAMERA)
|
||||||
|
CameraService _cameraService;
|
||||||
|
CameraSettingsService _cameraSettingsService;
|
||||||
|
#endif
|
||||||
|
|
||||||
String _appName = APP_NAME;
|
String _appName = APP_NAME;
|
||||||
|
|
||||||
|
|||||||
@@ -57,4 +57,9 @@
|
|||||||
#define FT_ANALYTICS 1
|
#define FT_ANALYTICS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ESP32 camera off by default
|
||||||
|
#ifndef FT_CAMERA
|
||||||
|
#define FT_CAMERA 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ void FeaturesService::begin() {
|
|||||||
root["sleep"] = FT_SLEEP;
|
root["sleep"] = FT_SLEEP;
|
||||||
root["battery"] = FT_BATTERY;
|
root["battery"] = FT_BATTERY;
|
||||||
root["analytics"] = FT_ANALYTICS;
|
root["analytics"] = FT_ANALYTICS;
|
||||||
|
root["camera"] = FT_CAMERA;
|
||||||
root["firmware_version"] = APP_VERSION;
|
root["firmware_version"] = APP_VERSION;
|
||||||
root["firmware_name"] = APP_NAME;
|
root["firmware_name"] = APP_NAME;
|
||||||
root["firmware_built_target"] = BUILD_TARGET;
|
root["firmware_built_target"] = BUILD_TARGET;
|
||||||
|
|||||||
@@ -3,19 +3,20 @@
|
|||||||
|
|
||||||
//#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
|
//#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
|
||||||
|
|
||||||
#include <http_status.h>
|
#include "PsychicEndpoint.h"
|
||||||
|
#include "PsychicEventSource.h"
|
||||||
|
#include "PsychicFileResponse.h"
|
||||||
|
#include "PsychicHandler.h"
|
||||||
#include "PsychicHttpServer.h"
|
#include "PsychicHttpServer.h"
|
||||||
|
#include "PsychicJson.h"
|
||||||
#include "PsychicRequest.h"
|
#include "PsychicRequest.h"
|
||||||
#include "PsychicResponse.h"
|
#include "PsychicResponse.h"
|
||||||
#include "PsychicEndpoint.h"
|
|
||||||
#include "PsychicHandler.h"
|
|
||||||
#include "PsychicStaticFileHandler.h"
|
#include "PsychicStaticFileHandler.h"
|
||||||
#include "PsychicFileResponse.h"
|
#include "PsychicStream.h"
|
||||||
#include "PsychicStreamResponse.h"
|
#include "PsychicStreamResponse.h"
|
||||||
#include "PsychicUploadHandler.h"
|
#include "PsychicUploadHandler.h"
|
||||||
#include "PsychicWebSocket.h"
|
#include "PsychicWebSocket.h"
|
||||||
#include "PsychicEventSource.h"
|
#include <http_status.h>
|
||||||
#include "PsychicJson.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_ASYNC
|
#ifdef ENABLE_ASYNC
|
||||||
#include "async_worker.h"
|
#include "async_worker.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user