#include static const char *TAG = "Spot"; Spot::Spot() : #if FT_ENABLED(USE_MOTION) _motionService(&_servoController, &_peripherals) #endif { } void Spot::initialize() { ESP_LOGI(TAG, "Running Firmware Version: %s", APP_VERSION); feature_service::printFeatureConfiguration(); ESPFS.begin(true); g_taskManager.begin(); #if FT_ENABLED(USE_WS2812) _ledService.loop(); #endif _wifiService.begin(); setupServer(); startServices(); ESP_LOGV(TAG, "Starting misc loop task"); g_taskManager.createTask(this->_loopImpl, "Spot misc", 4096, this, 2, NULL, APPLICATION_CORE); } void Spot::setupServer() { _server.config.max_uri_handlers = _numberEndpoints; _server.maxUploadSize = _maxFileUpload; _server.listen(_port); // WIFI _server.on("/api/wifi/scan", HTTP_GET, _wifiService.handleScan); _server.on("/api/wifi/networks", HTTP_GET, [this](PsychicRequest *request) { return _wifiService.getNetworks(request); }); _server.on("/api/wifi/sta/status", HTTP_GET, [this](PsychicRequest *request) { return _wifiService.getNetworkStatus(request); }); _server.on("/api/wifi/sta/settings", HTTP_GET, [this](PsychicRequest *request) { return _wifiService.endpoint.getState(request); }); _server.on("/api/wifi/sta/settings", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _wifiService.endpoint.handleStateUpdate(request, json); }); // AP _server.on("/api/wifi/ap/status", HTTP_GET, [this](PsychicRequest *request) { return _apService.getStatus(request); }); _server.on("/api/wifi/ap/settings", HTTP_GET, [this](PsychicRequest *request) { return _apService.endpoint.getState(request); }); _server.on("/api/wifi/ap/settings", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _apService.endpoint.handleStateUpdate(request, json); }); // CAMERA #if USE_CAMERA _server.on("/api/camera/still", HTTP_GET, [this](PsychicRequest *request) { return _cameraService.cameraStill(request); }); _server.on("/api/camera/stream", HTTP_GET, [this](PsychicRequest *request) { return _cameraService.cameraStream(request); }); _server.on("/api/camera/settings", HTTP_GET, [this](PsychicRequest *request) { return _cameraService.endpoint.getState(request); }); _server.on("/api/camera/settings", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _cameraService.endpoint.handleStateUpdate(request, json); }); #endif // SYSTEM _server.on("/api/system/reset", HTTP_POST, system_service::handleReset); _server.on("/api/system/restart", HTTP_POST, system_service::handleRestart); _server.on("/api/system/sleep", HTTP_POST, system_service::handleSleep); _server.on("/api/system/status", HTTP_GET, system_service::getStatus); _server.on("/api/system/metrics", HTTP_GET, system_service::getMetrics); // FILESYSTEM _server.on("/api/files", HTTP_GET, FileSystem::getFiles); _server.on("/api/files/delete", HTTP_POST, FileSystem::handleDelete); _server.on("/api/files/upload/*", HTTP_POST, FileSystem::uploadHandler); _server.on("/api/files/edit", HTTP_POST, FileSystem::handleEdit); _server.on("/api/files/mkdir", HTTP_POST, FileSystem::mkdir); // SERVO _server.on("/api/servo/config", HTTP_GET, [this](PsychicRequest *request) { return _servoController.endpoint.getState(request); }); _server.on("/api/servo/config", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _servoController.endpoint.handleStateUpdate(request, json); }); // PERIPHERALS _server.on("/api/peripheral/settings", HTTP_GET, [this](PsychicRequest *request) { return _peripherals.endpoint.getState(request); }); _server.on("/api/peripheral/settings", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _peripherals.endpoint.handleStateUpdate(request, json); }); // MISC _server.on("/api/ws/events", socket.getHandler()); _server.on("/api/features", feature_service::getFeatures); #if FT_ENABLED(USE_UPLOAD_FIRMWARE) _server.on("/api/firmware", HTTP_POST, _uploadFirmwareService.getHandler()); #endif #if FT_ENABLED(USE_DOWNLOAD_FIRMWARE) _server.on("/api/firmware/download", HTTP_POST, [this](PsychicRequest *r, JsonVariant &json) { return _downloadFirmwareService.handleDownloadUpdate(r, json); }); #endif // MDNS _server.on("/api/mdns/status", HTTP_GET, [this](PsychicRequest *request) { return _mdnsService.getStatus(request); }); _server.on("/api/mdns/settings", HTTP_GET, [this](PsychicRequest *request) { return _mdnsService.endpoint.getState(request); }); _server.on("/api/mdns/settings", HTTP_POST, [this](PsychicRequest *request, JsonVariant &json) { return _mdnsService.endpoint.handleStateUpdate(request, json); }); _server.on("/api/mdns/query", HTTP_POST, MDNSService::queryServices); #ifdef EMBED_WWW ESP_LOGV(TAG, "Registering routes from PROGMEM static resources"); WWWData::registerRoutes([&](const String &uri, const String &contentType, const uint8_t *content, size_t len) { PsychicHttpRequestCallback requestHandler = [contentType, content, len](PsychicRequest *request) { PsychicResponse response(request); response.setCode(200); response.setContentType(contentType.c_str()); response.addHeader("Content-Encoding", "gzip"); response.addHeader("Cache-Control", "public, immutable, max-age=31536000"); response.setContent(content, len); 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 // this is easier than using webServer.onNotFound() if (uri.equals("/index.html")) { _server.defaultEndpoint->setHandler(handler); } }); #else // Serve static resources from /www/ ESP_LOGV(TAG, "Registering routes from FS /www/ static resources"); _server.serveStatic("/_app/", ESPFS, "/www/_app/"); _server.serveStatic("/favicon.png", ESPFS, "/www/favicon.png"); // Serving all other get requests with "/www/index.htm" _server.onNotFound([](PsychicRequest *request) { if (request->method() == HTTP_GET) { PsychicFileResponse response(request, ESPFS, "/www/index.html", "text/html"); return response.send(); // String url = "http://" + request->host() + "/index.html"; // request->redirect(url.c_str()); } }); #endif #ifdef SERVE_CONFIG_FILES _server.serveStatic("/api/config/", ESPFS, "/config/"); #endif #if defined(ENABLE_CORS) ESP_LOGV(TAG, "Enabling CORS headers"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); #endif DefaultHeaders::Instance().addHeader("Server", _appName); } void Spot::startServices() { _apService.begin(); #if FT_ENABLED(USE_UPLOAD_FIRMWARE) _uploadFirmwareService.begin(); #endif _peripherals.begin(); _servoController.begin(); #if FT_ENABLED(USE_MOTION) _motionService.begin(); #endif #if FT_ENABLED(USE_CAMERA) _cameraService.begin(); #endif _mdnsService.begin(); } void IRAM_ATTR Spot::loop() { while (1) { _wifiService.loop(); _apService.loop(); EXECUTE_EVERY_N_MS(2000, system_service::emitMetrics()); delay(20); } }