#include "appGlobals.h"
static char variable[FILE_NAME_LEN];
static char value[FILE_NAME_LEN];
static char alertCaption[100];
static bool alertReady = false;
static bool depthColor = true;
static bool devHub = false;
char AuxIP[MAX_IP_LEN];
bool useUart = false;
int quality;
volatile audioAction THIS_ACTION = PASS_ACTION;
static void stopRC();
bool updateAppStatus(const char* variable, const char* value, bool fromUser) {
esp_err_t res = ESP_OK;
#ifndef AUXILIARY
sensor_t* s = esp_camera_sensor_get();
#endif
int intVal = atoi(value);
float fltVal = atof(value);
if (!strcmp(variable, "custom")) return true;
#ifndef AUXILIARY
else if (!strcmp(variable, "stopStream")) stopSustainTask(intVal);
else if (!strcmp(variable, "stopPlaying")) stopPlaying();
else if (!strcmp(variable, "minf")) minSeconds = intVal;
else if (!strcmp(variable, "motionVal")) motionVal = intVal;
else if (!strcmp(variable, "moveStartChecks")) moveStartChecks = intVal;
else if (!strcmp(variable, "moveStopSecs")) moveStopSecs = intVal;
else if (!strcmp(variable, "maxFrames")) maxFrames = intVal > 0 ? intVal : maxFrames;
else if (!strcmp(variable, "detectMotionFrames")) detectMotionFrames = intVal;
else if (!strcmp(variable, "detectNightFrames")) detectNightFrames = intVal;
else if (!strcmp(variable, "detectNumBands")) detectNumBands = intVal;
else if (!strcmp(variable, "detectStartBand")) detectStartBand = intVal;
else if (!strcmp(variable, "detectEndBand")) detectEndBand = intVal;
else if (!strcmp(variable, "detectChangeThreshold")) detectChangeThreshold = intVal;
else if (!strcmp(variable, "mlUse")) mlUse = (bool)intVal;
else if (!strcmp(variable, "mlProbability")) mlProbability = fltVal < 0 ? 0.0 : (fltVal > 1.0 ? 1.0 : fltVal);
else if (!strcmp(variable, "depthColor")) {
depthColor = (bool)intVal;
colorDepth = depthColor ? RGB888_BYTES : GRAYSCALE_BYTES;
}
else if (!strcmp(variable, "enableMotion")) {
useMotion = (intVal) ? true : false;
LOG_INF("%s motion detection by camera", useMotion ? "Enabling" : "Disabling");
}
else if (!strcmp(variable, "timeLapseOn")) timeLapseOn = intVal;
else if (!strcmp(variable, "dashCamOn")) {
dashCamOn = intVal;
if (dashCamOn == 0) forceRecord = false;
}
else if (!strcmp(variable, "tlSecsBetweenFrames")) tlSecsBetweenFrames = intVal;
else if (!strcmp(variable, "tlDurationMins")) tlDurationMins = intVal;
else if (!strcmp(variable, "tlPlaybackFPS")) tlPlaybackFPS = intVal;
#if !INCLUDE_RTSP
else if (!strcmp(variable, "streamVid")) streamVid = (bool)intVal;
else if (!strcmp(variable, "streamAud")) streamAud = (bool)intVal;
else if (!strcmp(variable, "streamSrt")) streamSrt = (bool)intVal;
#endif
else if (!strcmp(variable, "lswitch")) nightSwitch = intVal;
#endif
#if INCLUDE_FTP_HFS
else if (!strcmp(variable, "upload")) fsStartTransfer(value);
#endif
else if (!strcmp(variable, "delete")) {
stopPlayback = true;
deleteFolderOrFile(value);
}
else if (!strcmp(variable, "record")) doRecording = (intVal) ? true : false;
else if (!strcmp(variable, "forceRecord")) forceRecord = (intVal) ? true : false;
else if (!strcmp(variable, "dbgMotion")) {
dbgMotion = (intVal && useMotion) ? true : false;
doRecording = !dbgMotion;
}
else if (!strcmp(variable, "devHub")) devHub = (bool)intVal;
#if INCLUDE_PERIPH
else if (!strcmp(variable, "pirUse")) pirUse = (bool)intVal;
else if (!strcmp(variable, "lampLevel")) {
lampLevel = intVal;
if (!lampType) setLamp(lampLevel);
}
else if (!strcmp(variable, "lampType")) {
lampType = intVal;
lampAuto = lampNight = false;
if (lampType == 1) lampAuto = true;
if (!lampType) setLamp(lampLevel);
else setLamp(0);
}
else if (!strcmp(variable, "relayPin")) relayPin = intVal;
else if (!strcmp(variable, "relayMode")) relayMode = (bool)intVal;
else if (!strcmp(variable, "relaySwitch")) digitalWrite(relayPin, intVal);
else if (!strcmp(variable, "SVactive")) SVactive = (bool)intVal;
else if (!strcmp(variable, "voltUse")) voltUse = (bool)intVal;
else if (!strcmp(variable, "pirPin")) pirPin = intVal;
else if (!strcmp(variable, "lampPin")) lampPin = intVal;
else if (!strcmp(variable, "servoPanPin")) servoPanPin = intVal;
else if (!strcmp(variable, "servoTiltPin")) servoTiltPin = intVal;
else if (!strcmp(variable, "voltPin")) voltPin = intVal;
else if (!strcmp(variable, "servoSteerPin")) servoSteerPin = intVal;
else if (!strcmp(variable, "servoDelay")) servoDelay = intVal;
else if (!strcmp(variable, "servoMinAngle")) servoMinAngle = intVal;
else if (!strcmp(variable, "servoMaxAngle")) servoMaxAngle = intVal;
else if (!strcmp(variable, "servoMinPulseWidth")) servoMinPulseWidth = intVal;
else if (!strcmp(variable, "servoMaxPulseWidth")) servoMaxPulseWidth = intVal;
else if (!strcmp(variable, "servoCenter")) servoCenter = intVal;
else if (!strcmp(variable, "voltDivider")) voltDivider = intVal;
else if (!strcmp(variable, "voltLow")) voltLow = fltVal;
else if (!strcmp(variable, "voltInterval")) voltInterval = intVal;
else if (!strcmp(variable, "buzzerUse")) buzzerUse = (bool)intVal;
else if (!strcmp(variable, "buzzerPin")) buzzerPin = intVal;
else if (!strcmp(variable, "buzzerDuration")) buzzerDuration = intVal;
else if (!strcmp(variable, "ds18b20Pin")) ds18b20Pin = intVal;
#endif
#if INCLUDE_I2C
else if (!strcmp(variable, "I2Csda")) I2Csda = intVal;
else if (!strcmp(variable, "I2Cscl")) I2Cscl = intVal;
else if (!strcmp(variable, "accelUse")) accelUse = (bool)intVal;
else if (!strcmp(variable, "accelDeg")) accelDeg = intVal;
#endif
#if INCLUDE_AUDIO
else if (!strcmp(variable, "micRem")) {
micRem = bool(intVal);
LOG_INF("Remote mic is %s", micRem ? "On" : "Off");
if (micRem && !ampVol) LOG_WRN("Amp volume is off");
}
else if (!strcmp(variable, "spkrRem")) {
spkrRem = (bool)intVal;
LOG_INF("Remote speaker is %s", spkrRem ? "On" : "Off");
if (spkrRem && !micGain) LOG_WRN("Mic gain is off");
}
else if (!strcmp(variable, "micGain")) micGain = intVal;
else if (!strcmp(variable, "micSckPin")) micSckPin = intVal;
else if (!strcmp(variable, "micSWsPin")) micSWsPin = intVal;
else if (!strcmp(variable, "micSdPin")) micSdPin = intVal;
else if (!strcmp(variable, "ampVol")) ampVol = intVal;
else if (!strcmp(variable, "mampBckIo")) mampBckIo = intVal;
else if (!strcmp(variable, "mampSwsIo")) mampSwsIo = intVal;
else if (!strcmp(variable, "mampSdIo")) mampSdIo = intVal;
else if (!strcmp(variable, "AudActive")) AudActive = intVal;
#endif
#if INCLUDE_TELEM
else if (!strcmp(variable, "teleUse")) teleUse = (bool)intVal;
#endif
else if (!strcmp(variable, "teleInterval")) srtInterval = intVal;
else if (!strcmp(variable, "wakeUse")) wakeUse = (bool)intVal;
else if (!strcmp(variable, "wakePin")) wakePin = intVal;
else if (!strcmp(variable, "wakeLevel")) wakeLevel = intVal;
#if INCLUDE_MCPWM
else if (!strcmp(variable, "motorRevPin")) motorRevPin = intVal;
else if (!strcmp(variable, "motorFwdPin")) motorFwdPin = intVal;
else if (!strcmp(variable, "motorRevPinR")) motorRevPinR = intVal;
else if (!strcmp(variable, "motorFwdPinR")) {
motorFwdPinR = intVal;
if (motorFwdPinR > 0) trackSteer = true;
}
else if (!strcmp(variable, "pwmFreq")) pwmFreq = intVal;
#endif
#ifndef AUXILIARY
else if (!strcmp(variable, "AuxIP")) strncpy(AuxIP, value, MAX_IP_LEN-1);
#endif
#if INCLUDE_PERIPH
else if (!strcmp(variable, "RCactive")) {
RCactive = (bool)intVal;
bool aux = false;
#ifdef AUXILIARY
aux = true;
#endif
#if INCLUDE_MCPWM
useBDC = (useUart && !aux) ? false : (bool)intVal;
#endif
}
else if (!strcmp(variable, "heartbeatRC")) heartbeatRC = intVal;
else if (!strcmp(variable, "maxSteerAngle")) maxSteerAngle = intVal;
else if (!strcmp(variable, "maxDutyCycle")) maxDutyCycle = intVal;
else if (!strcmp(variable, "minDutyCycle")) minDutyCycle = intVal;
else if (!strcmp(variable, "maxTurnSpeed")) maxTurnSpeed = intVal;
else if (!strcmp(variable, "allowReverse")) allowReverse = (bool)intVal;
else if (!strcmp(variable, "autoControl")) autoControl = (bool)intVal;
else if (!strcmp(variable, "waitTime")) waitTime = intVal;
else if (!strcmp(variable, "lightsRCpin")) lightsRCpin = intVal;
else if (!strcmp(variable, "stickUse")) stickUse = (bool)intVal;
else if (!strcmp(variable, "stickXpin")) stickXpin = intVal;
else if (!strcmp(variable, "stickYpin")) stickYpin = intVal;
else if (!strcmp(variable, "stickzPushPin")) stickzPushPin = intVal;
#endif
#if (INCLUDE_PGRAM && INCLUDE_PERIPH)
else if (!strcmp(variable, "stepIN1pin")) setStepperPin((uint8_t)intVal, 0);
else if (!strcmp(variable, "stepIN2pin")) setStepperPin((uint8_t)intVal, 1);
else if (!strcmp(variable, "stepIN3pin")) setStepperPin((uint8_t)intVal, 2);
else if (!strcmp(variable, "stepIN4pin")) setStepperPin((uint8_t)intVal, 3);
else if (!strcmp(variable, "PGactive")) {
PGactive = stepperUse = (bool)intVal;
if (PGactive) setLamp(0);
}
else if (!strcmp(variable, "numberOfPhotos")) numberOfPhotos = intVal;
else if (!strcmp(variable, "gearing")) gearing = fltVal;
else if (!strcmp(variable, "RPM")) tRPM = intVal;
else if (!strcmp(variable, "clockwise")) clockWise = (bool)intVal;
else if (!strcmp(variable, "timeForPhoto")) timeForPhoto = intVal;
else if (!strcmp(variable, "timeForFocus")) timeForFocus = intVal;
else if (!strcmp(variable, "pinShutter")) pinShutter = intVal;
else if (!strcmp(variable, "pinFocus")) pinFocus = intVal;
else if (!strcmp(variable, "extCam")) extCam = (bool)intVal;
#endif
#if INCLUDE_EXTHB
else if (!strcmp(variable, "external_heartbeat_active")) external_heartbeat_active = (bool)intVal;
else if (!strcmp(variable, "external_heartbeat_domain")) snprintf(external_heartbeat_domain, MAX_HOST_LEN, "%s", value);
else if (!strcmp(variable, "external_heartbeat_uri")) snprintf(external_heartbeat_uri, FILE_NAME_LEN, "%s", value);
else if (!strcmp(variable, "external_heartbeat_port")) external_heartbeat_port = intVal;
else if (!strcmp(variable, "external_heartbeat_token")) snprintf(external_heartbeat_token, EXTHB_LEN, "%s", value);
#endif
else if (!strcmp(variable, "useUart")) useUart = (bool)intVal;
#if INCLUDE_UART
else if (!strcmp(variable, "uartTxdPin")) uartTxdPin = intVal;
else if (!strcmp(variable, "uartRxdPin")) uartRxdPin = intVal;
#endif
#ifndef AUXILIARY
else if (!strcmp(variable, "xclkMhz")) xclkMhz = intVal;
else if (!strcmp(variable, "framesize")) {
if (intVal > maxFS && fromUser) LOG_WRN("Frame size %s too large for %s PSRAM ", frameData[intVal].frameSizeStr, fmtSize(ESP.getPsramSize()));
else {
fsizePtr = intVal;
if (fsizePtr > FRAMESIZE_SXGA) LOG_WRN("Motion detection not available as frame size %s too large", frameData[fsizePtr].frameSizeStr);
if (s) {
res = s->set_framesize(s, (framesize_t)fsizePtr);
if (res == ESP_OK) {
if (playbackHandle != NULL) {
setFPSlookup(fsizePtr);
updateConfigVect("fps", String(FPS).c_str());
}
}
}
}
}
else if (!strcmp(variable, "fps")) {
FPS = intVal;
if (playbackHandle != NULL) setFPS(FPS);
}
else if (s) {
if (!strcmp(variable, "quality")) {
res = s->set_quality(s, intVal);
if (res == ESP_OK) quality = intVal;
}
else if (!strcmp(variable, "contrast")) res = s->set_contrast(s, intVal);
else if (!strcmp(variable, "brightness")) res = s->set_brightness(s, intVal);
else if (!strcmp(variable, "saturation")) res = s->set_saturation(s, intVal);
else if (!strcmp(variable, "denoise")) res = s->set_denoise(s, intVal);
else if (!strcmp(variable, "sharpness")) res = s->set_sharpness(s, intVal);
else if (!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)intVal);
else if (!strcmp(variable, "colorbar")) res = s->set_colorbar(s, intVal);
else if (!strcmp(variable, "awb")) res = s->set_whitebal(s, intVal);
else if (!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, intVal);
else if (!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, intVal);
else if (!strcmp(variable, "hmirror")) res = s->set_hmirror(s, intVal);
else if (!strcmp(variable, "vflip")) res = s->set_vflip(s, intVal);
else if (!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, intVal);
else if (!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, intVal);
else if (!strcmp(variable, "aec_value")) res = s->set_aec_value(s, intVal);
else if (!strcmp(variable, "aec2")) res = s->set_aec2(s, intVal);
else if (!strcmp(variable, "dcw")) res = s->set_dcw(s, intVal);
else if (!strcmp(variable, "bpc")) res = s->set_bpc(s, intVal);
else if (!strcmp(variable, "wpc")) res = s->set_wpc(s, intVal);
else if (!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, intVal);
else if (!strcmp(variable, "lenc")) res = s->set_lenc(s, intVal);
else if (!strcmp(variable, "special_effect")) res = s->set_special_effect(s, intVal);
else if (!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, intVal);
else if (!strcmp(variable, "ae_level")) res = s->set_ae_level(s, intVal);
else return false;
}
#endif
else return false;
if (res != ESP_OK && fromUser) LOG_WRN("Value %d for setting %s not supported for camera type %s", intVal, variable, camModel);
return true;
}
static bool extractKeyVal(const char* wsMsg) {
strncpy(variable, wsMsg, FILE_NAME_LEN - 1);
char* endPtr = strchr(variable, '=');
if (endPtr != NULL) {
*endPtr = 0;
strcpy(value, variable + strlen(variable) + 1);
return true;
} else LOG_ERR("Invalid query string: %s", wsMsg);
return false;
}
esp_err_t appSpecificWebHandler(httpd_req_t *req, const char* variable, const char* value) {
if (!strcmp(variable, "sfile")) {
strcpy(inFileName, value);
if (!forceRecord) doPlayback = listDir(inFileName, jsonBuff, JSON_BUFF_LEN, AVI_EXT);
else strcpy(jsonBuff, "{}");
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, jsonBuff);
}
else if (!strcmp(variable, "updateFPS")) {
sprintf(jsonBuff, "{\"fps\":\"%u\"}", setFPSlookup(fsizePtr));
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, jsonBuff);
}
else if (!strcmp(variable, "still") || !strcmp(variable, "hub")) {
uint32_t startTime = millis();
doKeepFrame = true;
while (doKeepFrame && millis() - startTime < MAX_FRAME_WAIT) delay(100);
if (!doKeepFrame && alertBufferSize) {
httpd_resp_set_type(req, "image/jpeg");
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
httpd_resp_send(req, (const char*)alertBuffer, alertBufferSize);
uint32_t jpegTime = millis() - startTime;
LOG_INF("JPEG: %uB in %ums", alertBufferSize, jpegTime);
alertBufferSize = 0;
} else LOG_WRN("Failed to get still");
}
else if (!strcmp(variable, "formatSD")) {
if (formatSDcard()) doRestart("user requested format of SD card");
}
else return ESP_FAIL;
return ESP_OK;
}
static bool setPeripheral(char cmd, int controlVal, bool fromUart) {
bool res = true;
switch (cmd) {
#if INCLUDE_MCPWM
case 'M':
if (trackSteer) trackSteeering(controlVal, false);
else motorSpeed(controlVal);
break;
case 'D':
if (trackSteer) trackSteeering(controlVal, true);
else setSteering(controlVal);
break;
#endif
#if INCLUDE_PERIPH
case 'L':
setLightsRC((bool)controlVal);
break;
case 'P':
setCamPan(controlVal);
break;
case 'T':
setCamTilt(controlVal);
break;
#endif
#if INCLUDE_PGRAM
case 'G':
takePhotos(bool(controlVal));
break;
#endif
case 'K':
#ifdef AUXILIARY
if (fromUart)
#endif
stopRC();
break;
default:
res = false;
break;
}
return res;
}
void appSpecificWsHandler(const char* wsMsg) {
int wsLen = strlen(wsMsg) - 1;
char cmd = (char)wsMsg[0];
int controlVal = atoi(wsMsg + 1);
bool aux = false;
#ifdef AUXILIARY
aux = true;
#endif
if (useUart && !aux) {
#if INCLUDE_UART
if (!writeUart(cmd, (uint32_t)controlVal)) LOG_WRN("Failed to send data to Auxiliary over UART");
#endif
} else {
if (!setPeripheral(cmd, controlVal, false)) {
switch (cmd) {
case 'X':
#if INCLUDE_AUDIO
stopAudio = true;
#endif
break;
case 'C':
if (extractKeyVal(wsMsg + 1)) updateStatus(variable, value);
break;
case 'S':
buildJsonString(wsLen);
logPrint("%s\n", jsonBuff);
break;
case 'U':
memcpy(jsonBuff, wsMsg + 1, wsLen);
parseJson(wsLen);
break;
case 'H':
heartBeatDone = true;
break;
case 'K':
killSocket();
break;
default:
LOG_WRN("unknown command %s", wsMsg);
break;
}
}
}
}
void appSpecificWsBinHandler(uint8_t* wsMsg, size_t wsMsgLen) {
#if INCLUDE_AUDIO
browserMicInput(wsMsg, wsMsgLen);
#endif
}
void buildAppJsonString(bool filter) {
char* p = jsonBuff + 1;
p += sprintf(p, "\"llevel\":%u,", lightLevel);
p += sprintf(p, "\"night\":%s,", nightTime ? "\"Yes\"" : "\"No\"");
float aTemp = readTemperature(true);
if (aTemp > -127.0) p += sprintf(p, "\"atemp\":\"%0.1f\",", aTemp);
else p += sprintf(p, "\"atemp\":\"n/a\",");
float currentVoltage = readVoltage();
if (currentVoltage < 0) p += sprintf(p, "\"battv\":\"n/a\",");
else p += sprintf(p, "\"battv\":\"%0.1fV\",", currentVoltage);
p += sprintf(p, "\"camModel\":\"%s\",", camModel);
#if INCLUDE_PERIPH
p += sprintf(p, "\"SVactive\":\"%d\",", SVactive);
#if INCLUDE_AUDIO
p += sprintf(p, "\"AudActive\":\"%d\",", AudActive);
#endif
#if (INCLUDE_PGRAM)
p += sprintf(p, "\"PGactive\":\"%d\",", PGactive);
#endif
#endif
#if INCLUDE_MCPWM
p += sprintf(p, "\"maxSteerAngle\":\"%d\",", maxSteerAngle);
p += sprintf(p, "\"maxDutyCycle\":\"%d\",", maxDutyCycle);
p += sprintf(p, "\"minDutyCycle\":\"%d\",", minDutyCycle);
p += sprintf(p, "\"allowReverse\":\"%d\",", allowReverse);
p += sprintf(p, "\"autoControl\":\"%d\",", autoControl);
p += sprintf(p, "\"waitTime\":\"%d\",", waitTime);
p += sprintf(p, "\"RCactive\":\"%d\",", RCactive);
p += sprintf(p, "\"maxSteerAngle\":\"%d\",", maxSteerAngle);
p += sprintf(p, "\"maxDutyCycle\":\"%d\",", maxDutyCycle);
p += sprintf(p, "\"minDutyCycle\":\"%d\",", minDutyCycle);
p += sprintf(p, "\"allowReverse\":\"%d\",", allowReverse);
p += sprintf(p, "\"autoControl\":\"%d\",", autoControl);
p += sprintf(p, "\"waitTime\":\"%d\",", waitTime);
p += sprintf(p, "\"heartbeatRC\":\"%d\",", heartbeatRC);
#endif
p += sprintf(p, "\"sustainId\":\"%u\",", sustainId);
#ifndef AUXILIARY
uint8_t cardType = 99;
if ((fs::SDMMCFS*)&STORAGE == &SD_MMC) cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) p += sprintf(p, "\"card\":\"%s\",", "NO card");
else {
if (!filter) {
if (cardType == CARD_MMC) p += sprintf(p, "\"card\":\"%s\",", "MMC");
else if (cardType == CARD_SD) p += sprintf(p, "\"card\":\"%s\",", "SDSC");
else if (cardType == CARD_SDHC) p += sprintf(p, "\"card\":\"%s\",", "SDHC");
else if (cardType == 99) p += sprintf(p, "\"card\":\"%s\",", "LittleFS");
}
if ((fs::SDMMCFS*)&STORAGE == &SD_MMC) p += sprintf(p, "\"card_size\":\"%s\",", fmtSize(SD_MMC.cardSize()));
p += sprintf(p, "\"used_bytes\":\"%s\",", fmtSize(STORAGE.usedBytes()));
p += sprintf(p, "\"free_bytes\":\"%s\",", fmtSize(STORAGE.totalBytes() - STORAGE.usedBytes()));
p += sprintf(p, "\"total_bytes\":\"%s\",", fmtSize(STORAGE.totalBytes()));
}
p += sprintf(p, "\"free_psram\":\"%s\",", fmtSize(ESP.getFreePsram()));
#endif
#if INCLUDE_FTP_HFS
p += sprintf(p, "\"progressBar\":%d,", percentLoaded);
if (percentLoaded == 100) percentLoaded = 0;
#endif
*p = 0;
}
void externalAlert(const char* subject, const char* message) {
#if INCLUDE_TGRAM
if (tgramUse) tgramAlert(subject, message);
#endif
#if INCLUDE_SMTP
if (smtpUse) emailAlert(subject, message);
#endif
}
void displayAudioLed(int16_t audioSample) {
}
void setupAudioLed() {
}
int8_t checkPotVol(int8_t adjVol) {
return adjVol;
}
void applyFilters() {
applyVolume();
}
#if !INCLUDE_PERIPH
float readVoltage() {
return -1.0;
}
float readTemperature(bool isCelsius, bool onlyDS18) {
return readInternalTemp();
}
#endif
void setInputPeripheral(uint8_t cmd, uint32_t controlVal) {
}
int getInputPeripheral(uint8_t cmd) {
uint32_t inputVal = -1;
if ((char)cmd == 'I') {
bool pirVal = getPIRval();
memcpy(&inputVal, &pirVal, sizeof(pirVal));
}
return inputVal;
}
bool setOutputPeripheral(uint8_t cmd, uint32_t rxValue) {
int controlValue;
memcpy(&controlValue, &rxValue, sizeof(controlValue));
return setPeripheral((char)cmd, controlValue, true);
}
bool appDataFiles() {
return true;
}
void currentStackUsage() {
checkStackUse(captureHandle, 0);
#if INCLUDE_SMTP
checkStackUse(emailHandle, 2);
#endif
#if INCLUDE_FTP
checkStackUse(fsHandle, 3);
#endif
checkStackUse(logHandle, 4);
#if INCLUDE_AUDIO
checkStackUse(audioHandle, 5);
#endif
#if INCLUDE_MQTT
checkStackUse(mqttTaskHandle, 6);
#endif
checkStackUse(playbackHandle, 8);
#if INCLUDE_PERIPH
#if INCLUDE_DS18B20
checkStackUse(DS18B20handle, 1);
#endif
checkStackUse(servoHandle, 9);
checkStackUse(stickHandle, 10);
checkStackUse(heartBeatHandle, 14);
checkStackUse(battHandle, 15);
#endif
#if INCLUDE_TGRAM
checkStackUse(telegramHandle, 11);
#endif
#if INCLUDE_TELEM
checkStackUse(telemetryHandle, 12);
#endif
#if INCLUDE_UART
checkStackUse(uartRxHandle, 13);
#endif
for (int i=0; i < numStreams; i++) checkStackUse(sustainHandle[i], 17 + i);
}
static void stopRC() {
#if INCLUDE_PERIPH
setLightsRC(false);
#endif
#if INCLUDE_MCPWM
if (motorFwdPin > 0) motorSpeed(0, true);
if (motorFwdPinR > 0) motorSpeed(0, false);
#endif
}
#if INCLUDE_PERIPH
static void heartBeatTask (void *pvParameter) {
while (true) {
delay((heartbeatRC + 1) * 1000);
if (!heartBeatDone) stopRC();
heartBeatDone = false;
}
}
void startHeartbeat() {
if (RCactive || useUart) {
if (heartBeatHandle == NULL) xTaskCreateWithCaps(&heartBeatTask, "heartBeatTask", HB_STACK_SIZE, NULL, HB_PRI, &heartBeatHandle, HEAP_MEM);
}
}
#endif
void doAppPing() {
if (DEBUG_MEM) {
currentStackUsage();
checkMemory();
}
if (checkAlarm()) {
remoteServerReset();
getExtIP();
#if INCLUDE_SMTP
if (smtpUse) {
emailCount = 0;
LOG_INF("Reset daily email allowance");
}
#endif
LOG_INF("Daily rollover");
}
#if INCLUDE_EXTHB
if (external_heartbeat_active) sendExternalHeartbeat();
#endif
#if INCLUDE_PERIPH
static bool atNight = false;
#endif
if (isNight(nightSwitch)) {
if (wakeUse && wakePin) {
#ifndef AUXILIARY
digitalWrite(PWDN_GPIO_NUM, 1);
#endif
goToSleep(true);
}
#if INCLUDE_PERIPH
if (relayPin && relayMode && !atNight) {
digitalWrite(relayPin, HIGH);
atNight = true;
}
} else if (relayPin && relayMode && atNight) {
digitalWrite(relayPin, LOW);
atNight = false;
#endif
}
}
void tgramAlert(const char* subject, const char* message) {
const char* pos1 = strchr(subject + 1, '/');
const char* pos2 = strrchr(subject + 1, '.');
if (pos1 != NULL && pos2 != NULL) {
strncpy(alertCaption, pos1, pos2 - pos1);
alertCaption[pos2 - pos1] = 0;
strcat(alertCaption, " from ");
strncat(alertCaption, hostName, sizeof(alertCaption) - strlen(alertCaption) - 1);
if (alertBufferSize) alertReady = true;
} else LOG_WRN("Unable to send motion alert");
}
static bool downloadAvi(const char* userCmd) {
char* pos = strchr(userCmd, '_');
if (pos != NULL) {
char fileName[FILE_NAME_LEN];
strncpy(fileName, userCmd, FILE_NAME_LEN - 1);
pos = strchr(fileName, '_');
memmove(pos, fileName, sizeof(fileName) - (pos - fileName));
strncat(fileName, ".avi", sizeof(fileName - 1) - strlen(fileName));
if (STORAGE.exists(fileName)) sendTgramFile(fileName, "video/x-msvideo", "");
else sendTgramMessage("AVI file not found: ", fileName, "");
}
return (bool)pos;
}
static void saveRamLog(const char* ramLogName) {
File ramFile = STORAGE.open(ramLogName, FILE_WRITE);
int startPtr, endPtr;
startPtr = endPtr = mlogEnd;
do {
int maxChunk = startPtr < endPtr ? endPtr - startPtr : RAM_LOG_LEN - startPtr;
size_t chunkSize = std::min(CHUNKSIZE, maxChunk);
if (chunkSize > 0) ramFile.write((uint8_t*)messageLog + startPtr, chunkSize);
startPtr += chunkSize;
if (startPtr >= RAM_LOG_LEN) startPtr = 0;
} while (startPtr != endPtr);
ramFile.close();
}
void appSpecificTelegramTask(void* p) {
#if INCLUDE_TGRAM
snprintf(tgramHdr, FILE_NAME_LEN - 1, "%s\n Ver: " APP_VER "\n\n/snap\n\n/log\n\n/extIP", hostName);
sendTgramMessage("Rebooted", "", "");
char userCmd[FILE_NAME_LEN];
while (true) {
if (getTgramUpdate(userCmd)) {
if (!strcmp(userCmd, "/snap")) {
doKeepFrame = true;
delay(1000);
sprintf(userCmd, "/snap from %s", hostName);
sendTgramPhoto(alertBuffer, alertBufferSize, userCmd);
} else if (!strcmp(userCmd, "/log")) {
char ramLogName[FILE_NAME_LEN];
sprintf(ramLogName, "%s/ramlog_", DATA_DIR);
time_t currEpoch = getEpoch();
strftime(ramLogName + strlen(ramLogName), FILE_NAME_LEN - strlen(ramLogName), "%H%M%S", localtime(&currEpoch));
strcat(ramLogName, TEXT_EXT);
saveRamLog(ramLogName);
sprintf(userCmd, "/log from %s", hostName);
sendTgramFile(ramLogName, "text/plain", userCmd);
deleteFolderOrFile(ramLogName);
} else if (!strcmp(userCmd, "/extIP")) {
char extIpPt[24];
strcpy(extIpPt, extIP);
if (strlen(portFwd)) strcat(extIpPt, portFwd);
sendTgramMessage("Ext IP: ", extIpPt, "");
} else {
if (!downloadAvi(userCmd)) sendTgramMessage("Request not recognised: ", userCmd, "");
}
} else {
if (alertReady) {
alertReady = false;
sendTgramPhoto(alertBuffer, alertBufferSize, alertCaption);
alertBufferSize = 0;
} else delay(5000);
}
}
#endif
vTaskDelete(NULL);
}
const char* appConfig = R"~(
ST_SSID~~99~~na
netMode~0~99~S:WiFi:Ethernet:Eth+AP~Network interface selection
fsPort~21~99~~na
fsServer~~99~~na
ftpUser~~99~~na
fsWd~~99~~na
fsUse~~99~~na
smtp_port~465~99~~na
smtp_server~smtp.gmail.com~99~~na
smtp_login~~99~~na
smtp_email~~99~~na
Auth_Name~~99~~na
useHttps~~99~~na
useSecure~~99~~na
useFtps~~99~~na
extIP~~99~~na
restart~~99~~na
sdLog~0~99~~na
xclkMhz~20~98~~na
ae_level~-2~98~~na
aec~1~98~~na
aec2~1~98~~na
aec_value~204~98~~na
agc~1~98~~na
agc_gain~0~98~~na
autoUpload~0~98~~na
deleteAfter~0~98~~na
awb~1~98~~na
awb_gain~1~98~~na
bpc~1~98~~na
brightness~0~98~~na
colorbar~0~98~~na
contrast~0~98~~na
dcw~1~98~~na
enableMotion~1~98~~na
fps~20~98~~na
framesize~10~98~~na
gainceiling~0~98~~na
hmirror~0~98~~na
lampLevel~0~98~~na
lenc~1~98~~na
lswitch~10~98~~na
micGain~0~98~~na
ampVol~0~98~~na
minf~5~98~~na
motionVal~8~98~~na
quality~12~98~~na
raw_gma~1~98~~na
record~1~98~~na
saturation~0~98~~na
sharpness~0~98~~na
denoise~0~98~~na
special_effect~0~98~~na
timeLapseOn~0~98~~na
timezone~GMT0~98~~na
vflip~0~98~~na
wb_mode~0~98~~na
wpc~1~98~~na
ST_ip~~0~T~Static IP address
ST_gw~~0~T~Router IP address
ST_sn~255.255.255.0~0~T~Router subnet
ST_ns1~~0~T~DNS server
ST_ns2~~0~T~Alt DNS server
AP_Pass~~0~T~AP Password
AP_ip~~0~T~AP IP Address if not 192.168.4.1
AP_sn~~0~T~AP subnet
AP_gw~~0~T~AP gateway
allowAP~1~0~C~Allow simultaneous AP
doGetExtIP~1~0~C~Enable get external IP
wifiTimeoutSecs~30~0~N~WiFi connect timeout (secs)
logType~0~99~N~Output log selection
ntpServer~pool.ntp.org~0~T~NTP Server address
alarmHour~1~2~N~Hour of day for daily actions
refreshVal~5~2~N~Web page refresh rate (secs)
responseTimeoutSecs~10~2~N~Server response timeout (secs)
useUart~0~3~C~Use UART for Auxiliary connection
uartTxdPin~~3~N~UART TX pin
uartRxdPin~~3~N~UART RX pin
tlSecsBetweenFrames~600~1~N~Timelapse interval (secs)
tlDurationMins~720~1~N~Timelapse duration (mins)
tlPlaybackFPS~1~1~N~Timelapse playback FPS
maxFrames~20000~1~N~Max frames in recording
dashCamOn~0~98~~na
moveStartChecks~5~1~N~Checks per second for start motion
moveStopSecs~2~1~N~Non movement to stop recording (secs)
detectMotionFrames~5~1~N~Num changed frames to start motion
detectNightFrames~10~1~N~Min dark frames to indicate night
detectNumBands~10~1~N~Total num of detection bands
detectStartBand~3~1~N~Top band where motion is checked
detectEndBand~8~1~N~Bottom band where motion is checked
detectChangeThreshold~15~1~N~Pixel difference to indicate change
mlUse~0~1~C~Use Machine Learning
mlProbability~0.8~1~N~ML minimum positive probability 0.0 - 1.0
depthColor~0~1~C~Color depth for motion detection: Gray <> RGB
streamVid~0~8~C~Enable NVR Video stream: /sustain?video=1
streamAud~0~8~C~Enable NVR Audio stream: /sustain?audio=1
streamSrt~0~8~C~Enable NVR Subtitle stream: /sustain?srt=1
smtpUse~0~2~C~Enable email sending
smtpMaxEmails~10~2~N~Max daily alerts
sdMinCardFreeSpace~100~2~N~Min free MBytes on SD before action
sdFreeSpaceMode~1~2~S:No Check:Delete oldest:Ftp then delete~Action mode on SD min free
formatIfMountFailed~0~2~C~Format file system on failure
pirUse~0~3~C~Use PIR for detection
accelUse~0~3~C~Use I2C accelerometer for detection
accelDeg~5~3~N~Min accelerometer degrees movement
lampType~0~3~S:Manual:Auto~How lamp activated
SVactive~0~3~C~Enable servo use
pirPin~~3~N~Pin used for PIR
lampPin~~3~N~Pin used for Lamp
servoPanPin~~6~N~Pin used for Pan Servo
servoTiltPin~~6~N~Pin used for Tilt Servo
ds18b20Pin~~3~N~Pin used for DS18B20 temperature sensor
AudActive~0~3~C~Show audio configuration
micSckPin~-1~7~N~Microphone I2S SCK pin
micSWsPin~-1~7~N~Microphone I2S WS, PDM CLK pin
micSdPin~-1~7~N~Microphone I2S SD, PDM DAT pin
mampBckIo~-1~7~N~Amplifier I2S BCLK (SCK) pin
mampSwsIo~-1~7~N~Amplifier I2S LRCLK (WS) pin
mampSdIo~-1~7~N~Amplifier I2S DIN pin
servoDelay~0~6~N~Delay between each 1 degree change (ms)
servoMinAngle~0~6~N~Set min angle for servo model
servoMaxAngle~180~6~N~Set max angle for servo model
servoMinPulseWidth~544~6~N~Set min pulse width for servo model (usecs)
servoMaxPulseWidth~2400~6~N~Set max pulse width for servo model (usecs)
servoCenter~90~6~N~Angle at which servo centered
voltDivider~2~3~N~Voltage divider resistor ratio
voltLow~3~3~N~Warning level for low voltage
voltInterval~5~3~N~Voltage check interval (mins)
voltPin~~3~N~ADC Pin used for battery voltage
voltUse~0~3~C~Use Voltage check
wakePin~~3~N~Pin used to wake app from sleep
wakeLevel~1~3~N~Pin level (0,1) to wake app from sleep
wakeUse~0~3~C~Deep sleep app during night
mqtt_active~0~2~C~Mqtt enabled
mqtt_broker~~2~T~Mqtt server ip to connect
mqtt_port~1883~2~N~Mqtt server port
mqtt_user~~2~T~Mqtt user name
mqtt_user_Pass~~2~T~Mqtt user password
mqtt_topic_prefix~homeassistant/~2~T~Mqtt topic path prefix
external_heartbeat_active~0~2~C~External Heartbeat Server enabled
external_heartbeat_domain~~2~T~Heartbeat receiver domain or IP (eg. www.espsee.com)
external_heartbeat_uri~~2~T~Heartbeat receiver URI (eg. /heartbeat/)
external_heartbeat_port~443~2~N~Heartbeat receiver port
external_heartbeat_token~~2~T~Heartbeat receiver auth token
usePing~1~0~C~Use ping
teleUse~0~3~C~Use telemetry recording
teleInterval~1~3~N~Telemetry collection interval (secs)
RCactive~0~3~C~Enable remote control
servoSteerPin~~4~N~Pin used for steering servo
motorRevPin~~4~N~Pin used for motor reverse / left track
motorFwdPin~~4~N~Pin used for motor forward / left track
motorRevPinR~~4~N~Pin used for right track reverse
motorFwdPinR~~4~N~Pin used for right track forward
lightsRCpin~~4~N~Pin used for RC lights output
heartbeatRC~5~4~N~RC connection heartbeat time (secs)
AuxIP~~3~T~Send RC / Servo / PG commands to Auxiliary IP
stickXpin~~4~N~Pin used for joystick steering
stickYpin~~4~N~Pin used for joystick motor
stickzPushPin~~4~N~Pin used for joystick lights
stickUse~0~4~C~Use joystick
pwmFreq~50~4~N~RC Motor PWM frequency
maxSteerAngle~45~4~N~Max steering angle from straightahead
maxTurnSpeed~50~4~N~Max tracked turn speed differential
maxDutyCycle~100~4~N~Max motor duty cycle % (speed)
minDutyCycle~10~4~N~Min motor duty cycle % (stop)
allowReverse~1~4~C~Reverse motion required
autoControl~1~4~C~Stop motor or center steering if control inactive
waitTime~20~4~N~Min wait (ms) between RC updates to app
tgramUse~0~2~C~Use Telegram Bot
tgramToken~~2~T~Telegram Bot token
tgramChatId~~2~T~Telegram chat identifier
devHub~0~2~C~Show Camera Hub tab
buzzerUse~0~3~C~Use active buzzer
buzzerPin~~3~N~Pin used for active buzzer
buzzerDuration~~3~N~Duration of buzzer sound in secs
stepIN1pin~-1~5~N~Stepper IN1 pin number
stepIN2pin~-1~5~N~Stepper IN2 pin number
stepIN3pin~-1~5~N~Stepper IN3 pin number
stepIN4pin~-1~5~N~Stepper IN4 pin number
PGactive~0~3~C~Enable photogrammetry
numberOfPhotos~20~5~N~Number of photos
RPM~1~5~N~Turntable revolution speed as RPM
gearing~5.7~5~N~Turntable / motor gearing ratio
clockwise~1~5~C~Clockwise turntable if true
timeForFocus~0~5~N~Time allocated to auto focus (secs)
timeForPhoto~2~5~N~Time allocated to take photo (secs)
pinShutter~-1~5~N~Pin connected to camera shutter
pinFocus~-1~5~N~Pin connected to camera focus
extCam~0~5~C~Use external camera
AtakePhotos~Start~5~A~Start photogrammetry
BabortPhotos~Abort~5~A~Abort photogrammetry
relayPin~-1~3~N~Pin to switch relay
relayMode~0~3~S:Manual:Night~How relay activated
relaySwitch~0~3~C~Switch relay off / on
I2Csda~-1~3~N~I2C SDA pin if unshared
I2Cscl~-1~3~N~I2C SCL pin if unshared
RTSP_Name~~8~T~RTSP Auth Username
RTSP_Pass~~8~T~RTSP Auth Password
rtsp00Video~0~8~C~Enable RTSP Video
rtsp01Audio~0~8~C~Enable RTSP Audio
rtsp02Subtitles~0~8~C~Enable RTSP Subtitles
rtsp03Port~554~8~N~RTSP ServerPort
rtsp04VideoPort~5430~8~N~RTSP Video Port
rtsp05AudioPort~5432~8~N~RTSP Audio Port
rtsp06SubtitlesPort~5434~8~N~RTSP Subtitles Port
rtsp07Ip~239.255.0.1~8~T~RTSP Multicast IP
rtsp08MaxC~3~8~N~RTSP Multicast Max Connections
rtsp09TTL~1~8~N~RTSP Multicast Time-to-Live
ethCS~-1~9~N~Ethernet CS pin
ethInt~-1~9~N~Ethernet Interrupt pin
ethRst~-1~9~N~Ethernet Reset pin
ethSclk~-1~9~N~Ethernet SPI clock pin
ethMiso~-1~9~N~Ethernet SPI MISO pin
ethMosi~-1~9~N~Ethernet SPI MOSI pin
)~";